/*******************************************************************************
* 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.util;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.henshin.interpreter.EGraph;
import org.eclipse.emf.henshin.interpreter.matching.constraints.DomainSlot;
import org.eclipse.emf.henshin.model.Attribute;
import org.eclipse.emf.henshin.model.Edge;
import org.eclipse.emf.henshin.model.Formula;
import org.eclipse.emf.henshin.model.Graph;
import org.eclipse.emf.henshin.model.Mapping;
import org.eclipse.emf.henshin.model.NestedCondition;
import org.eclipse.emf.henshin.model.Node;
import de.tub.tfs.henshin.tgg.GraphLayout;
import de.tub.tfs.henshin.tgg.ImportedPackage;
import de.tub.tfs.henshin.tgg.NodeLayout;
import de.tub.tfs.henshin.tgg.TGG;
import de.tub.tfs.henshin.tgg.TNode;
import de.tub.tfs.henshin.tgg.TggFactory;
import de.tub.tfs.henshin.tgg.TripleComponent;
import de.tub.tfs.henshin.tgg.TripleGraph;
import de.tub.tfs.henshin.tgg.interpreter.impl.NodeTypes;
import de.tub.tfs.henshin.tgg.interpreter.impl.TggHenshinEGraph;
import de.tub.tfs.henshin.tgg.interpreter.util.ExceptionUtil;
import de.tub.tfs.henshin.tgg.interpreter.util.NodeUtil;
import de.tub.tfs.henshin.tgg.interpreter.util.RuleUtil;
import de.tub.tfs.henshin.tggeditor.TreeEditor;
import de.tub.tfs.henshin.tggeditor.figures.NodeFigure;
import de.tub.tfs.muvitor.commands.SimpleDeleteEObjectCommand;
import de.tub.tfs.muvitor.ui.IDUtil;
/**
* The Class Nodeutil
* Holds many helpful static functions for operating on nodes.
*/
public class GraphicalNodeUtil {
/**
* Gets the layout system which holds the given EObject
*
* @param eobject the eobject
* @return the layout system
*/
public static TGG getLayoutSystem(EObject eobject) {
if(!(IDUtil.getHostEditor(eobject) instanceof TreeEditor)) {
return null;
}
TreeEditor editor = (TreeEditor) IDUtil.getHostEditor(eobject);
if(editor == null) {ExceptionUtil.error("Tree editor is missing for retrieving the layout model."); return null;}
return editor.getLayout();
}
/**
* find all nodeLayouts to specific EPackage
* @param tgg the layoutSystem
* @param p EPackage for source, target oder correspondence
* @return a set node layouts with all nodeLayouts belongs to EPackage p
*/
public static Set<NodeLayout> getNodeLayouts(TGG tgg, EPackage p) {
Set<NodeLayout> set = new HashSet<NodeLayout>();
EList<NodeLayout> l = tgg.getNodelayouts();
if (p != null) {
for (NodeLayout nl : l) {
if (p.eContents().contains(nl.getNode().getType())) {
set.add(nl);
}
}
}
return set;
}
/**
* correct position of a node (in nodeFigure and nodeLayout) in relation to its NodeGraphType
* and the divider positions, this correction generates no notifications
* @param nodeFigure the figure of the node
*/
public static void correctNodeFigurePosition(NodeFigure nodeFigure) {
if(nodeFigure == null)return;
TNode node = nodeFigure.getNode();
if(node == null )
return;
if (node.getGraph()==null) return;
TripleGraph tripleGraph =(TripleGraph) node.getGraph();
int divSCx = tripleGraph.getDividerSC_X();
int divCTx = tripleGraph.getDividerCT_X();
int width = nodeFigure.getBounds().width;
int leftX = node.getX();
int correctionValue = 0;
TripleComponent type = NodeUtil.guessTripleComponent(node);
if (type == TripleComponent.SOURCE) {
if (leftX+width >= divSCx)
correctionValue = divSCx - leftX - width - 5;
}
else if (type == TripleComponent.CORRESPONDENCE) {
if (leftX < divSCx)
correctionValue = divSCx - leftX + 5;
else if (leftX+width > divCTx)
correctionValue = divCTx - leftX - width - 5;
// if node does not fit between the dividers: do not correct - dividers need to be moved manually
if (leftX + correctionValue < divSCx)
correctionValue=divSCx-leftX;
}
else if (type == TripleComponent.TARGET) {
if (leftX <= divCTx)
correctionValue = divCTx - leftX + 5;
}
if(correctionValue != 0) {
node.setX(leftX + correctionValue);
nodeFigure.setLocation(new Point(node.getX(), node.getY()));
}
}
private static final String EXCEPTION_NODE_IS_NOT_TNODE = "Triple component of node cannot be determined, because it is not of type TNode (node in a triple graph).";
/**
* Get the node layout of given node. If there's no node layout it creates one
* @param node
* @return the node layout linked to given node
*/
public static NodeLayout getNodeLayout(Node node) {
TGG layoutModel = getLayoutSystem(node);
if(layoutModel == null){ExceptionUtil.error("Layout model is missing for computing the node layout."); return null;}
NodeLayout result = null;
if (layoutModel != null) {
result = findNodeLayout(node, layoutModel);
if (result==null){
result = createNodeLayout(node,layoutModel);
}
}
return result;
}
/**
* get the mapping in rule of given node of rhs
* @param rhsNode
* @return
*/
public static Mapping getNodeMapping(Node rhsNode) {
EList<Mapping> mappingList = rhsNode.getGraph().getRule().getMappings();
for (Mapping m : mappingList) {
if (m.getImage() == rhsNode) {
return m;
}
}
return null;
}
/**
* checks if given node has a nac mapping
* @param node
* @return
*/
public static Boolean hasNodeNacMapping(Node node) {
if (node.getGraph() == null)
return false;
Formula formula = node.getGraph().getFormula();
if (formula != null) {
TreeIterator<EObject> iter = node.getGraph().getFormula().eAllContents();
while (iter.hasNext()) {
EObject o = iter.next();
if (o instanceof NestedCondition) {
NestedCondition nc = (NestedCondition)o;
for (Mapping m : nc.getMappings()) {
if (m.getOrigin() == node) {
return true;
}
}
}
}
}
return false;
}
/**
* get nac mapping of given node
* @param nc the nested condition with all mappings
* @param node
* @return (returns null if there's no mapping)
*/
public static Mapping getNodeNacMapping(NestedCondition nc, Node node) {
EList<Mapping> list = nc.getMappings();
for (Mapping m : list) {
if (m.getImage() == node) {
return m;
}
}
return null;
}
/**
* searches all mappings belongs to given node
* @param rhsNode
* @return a set of Mappings belongs to the given rhsNode (empty list if there are no mapping)
*/
public static List<Mapping> getNodeNacMappings(Node rhsNode) {
List<Mapping> nacMappings = new ArrayList<Mapping>();
Mapping ruleMapping = RuleUtil.getRHSNodeMapping(rhsNode);
if (ruleMapping != null) {
Node lhsNode = ruleMapping.getOrigin();
Formula formula = lhsNode.getGraph().getFormula();
if (formula !=null) {
TreeIterator<EObject> iter = ruleMapping.getOrigin().getGraph().getFormula().eAllContents();
while (iter.hasNext()) {
EObject o = iter.next();
if (o instanceof NestedCondition) {
NestedCondition nc = (NestedCondition)o;
for (Mapping m : nc.getMappings()) {
if (m.getOrigin() == lhsNode) {
nacMappings.add(m);
}
}
}
}
}
}
return nacMappings;
}
public static void deleteNodeNacMapping(Node node) {
}
/**
* creates a new nodeLayout in given layoutSystem which is linked to given node
* @param node
* @param layoutSystem
* @return the created node layout
*/
private static NodeLayout createNodeLayout(Node node, TGG layoutSystem) {
NodeLayout nodeLayout = TggFactory.eINSTANCE.createNodeLayout();
nodeLayout.setNode(node);
layoutSystem.getNodelayouts().add(nodeLayout);
return nodeLayout;
}
/**
* find node layout linked to given node in layoutSystem
* @param node
* @param tgg the layoutSystem
* @return the nodeLyout which belongs to given node
*/
protected static NodeLayout findNodeLayout(Node node, TGG tgg) {
for (NodeLayout nodeLayout : tgg.getNodelayouts()) {
if (nodeLayout.getNode() == node || nodeLayout.getLhsnode() == node) {
return nodeLayout;
}
}
return null;
}
/**
* find node layout linked to given node
* @param node
* @return the nodeLyout which belongs to given node
*/
protected static NodeLayout findNodeLayout(Node node) {
TGG tgg = getLayoutSystem(node);
return findNodeLayout(node, tgg);
}
/**
* find all nodeLayouts to specific EPackage
* @param p EPackage for source, target oder correspondence
* @param g Graph containing the nodes
* @return set of nodes belonging to EPackage p
*/
public static Set<Node> getNodes(EPackage p, Graph g) {
Set<Node> set = new HashSet<Node>();
if (p != null) {
for (Node n : g.getNodes()) {
if (p.eContents().contains(n.getType())) {
set.add(n);
}
}
}
return set;
}
/**
* find all nodeLayouts to specified list of EPackages
* @param p EPackage for source, target oder correspondence
* @param g Graph containing the nodes
* @return set of nodes belonging to EPackage p
*/
public static Set<Node> getNodes(List<EPackage> pkgs, Graph g) {
Set<Node> nodes = new HashSet<Node>();
for (EPackage p: pkgs){
nodes.addAll(getNodes(p, g));
}
return nodes;
}
/**
* checks whether a specific EClass is a source type in given layoutSystem
* @param tgg the layoutSystem
* @param type the EClass for check
* @return true if specific EClass is a source type
*/
public static boolean isSourceNode(TGG tgg, EClass type) {
if (tgg == null){
return false;
}
if(!isTypeGraphComplete(tgg.getImportedPkgs()))
return true;
else{
List<ImportedPackage> pkgs = NodeTypes.getImportedPackagesOfComponent(tgg.getImportedPkgs(),TripleComponent.SOURCE);
List<ImportedPackage> pkgt = NodeTypes.getImportedPackagesOfComponent(tgg.getImportedPkgs(),TripleComponent.TARGET);
List<ImportedPackage> pkgc = NodeTypes.getImportedPackagesOfComponent(tgg.getImportedPkgs(),TripleComponent.CORRESPONDENCE);
for (ImportedPackage importedPackage : pkgs) {
if (importedPackage.getPackage().equals(type.getEPackage()))
return true;
}
}
return false;
}
/**
* checks whether a node belongs to the source component
* @param node the graph node to be analysed
* @return true, if the node belongs to the source component
*/
public static boolean isSourceNodeByPosition(TNode node) {
if (node==null) return false;
//return guessTripleComponent(node) == TripleComponent.SOURCE;
// position has to be left of SC divider
TripleGraph tripleGraph =(TripleGraph) node.getGraph();
return node.getX() <= tripleGraph.getDividerSC_X();
}
/**
* computes the triple component of a given graph node
* @param node the graph node to by analysed
* @return the triple component of the graph node
*/
public static TripleComponent getComponent(TNode node) {
if (node==null) return TripleComponent.SOURCE;
TripleGraph tripleGraph =(TripleGraph) node.getGraph();
// position is left of SC divider
if (node.getX() <= tripleGraph.getDividerSC_X())
return TripleComponent.SOURCE;
// position is right of SC divider and left of CT divider
else if (node.getX() <= tripleGraph.getDividerCT_X())
return TripleComponent.CORRESPONDENCE;
// position is (right of SC divider and) right of CT divider
return TripleComponent.TARGET;
}
/**
* determines whether at least one package is loaded for each triple component
* @param importedPkgs
* @return
*/
public static boolean isTypeGraphComplete(
EList<ImportedPackage> importedPkgs) {
boolean isSetSourceTG = false;
boolean isSetCorrespondenceTG = false;
boolean isSetTargetTG = false;
ImportedPackage pkg;
Iterator<ImportedPackage> iter = importedPkgs.iterator();
while (iter.hasNext()) {
pkg = iter.next();
switch (pkg.getComponent()) {
case SOURCE:
isSetSourceTG = true;
case CORRESPONDENCE:
isSetCorrespondenceTG = true;
case TARGET:
isSetTargetTG = true;
}
}
return (isSetSourceTG && isSetCorrespondenceTG && isSetTargetTG);
}
/**
* checks whether a specific EClass is a target type in given layoutSystem
* @param tgg the layoutSystem
* @param type the EClass for check
* @return true if specific EClass is a target type
*/
public static boolean isTargetNode(TGG tgg, EClass type) {
if (tgg == null){
return false;
}
List<ImportedPackage> pkgs = NodeTypes.getImportedPackagesOfComponent(tgg.getImportedPkgs(),TripleComponent.SOURCE);
List<ImportedPackage> pkgt = NodeTypes.getImportedPackagesOfComponent(tgg.getImportedPkgs(),TripleComponent.TARGET);
List<ImportedPackage> pkgc = NodeTypes.getImportedPackagesOfComponent(tgg.getImportedPkgs(),TripleComponent.CORRESPONDENCE);
for (ImportedPackage importedPackage : pkgt) {
if (importedPackage.getPackage().equals(type.getEPackage()))
return true;
}
return false;
}
/**
* checks whether a specific EClass is a correspondence type in given layoutSystem
* @param tgg the layoutSystem
* @param type the EClass for check
* @return true if specific EClass is a correspondence type
*/
public static boolean isCorrespNode(TGG tgg, EClass type) {
if (tgg == null){
return false;
}
List<ImportedPackage> pkgs = NodeTypes.getImportedPackagesOfComponent(tgg.getImportedPkgs(),TripleComponent.SOURCE);
List<ImportedPackage> pkgt = NodeTypes.getImportedPackagesOfComponent(tgg.getImportedPkgs(),TripleComponent.TARGET);
List<ImportedPackage> pkgc = NodeTypes.getImportedPackagesOfComponent(tgg.getImportedPkgs(),TripleComponent.CORRESPONDENCE);
for (ImportedPackage importedPackage : pkgc) {
if (importedPackage.getPackage().equals(type.getEPackage()))
return true;
}
return false;
}
/**
* Searches the graph layout of divider. If dividerSC is true it searches for
* source-correspondence divider, if its false it searches for the correspondence-target divider.
* @param graph which is linked to its dividers
* @param dividerSC (search SC or CT divider?)
* @return graph layout of searched divider
*/
private static GraphLayout getGraphLayout(Graph graph, boolean dividerSC) {
TGG layoutSystem = getLayoutSystem(graph);
if (layoutSystem != null) {
for (GraphLayout graphLayout : layoutSystem.getGraphlayouts()) {
if (graphLayout.getGraph() == graph) {
if (dividerSC == graphLayout.isIsSC())
return graphLayout;
}
}
}
return null;
}
public static void refreshIsMarked(Node ruleNodeRHS) {
if (((TNode) ruleNodeRHS).getMarkerType() != null)
return;
else { // marker is not available, thus copy from layout model and
// delete entry in layout model
computeAndCreateIsMarked(ruleNodeRHS);
}
}
private static void computeAndCreateIsMarked(Node ruleNodeRHS) {
// marker value is not available in ruleAttributeRHS, thus compute it
NodeLayout nodeLayout = findNodeLayout(ruleNodeRHS);
if (nodeLayout == null) { // no layout is found
// determine type of marker
// Rule rule = ruleNodeRHS.getGraph().getContainerRule();
// if (ModelUtil.getRuleLayout(rule)!=null)
// ruleNodeRHS.setMarkerType(RuleUtil.Translated);
// else
((TNode) ruleNodeRHS).setMarkerType(RuleUtil.NEW);
// check for existing node in LHS
Node lhsNode = RuleUtil
.getLHSNode(ruleNodeRHS);
if (lhsNode != null) {
// node is preserved -> no marker
((TNode) ruleNodeRHS).setMarkerType(null);
} else {
// node is created -> add marker
((TNode) ruleNodeRHS).setMarkerType(RuleUtil.NEW);
}
} else { // layout is found
Boolean isTranslatedLHS = nodeLayout.getLhsTranslated();
boolean isNew = nodeLayout.isNew();
if (isTranslatedLHS == null) {
((TNode) ruleNodeRHS).setMarkerType(RuleUtil.NEW);
} else {
((TNode) ruleNodeRHS).setMarkerType(RuleUtil.Translated);
}
}
// delete layout entry in layout model
while (nodeLayout != null) {
SimpleDeleteEObjectCommand cmd = new SimpleDeleteEObjectCommand(
nodeLayout);
cmd.execute();
// find possible duplicates of layout
nodeLayout = findNodeLayout(ruleNodeRHS);
}
return;
}
public static void refreshLayout(TNode node, NodeLayout nodeLayout) {
computeAndCreateLayout(node,nodeLayout);
}
private static void computeAndCreateLayout(TNode node, NodeLayout nodeLayout) {
// marker value is not available in ruleAttributeRHS, thus compute it
if (nodeLayout == null) { // no layout is found
// store coordinates (0,0)
node.setX(0);
node.setY(0);
} else { // layout is found
// store coordinates
node.setX(nodeLayout.getX());
node.setY(nodeLayout.getY());
}
return;
}
// returns whether the node is translated already in the LHS
public static Boolean getNodeIsTranslated(Node node) {
if (((TNode) node).getMarkerType() != null) {
if (RuleUtil.Not_Translated_Graph.equals(((TNode) node).getMarkerType()))
// node is translated by the rule - it is not yet translated
return false;
else if (RuleUtil.Translated_Graph.equals(((TNode) node).getMarkerType()))
// node is context element - it is already translated
return true;
}
// node is not marked with a relevant marker
return null;
}
// returns true, if the node is marked with the "NEW" marker
public static boolean isNew(Node rn) {
return (((TNode) rn).getMarkerType()!=null && ((TNode) rn).getMarkerType().equals(RuleUtil.NEW));
}
/**
* Find the attribute with a specific type. Is just working
* when there is not more than one one type of attribute in a node.
* @param graphNode source node
* @param type type of the attribute
* @return the corresponding attribute of graphNode
*/
public static Attribute findAttribute(Node graphNode, EAttribute type) {
for (Attribute a : graphNode.getAttributes()) {
if (a.getType() == type) {
return a;
}
}
return null;
}
public static Node getGraphNode(DomainSlot slot, EGraph graph) {
return ((TggHenshinEGraph)graph).getObject2NodeMap().get(slot.getValue());
}
public static Node getGraphNode(EObject slot, EGraph graph) {
return ((TggHenshinEGraph)graph).getObject2NodeMap().get(slot);
}
//NEW
public static boolean isCorrNode(Node node){
return guessTC(node).equals(TripleComponent.CORRESPONDENCE);
}
//NEW
public static TripleComponent guessTC(Node n){
// TripleComponent result = null;
// if ((n instanceof TNode) && n.getGraph()!=null && (n.getGraph() instanceof TripleGraph)){
// TNode node = (TNode)n;
// TripleGraph graph = (TripleGraph) node.getGraph();
// int scx = graph.getDividerSC_X();
// int ctx = graph.getDividerCT_X();
// int x = node.getX();
//
// if (scx==0 || ctx==0 || x==0) throw new IllegalArgumentException("Coordinates of node"+node+" Rule: "+node.getGraph().getRule()+ " and graphdividers should be set for TripleComponent guessing: scx "+scx+" ctx "+ctx+" x"+x);
//
// if (0<x && x<scx){
// result = TripleComponent.SOURCE;
// }else if (scx<=x && x<=ctx){
// result = TripleComponent.CORRESPONDENCE;
// }else if (x>ctx){
// result = TripleComponent.TARGET;
// }
// }
//
// TripleComponent guess = guessTripleComponent((TNode)n);
// if ((result!=null) && !result.equals(guess)){
// throw new IllegalArgumentException("TripleComponent of inconsistent node can not be guessed");
// }
// return guess;
TNode node = (TNode) n;
if (node.getComponent()!=null) return node.getComponent();
return NodeUtil.getComponentFromPosition((TNode)n);
}
//NEW
public static boolean replaeAttributeVariables(Node node){
int s = node.getAttributes().size();
boolean found = false;
for (int i=0; i<s; i++) {
Attribute att = node.getAttributes().get(i);
if (!(att.getValue().startsWith("\'") || att
.getValue().startsWith("\""))) {
att.setValue("\'variable\'");
found = true;
}
}
return found;
}
/*
//NEW
public static boolean expands(Node n1, Node n2, boolean allowCorrespondenceNodes){
if (n1==null || n2 == null) {
return false;
}
if (!n1.toString().equals(n2.toString())){
return false;
}
if (!n1.getType().getEPackage().equals(n2.getType().getEPackage())) {
if (n1.toString().equals(n2.toString())){
//System.out.println(n1.toString()+" different packages");
}
return false;
}
EList<Attribute> as1 = n1.getAttributes();
EList<Attribute> as2 = n2.getAttributes();
for(Attribute a2 : as2){
boolean found = false;
for (Attribute a1 : as1){
if (a1.getType().getName().equals(a2.getType().getName()) &&
a1.getValue().equals(a2.getValue())){
found = true;
break;
}
}
if (!found) {
return false;
}
}
if (n1.getName()!= null && n2.getName()!= null && !n1.getName().equals(n2.getName())) {
//if(n1.toString().equals(n2.toString())){
// System.out.println(n1.toString()+" Different names");
//}
System.out.println("weird: only different names: "+n1.getName()+" "+n2.getName());
return false;
}
if (n1 instanceof TNode && n2 instanceof TNode){
TNode tn1 = (TNode)n1;
TNode tn2 = (TNode)n2;
if (!allowCorrespondenceNodes){
if (guessTripleComponent(tn1).equals(TripleComponent.CORRESPONDENCE)
|| guessTripleComponent(tn2).equals(TripleComponent.CORRESPONDENCE)) return false;
}
if (!guessTripleComponent(tn1).equals(guessTripleComponent(tn2))) {
System.out.println("different guesses");
//return false;
}
if (!guessTC(tn1).equals(guessTC(tn2))){
System.out.println("new Guess id different");
//return false;
}
}
return true;
}*/
}