/******************************************************************************* * 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.actions.validate; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.henshin.model.Edge; import org.eclipse.emf.henshin.model.Graph; import org.eclipse.emf.henshin.model.Node; import org.eclipse.gef.EditPart; import org.eclipse.gef.ui.actions.SelectionAction; import org.eclipse.swt.SWT; import org.eclipse.ui.IWorkbenchPart; import de.tub.tfs.henshin.tgg.TripleComponent; import de.tub.tfs.henshin.tgg.interpreter.impl.NodeTypes; import de.tub.tfs.henshin.tggeditor.dialogs.ValidTestDialog; import de.tub.tfs.henshin.tggeditor.editparts.tree.graphical.GraphTreeEditPart; import de.tub.tfs.muvitor.ui.MuvitorTreeEditor; /** * The class GraphValidAction validates the graph if it is emf consistent. */ public class GraphValidAction extends SelectionAction { /** The fully qualified class ID. */ public static final String ID = "henshineditor.actions.GraphValidAction"; /** The Constant DESC for the description. */ static private final String DESC = "Validate Graph"; /** The Constant TOOLTIP. */ static private final String TOOLTIP = "Validate Graph"; /** The graph which is supposed to be vaildated. */ protected Graph graph; /** * The constructor receives a part of the class and calls it IWorkbenchPart * Its super constructor. * Setting the tooltip text, and description of the action and the action ID. * In addition given a description. * * @param part the part */ public GraphValidAction(IWorkbenchPart part) { super(part); setId(ID); setText(DESC); setDescription(DESC); setToolTipText(TOOLTIP); } /* * (non-Javadoc) * * @see org.eclipse.gef.ui.actions.WorkbenchPartAction#calculateEnabled() */ @Override protected boolean calculateEnabled() { List<?> selectedObjects = getSelectedObjects(); if (selectedObjects.size() != 1) { return false; } Object selectedObject = selectedObjects.get(0); if ((selectedObject instanceof EditPart)) { EditPart editpart = (EditPart) selectedObject; if (editpart instanceof GraphTreeEditPart) { graph = (Graph) editpart.getModel(); return true; } } return false; } /** * In the run () - method checks the validity of the graph, * This is to check the root node and containment edges. * @see org.eclipse.jface.action.Action#run() */ @Override public void run() { List<String> fehlerMeldungen = new ArrayList<String>(); checkGraphValid(fehlerMeldungen); openDialog(fehlerMeldungen); } protected void openDialog(List<String> fehlerMeldungen) { if (fehlerMeldungen.size() == 0) { fehlerMeldungen.add("Everything Ok!"); } ValidTestDialog vD = new ValidTestDialog(getWorkbenchPart().getSite() .getShell(), SWT.NULL, fehlerMeldungen); vD.open(); } protected void checkGraphValid(List<String> fehlerMeldungen) { Map<EPackage, LinkedList<Node>> ePackage2NodeList = getRoots(); Set<EPackage> ePackages=ePackage2NodeList.keySet(); for (EPackage ePackage:ePackages){ LinkedList<Node> rootNodes=ePackage2NodeList.get(ePackage); if (rootNodes.size()!=1){ if (rootNodes.size() == 0) { fehlerMeldungen.add("The "+ePackage.getName()+"-graph has no rootnode!"); ePackage2NodeList.remove(ePackage); } if (rootNodes.size()>1){ TripleComponent type = NodeTypes.getTripleComponent(rootNodes.get(0)); if (type != TripleComponent.CORRESPONDENCE) { fehlerMeldungen.add("The "+ePackage.getName()+"-graph has more than one rootnode!"); } } } } for (Node node : graph.getNodes()) { if (NodeTypes.isContainment(node)) { int count = 0; for (Edge edge : node.getIncoming()) { if (edge.getType().isContainment()) { count++; } } if (count > 1) { fehlerMeldungen.add("The node \"" + node.getName() + ": " + node.getType().getName() + "\" has more than one containment edges!"); } } } LinkedList<Node> allNodes = new LinkedList<Node>(graph.getNodes()); while (allNodes.size() > 0) { Map<Node, List<List<Node>>> nodeWithPaths = new HashMap<Node, List<List<Node>>>(); List<Node> pfad = new ArrayList<Node>(); Node startNode = getNextStartNode(ePackage2NodeList, allNodes); addSuccessorNodes(nodeWithPaths, allNodes, startNode, pfad, true); while (nodeWithPaths.size() > 0) { Node node = nodeWithPaths.keySet().iterator().next(); List<List<Node>> actualPaths = nodeWithPaths.get(node); nodeWithPaths.remove(node); for (List<Node> visitedNodes : actualPaths) { if (visitedNodes.contains(node)) { String s="The graph contains a cycle! ("; int index=visitedNodes.indexOf(node); for (int i=index;i<visitedNodes.size();i++){ Node nn=visitedNodes.get(i); String name=" "; if (nn.getName()!=null){ name+=nn.getName(); } name+=":"+nn.getType().getName(); s+=name+" ->"; } String name=" "; if (node.getName()!=null){ name+=node.getName(); } name+=":"+node.getType().getName(); s+=name+")"; fehlerMeldungen.add(s); continue; } List<Node> newVisitedNode = new ArrayList<Node>( visitedNodes); addSuccessorNodes(nodeWithPaths, allNodes, node, newVisitedNode, false); } } } } // /* // * (non-Javadoc) // * // * @see org.eclipse.jface.action.Action#getImageDescriptor() // */ // @Override // public ImageDescriptor getImageDescriptor() { // return IconUtil.getDescriptor("check18.png"); // } /** * Gets the next start node. * * @param ePackage2NodeList the e package2 node list * @param allNodes the all nodes * @return the next start node */ private Node getNextStartNode( Map<EPackage, LinkedList<Node>> ePackage2NodeList, LinkedList<Node> allNodes) { Node startNode; if (ePackage2NodeList.size() > 0) { EPackage eP = ePackage2NodeList.keySet().iterator().next(); LinkedList<Node> wurzelnNodes = ePackage2NodeList.get(eP); startNode = wurzelnNodes.poll(); if (wurzelnNodes.isEmpty()) { ePackage2NodeList.remove(eP); } allNodes.remove(startNode); } else { startNode = allNodes.poll(); } return startNode; } /** * Adds the successor nodes. * * @param nodeWithPaths the node with paths * @param allNodes the all nodes * @param actualNode the actual node * @param pfad the pfad * @param nurNichtBesuchte the nur nicht besuchte */ private void addSuccessorNodes( Map<Node, List<List<Node>>> nodeWithPaths, LinkedList<Node> allNodes, Node actualNode, List<Node> pfad, boolean nurNichtBesuchte) { pfad.add(actualNode); for (Edge edge : actualNode.getOutgoing()) { if (edge.getType().isContainment()) { Node target = edge.getTarget(); if (!nurNichtBesuchte || allNodes.contains(target) || target==actualNode) { allNodes.remove(target); if (nodeWithPaths.containsKey(target)) { nodeWithPaths.get(target).add(pfad); } else { List<List<Node>> newPaths = new ArrayList<List<Node>>(); newPaths.add(pfad); nodeWithPaths.put(target, newPaths); } } } } } /** * Gets the roots. * * @return the roots */ protected Map<EPackage, LinkedList<Node>> getRoots(){ Map<EPackage, LinkedList<Node>> ePackage2NodeList = new HashMap<EPackage, LinkedList<Node>>(); for (Node node : graph.getNodes()) { EPackage ePackge= node.getType().getEPackage(); if (!ePackage2NodeList.containsKey(ePackge)){ ePackage2NodeList.put(ePackge, new LinkedList<Node>()); } if (!NodeTypes.isContainment(node)) { ePackage2NodeList.get( node.getType().getEPackage()).add(node); } } return ePackage2NodeList; } }