/*******************************************************************************
* Copyright (c) 2010-2015 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
*
* Contributors:
* TU Berlin, University of Luxembourg, SES S.A.
*******************************************************************************/
package de.tub.tfs.henshin.tggeditor.commands;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.henshin.interpreter.Match;
import org.eclipse.emf.henshin.interpreter.RuleApplication;
import org.eclipse.emf.henshin.interpreter.impl.RuleApplicationImpl;
import org.eclipse.emf.henshin.model.Attribute;
import org.eclipse.emf.henshin.model.Edge;
import org.eclipse.emf.henshin.model.Graph;
import org.eclipse.emf.henshin.model.Module;
import org.eclipse.emf.henshin.model.Node;
import org.eclipse.emf.henshin.model.Rule;
import org.eclipse.gef.commands.CompoundCommand;
import de.tub.tfs.henshin.tgg.TAttribute;
import de.tub.tfs.henshin.tgg.TEdge;
import de.tub.tfs.henshin.tgg.TNode;
import de.tub.tfs.henshin.tgg.TripleGraph;
import de.tub.tfs.henshin.tgg.interpreter.TggTransformation;
import de.tub.tfs.henshin.tgg.interpreter.impl.TggHenshinEGraph;
import de.tub.tfs.henshin.tgg.interpreter.impl.TggTransformationImpl;
import de.tub.tfs.henshin.tgg.interpreter.impl.TranslationMaps;
import de.tub.tfs.henshin.tgg.interpreter.util.NodeUtil;
import de.tub.tfs.henshin.tgg.interpreter.util.RuleUtil;
import de.tub.tfs.henshin.tgg.interpreter.util.TggUtil;
/**
* The Class ExecuteOpRulesCommand executes all the given Rules ({@link TRule}) on a given graph. For the
* execution are mainly the classes from org.eclipse.emf.henshin.interpreter used. The mapping
* of the RuleApplication will be checked with the class {@link OpRuleConstraint}.
* There will be also the layouts for nodes and edges created.
*/
public class ExecuteOpRulesCommand extends CompoundCommand {
protected String consistencyType=null;
protected String consistencyTypeLowerCase=null; /**
* The graph on which all the rules will be applied.
*/
protected Graph graph;
/**
* The list of the Rules ({@link TRule}).
*/
protected List<Rule> opRuleList;
/**
* List of the successful RuleApplications.
*/
protected ArrayList<RuleApplicationImpl> ruleApplicationList= new ArrayList<RuleApplicationImpl>();
public ArrayList<RuleApplicationImpl> getRuleApplicationList() {
return ruleApplicationList;
}
private TggTransformation tggTrafo = null;
protected TranslationMaps translationMaps = null;
protected HashMap<EObject, Boolean> isTranslatedNodeMap = null;
protected HashMap<EObject, HashMap<EAttribute, Boolean>> isTranslatedAttributeMap = null;
protected HashMap<EObject, HashMap<EReference, HashMap<EObject, Boolean>>> isTranslatedEdgeMap = null;
protected Map<Node,EObject> node2eObject;
protected Map<EObject, Node> eObject2Node;
/**the constructor
* @param graph {@link ExecuteOpRulesCommand#graph}
* @param opRuleList {@link ExecuteOpRulesCommand#opRuleList}
*/
public ExecuteOpRulesCommand(Graph graph, List<Rule> opRuleList) {
super();
this.graph = graph;
this.opRuleList = opRuleList;
}
/* (non-Javadoc)
* @see org.eclipse.gef.commands.Command#canExecute()
*/
@Override
public boolean canExecute() {
return (graph != null && !opRuleList.isEmpty());
}
/** Executes all the rules on the graph as long as it's possible. The choosing of the sequence
* of RuleApplications is determinated by the order in the {@link ExecuteOpRulesCommand#opRuleList}.
* So when you execute the command twice without changing the order in the list, the same sequence
* of applications is chosen.
* @see org.eclipse.gef.commands.Command#execute()
*/
@Override
public void execute() {
tggTrafo = new TggTransformationImpl();
// create an Egraph from the triple graph
final TggHenshinEGraph henshinGraph = new TggHenshinEGraph(graph);
node2eObject = henshinGraph.getNode2ObjectMap();
tggTrafo.setInput(henshinGraph);
Module module = TggUtil.getModuleFromElement(graph);
tggTrafo.setNullValueMatching(module.isNullValueMatching());
tggTrafo.setOpRuleList(opRuleList);
// execute the transformation
tggTrafo.applyRules();
// update the node positions of the created nodes
createNodePositions(henshinGraph);
// set the graph element markers according to translation maps
setGraphMarkers();
}
private void createNodePositions(final TggHenshinEGraph henshinGraph) {
ruleApplicationList= tggTrafo.getRuleApplicationList();
int index=0;
for (RuleApplication r: ruleApplicationList){
index++;
createNodePositions(r, henshinGraph,
index * 40);
}
}
private void setGraphMarkers() {
translationMaps = tggTrafo.getTranslationMaps();
isTranslatedNodeMap = translationMaps.getIsTranslatedNodeMap();
isTranslatedEdgeMap = translationMaps.getIsTranslatedEdgeMap();
isTranslatedAttributeMap = translationMaps.getIsTranslatedAttributeMap();
for (Node n : graph.getNodes()) {
TNode node = (TNode) n;
EObject graphNodeEObject = node2eObject.get(node);
// set node component using the hash map from the transformation
if (tggTrafo.getTripleComponentNodeMap().containsKey(graphNodeEObject))
node.setComponent(tggTrafo.getTripleComponentNodeMap().get(graphNodeEObject));
if (isTranslatedNodeMap.containsKey(graphNodeEObject)) {
// set marker type to mark the translated nodes
node.setMarkerType(RuleUtil.Not_Translated_Graph);
if (isTranslatedNodeMap.get(graphNodeEObject)) {
// mark the translated node
node.setMarkerType(RuleUtil.Translated_Graph);
}
// check contained attributes
for (Attribute at : node.getAttributes()) {
// set marker type to mark the translated attributes
TAttribute a = (TAttribute) at;
a.setMarkerType(RuleUtil.Not_Translated_Graph);
//if (isTranslatedAttributeMap.get(graphNodeEObject)==null)continue; //NEW GERARD
if(!isTranslatedAttributeMap.get(graphNodeEObject).containsKey(a.getType()))
System.out.println("Inconsistent marking: attribute" + a.getType() + "=" + a.getValue()
+ " is not marked, but its container node is marked.");
else if (isTranslatedAttributeMap.get(graphNodeEObject).get(a.getType())) {
// mark the translated attribute
a.setMarkerType(RuleUtil.Translated_Graph);
}
}
}
else // node is not in marked component
{
node.setMarkerType(null);
for (Attribute at : node.getAttributes()) {
TAttribute a = (TAttribute) at;
a.setMarkerType(null);
}
}
}
for (Edge e : graph.getEdges()) {
TEdge edge = (TEdge) e;
EObject sourceNodeEObject = node2eObject.get(e.getSource());
EObject targetNodeEObject = node2eObject.get(e.getTarget());
if (isTranslatedEdgeMap.get(sourceNodeEObject)!=null &&
isTranslatedEdgeMap.get(sourceNodeEObject).get(edge.getType())!=null &&
isTranslatedEdgeMap.get(sourceNodeEObject).get(edge.getType()).containsKey(targetNodeEObject))
{
// set marker type to mark the translated attributes
edge.setMarkerType(RuleUtil.Not_Translated_Graph);
if (isTranslatedEdgeMap.get(sourceNodeEObject).get(edge.getType()).get(targetNodeEObject)) {
// mark the translated edge
edge.setMarkerType(RuleUtil.Translated_Graph);
}
}
else // edge is not in marked component - delete markers
{
edge.setMarkerType(null);
}
}
return;
}
/* (non-Javadoc)
* @see org.eclipse.gef.commands.Command#undo()
*/
@Override
public void undo() {
// for (int i = ruleApplicationList.size()-1; i>=0; i--) {
// ruleApplicationList.get(i).undo(null);
// }
// TODO: revise
}
/* (non-Javadoc)
* @see org.eclipse.gef.commands.Command#redo()
*/
@Override
public void redo() {
// for (RuleApplicationImpl rp : ruleApplicationList) {
// rp.redo(null);
// }
// TODO: revise
}
/**
* Creates the node layouts for the new nodes in the graph. The coordinates
* are calculated for each new node in the graph. relative to the next node.
*
* @param ruleApplication the rule application with the applied rule
* @param henshinGraph henshingraph on which the rule was aplied
* @param deltaY adds the value to the y coordinate of all generated layouts
*/
protected static void createNodePositions(RuleApplication ruleApplication,
TggHenshinEGraph henshinGraph, int deltaY) {
Rule rule = ruleApplication.getRule();
EList<Node> ruleNodes = (EList<Node>) rule.getRhs().getNodes();
// store rule nodes in two lists of preserved and created nodes
ArrayList<TNode> createdRuleNodes = new ArrayList<TNode>();
ArrayList<TNode> preservedRuleNodes = new ArrayList<TNode>();
TNode rNode;
for (Node rn : ruleNodes) {
rNode = (TNode) rn;
if (NodeUtil.isNew(rNode)) {
createdRuleNodes.add(rNode);
} else {
preservedRuleNodes.add(rNode);
}
}
Match comatch = ruleApplication.getResultMatch();
Map<EObject, Node> eObject2graphNode = henshinGraph.getObject2NodeMap();
for (TNode createdRuleNode : createdRuleNodes) {
//find next preservedRuleNode
Point createdRnPoint = new Point(createdRuleNode.getX(), createdRuleNode.getY());
TNode closestRn = createdRuleNode;
double bestDistance = Double.MAX_VALUE;
for (TNode preservedRn : preservedRuleNodes) {
Point preservedRnP = new Point(preservedRn.getX(), preservedRn.getY());
double curDistance = createdRnPoint.getDistance(preservedRnP);
if (curDistance < bestDistance) {
bestDistance = curDistance;
closestRn = preservedRn;
}
}
//get graph node at closest position
EObject closestGraphEObject = comatch.getNodeTarget(closestRn);
TNode closestGraphNode = (TNode) eObject2graphNode.get(closestGraphEObject);
//get created graph node
EObject createdGraphEObject = comatch.getNodeTarget(createdRuleNode);
TNode createdGraphNode = (TNode) eObject2graphNode.get(createdGraphEObject);
//set Point for created graph node as closestGraphNode.Point+distance
int dX, dY;
if (closestRn == createdRuleNode) {
// there is no preserved rule node, thus use the position of the rule node
dX = createdRuleNode.getX();
dY = createdRuleNode.getY();
} else {
dX = createdRuleNode.getX() - closestRn.getX();
dY = createdRuleNode.getY() - closestRn.getY();
}
int x = closestGraphNode.getX() + dX;
int y = closestGraphNode.getY() + dY;
if (NodeUtil.isCorrespondenceNode(createdGraphNode)){
if (((TripleGraph)createdGraphNode.getGraph()).getDividerSC_X() > x){
x = ((TripleGraph)createdGraphNode.getGraph()).getDividerSC_X() + 20;
}
if (((TripleGraph)createdGraphNode.getGraph()).getDividerCT_X() < x){
x = ((TripleGraph)createdGraphNode.getGraph()).getDividerSC_X() + 20;
}
} else if (NodeUtil.isTargetNode(createdGraphNode)){
if (((TripleGraph)createdGraphNode.getGraph()).getDividerCT_X() > x){
x = ((TripleGraph)createdGraphNode.getGraph()).getDividerCT_X() + 20;
}
}
createdGraphNode.setY(y+deltaY);
createdGraphNode.setX(x);
}
}
}