/**
* <copyright>
* Copyright (c) 2010-2014 Henshin developers. All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v1.0 which
* accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
* </copyright>
*/
package org.eclipse.emf.henshin.model.actions;
import org.eclipse.emf.henshin.model.Graph;
import org.eclipse.emf.henshin.model.GraphElement;
import org.eclipse.emf.henshin.model.MappingList;
import org.eclipse.emf.henshin.model.NestedCondition;
import org.eclipse.emf.henshin.model.Rule;
public abstract class AbstractMapEditor<E extends GraphElement> implements MapEditor<E> {
// Source graph.
private Graph source;
// Target graph.
private Graph target;
// Mappings: source to target.
private MappingList mappings;
/**
* Default constructor.
* @param source Source graph.
* @param target Target graph.
* @param mappings Mappings from source to target.
*/
public AbstractMapEditor(Graph source, Graph target, MappingList mappings) {
setSourceAndTarget(source, target);
this.mappings = mappings;
}
/**
* Alternative constructor, which assumes that the given target graph
* is the RHS or a NAC of a rule.
* @param target Target graph.
*/
public AbstractMapEditor(Graph target) {
// Check whether the graph is properly contained in a rule.
Rule rule = target.getRule();
if (rule==null) {
throw new IllegalArgumentException("Source graph not contained in a rule");
}
// Set source and target:
setSourceAndTarget(rule.getLhs(), target);
// Determine the mappings to be used.
if (target==rule.getRhs()) {
this.mappings = rule.getMappings();
} else if (target.eContainer() instanceof NestedCondition) {
this.mappings = ((NestedCondition) target.eContainer()).getMappings();
} else {
throw new IllegalArgumentException("Target graph must be either the RHS or contained in a NestedCondition");
}
}
/**
* Alternative constructor.
*/
public AbstractMapEditor(MapEditor<?> mapEditor) {
this(mapEditor.getSource(), mapEditor.getTarget(), mapEditor.getMappings());
}
/*
* Set source and target graph.
*/
private void setSourceAndTarget(Graph source, Graph target) {
// Check whether the graphs are null or the same.
if (source==null || target==null || source==target) {
throw new IllegalArgumentException("Source and target graph cannot be the same or null");
}
this.source = source;
this.target = target;
}
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.diagram.edit.maps.MapEditor#getSource()
*/
public final Graph getSource() {
return source;
}
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.diagram.edit.maps.MapEditor#getTarget()
*/
public final Graph getTarget() {
return target;
}
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.diagram.edit.maps.MapEditor#getMappings()
*/
public final MappingList getMappings() {
return mappings;
}
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.diagram.edit.maps.MapEditor#getOpposite(java.lang.Object)
*/
public final E getOpposite(E e) {
if (mappings==null) {
return null;
}
Graph graph = e.getGraph();
if (graph==null || (graph!=source && graph!=target)) {
throw new IllegalArgumentException("Illegal element container: " + graph);
}
if (graph==source) {
return mappings.getImage(e, target);
} else {
return mappings.getOrigin(e);
}
}
/*
* Get the opposite graph.
*/
protected final Graph getOpposite(Graph graph) {
if (graph==source) return target;
if (graph==target) return source;
return null;
}
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.diagram.edit.maps.MapEditor#move(java.lang.Object)
*/
public final void move(E e) {
E opposite = getOpposite(e);
if (opposite!=null) {
replace(opposite); // Rather replace the opposite if it exists already
return;
}
// Do the moving:
doMove(e);
// Now move in the multi-rules:
if (getTarget().isRhs()) {
for (Rule multi : getTarget().getRule().getMultiRules()) {
E eImage = multi.getMultiMappings().getImage(e, null);
if (eImage!=null) {
newInstance(multi.getLhs(), multi.getRhs(), multi.getMappings()).move(eImage);
}
}
}
}
/*
* Perform the move operation.
*/
protected abstract void doMove(E e);
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.diagram.edit.maps.MapEditor#remove(java.lang.Object)
*/
public final void remove(E e) {
Graph container = e.getGraph();
if (container!=source && container!=target) {
throw new IllegalArgumentException();
}
// Remove the mapping first.
E opposite = getOpposite(e);
if (opposite!=null) {
removeMapping(e, opposite);
}
// Do the removal:
doRemove(e);
// Now remove in the multi-rules:
if (getTarget().isRhs()) {
for (Rule multi : getTarget().getRule().getMultiRules()) {
E eImage = multi.getMultiMappings().getImage(e, null);
if (eImage!=null) {
newInstance(multi.getLhs(), multi.getRhs(), multi.getMappings()).remove(eImage);
multi.getMultiMappings().remove(e, eImage);
}
}
}
}
/*
* Perform a remove operation.
*/
protected abstract void doRemove(E e);
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.diagram.edit.maps.MapEditor#replace(java.lang.Object)
*/
public final E replace(E e) {
if (getOpposite(e)==null) {
throw new IllegalArgumentException("Cannot replace an element that is not mapped: " + e);
}
// Do the replacement:
E r = doReplace(e);
// Now replace in the multi-rules:
if (getTarget().isRhs()) {
for (Rule multi : getTarget().getRule().getMultiRules()) {
E eImage = multi.getMultiMappings().getImage(e, null);
E rImage = multi.getMultiMappings().getImage(r, null);
if (eImage!=null && rImage!=null) {
newInstance(multi.getLhs(), multi.getRhs(), multi.getMappings()).replace(eImage);
multi.getMultiMappings().remove(e, eImage);
}
}
}
return r;
}
/*
* Perform a replace operation.
*/
protected abstract E doReplace(E e);
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.diagram.edit.maps.MapEditor#copy(java.lang.Object)
*/
public final E copy(E e) {
// Do the copy:
E c = doCopy(e);
// Now copy in the multi-rules:
if (getTarget().isRhs()) {
for (Rule multi : getTarget().getRule().getMultiRules()) {
E eImage = multi.getMultiMappings().getImage(e, null);
if (eImage!=null) {
E cImage = newInstance(multi.getLhs(), multi.getRhs(), multi.getMappings()).copy(eImage);
multi.getMultiMappings().add(c, cImage);
}
}
}
return c;
}
/*
* Perform a copy.
*/
protected abstract E doCopy(E e);
/*
* Remove a mapping between two elements.
*/
protected final void removeMapping(E e1, E e2) {
if (mappings==null) {
return;
}
Graph g1 = e1.getGraph();
Graph g2 = e2.getGraph();
if (g1==source && g2==target) {
doRemoveMapping(e1, e2);
}
else if (g1==target && g2==source) {
doRemoveMapping(e2, e1);
}
else {
throw new IllegalArgumentException();
}
}
/*
* Perform a removal of a mapping.
*/
protected void doRemoveMapping(E origin, E image) {
// Default implementation assumes that mappings are implicit, so nothing to do here.
}
/*
* Create a mapping.
*/
protected final void createMapping(E e1, E e2) {
if (mappings==null) {
return;
}
Graph g1 = e1.getGraph();
Graph g2 = e2.getGraph();
if (g1==source && g2==target) {
doCreateMapping(e1, e2);
}
else if (g1==target && g2==source) {
doCreateMapping(e2, e1);
} else {
throw new IllegalArgumentException();
}
}
/*
* Perform a creation of a mapping.
*/
protected void doCreateMapping(E origin, E image) {
// Default implementation assumes that mappings are implicit, so nothing to do here.
}
/*
* Create a new instance of the current map editor.
*/
@SuppressWarnings("unchecked")
private MapEditor<E> newInstance(Graph source, Graph target, MappingList mappings) {
try {
return getClass().getConstructor(Graph.class, Graph.class, MappingList.class)
.newInstance(source, target, mappings);
} catch (Throwable t) {
t.printStackTrace();
return null;
}
}
}