/******************************************************************************* * 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.tgg.interpreter.impl; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.henshin.interpreter.matching.constraints.BinaryConstraint; import org.eclipse.emf.henshin.interpreter.matching.constraints.DomainChange; import org.eclipse.emf.henshin.interpreter.matching.constraints.DomainSlot; import org.eclipse.emf.henshin.model.Edge; import org.eclipse.emf.henshin.model.Node; import org.eclipse.emf.henshin.model.Rule; import de.tub.tfs.henshin.tgg.TEdge; import de.tub.tfs.henshin.tgg.interpreter.util.RuleUtil; /** * This class is for checking the correct mapping in a execution cycle of * FTRules. It is given to the {@link EmfEngine} of henshin. * * @see ExecuteFTRulesCommand * @see EmfEngine#registerUserConstraint(Class, Object...) */ public class OpRuleEdgeConstraintEMF implements BinaryConstraint { /** * This hashmap will be filled during the execution of all the {@link TRule} * s in the {@link ExecuteFTRulesCommand}. The hashmap contains all the * already translated edges of the graph on which the {@link TRule}s are * executed. An edge is identified by the triple (source, type, target) */ private HashMap<EObject, HashMap<EReference, HashMap<EObject, Boolean>>> isTranslatedEdgeMap; /** * The node which can be mapped to another node in the graph (see * {@link FTRuleConstraint#check(Node graphNode)}). The node could be a node * in a {@link Rule} or in a nac. */ private TEdge ruleEdge; private String ruleEdgeMarker; private DomainSlot source; private DomainSlot target; public static String EDGE_ERROR="Matching error: ruleEdge is not a TEdge - rule is not valid in HenshinTGG."; /** * the constructor * * @param ruleTNode * see {@link FTRuleConstraint#ruleTNode} * @param isTranslatedMap * see {@link FTRuleConstraint#isTranslatedMap} */ public OpRuleEdgeConstraintEMF( Edge edge, HashMap<EObject, HashMap<EReference, HashMap<EObject, Boolean>>> isTranslatedEdgeMap) { assert(edge instanceof TEdge):EDGE_ERROR; this.ruleEdge = (TEdge) edge; assert(edge != null):EDGE_ERROR; this.ruleEdgeMarker = ruleEdge.getMarkerType(); this.isTranslatedEdgeMap = isTranslatedEdgeMap; } /** * Checks if the mapping in a {@link TRule}. * * @see org.eclipse.emf.henshin.interpreter.matching.constraints.HenshinUserConstraint#check(org.eclipse.emf.henshin.model.Node) */ @Override public boolean check(DomainSlot source, DomainSlot target) { assert (ruleEdge == null):EDGE_ERROR; if (ruleEdgeMarker == null || RuleUtil.TR_UNSPECIFIED.equals(ruleEdgeMarker)) // edge is not marked or marked with wild card - no marker // restriction - only component restriction return true; if (ruleEdge.getType().isDerived()) { return true; } // rule Edge is marked (no wild card) this.source = source; this.target = target; // source node is in marked component EObject sourceObjectInGraph = source.getValue(); // retrieve available marked target nodes in graph fitting to the graph // edge HashMap<EObject, Boolean> markedTargetsInGraph = null; HashMap<EReference, HashMap<EObject, Boolean>> markedReferencesInGraph = isTranslatedEdgeMap .get(sourceObjectInGraph); if (markedReferencesInGraph == null) return false; // markedReferences != null, retrieve marked target nodes markedTargetsInGraph = markedReferencesInGraph.get(ruleEdge.getType()); // check markers return checkEdgeMarker(markedTargetsInGraph); } private boolean checkEdgeMarker( HashMap<EObject, Boolean> markedTargetsInGraph) { // there are no marked edges available in graph if (markedTargetsInGraph == null) { // rule edge is not marked - all targets fit if (ruleEdgeMarker == null) return true; // rule edge is marked (no wild card) - no target fits else return false; } // iterate over all possible target graph nodes Collection<EObject> newReferredObjects = new ArrayList<EObject>(1); Collection<EObject> currentReferredObjects = null; currentReferredObjects = retrieveCurrentReferencedObjects(currentReferredObjects); if (currentReferredObjects.isEmpty()) return false; // if change of currently possible matches for the target node occur, it // will trigger a domain change boolean changeOccurred = false; // iterate over each currently possible target node in the graph for // this reference for (EObject graphTargetNodeObject : currentReferredObjects) { if (!markedTargetsInGraph.containsKey(graphTargetNodeObject)) { // current matched graph target of edge is not in the list of // possible // marked nodes, then check that the rule edge has either no // marker or unspecified marker if (ruleEdgeMarker == null) // case: ruleEdge and current nodeTarget reference are not // marked newReferredObjects.add(graphTargetNodeObject); else // case: rule edge is marked (no wild card), but graph edge // has no marker - // do not put in new list and indicate change changeOccurred = true; } else {// the current graph edge is marked // check that the marker of the rule edge fits if (ruleEdgeMarker == null) { // inconsistency: rule edge has no marker, but graph edge // has - do not put in new list changeOccurred = true; } Boolean edgeMarkerInGraph = markedTargetsInGraph .get(graphTargetNodeObject); if (RuleUtil.Translated_Graph.equals(ruleEdgeMarker)) { if (edgeMarkerInGraph == true) newReferredObjects.add(graphTargetNodeObject); } // edge is to be translated, thus it is not yet translated else if (RuleUtil.Not_Translated_Graph.equals(ruleEdgeMarker)) { if (edgeMarkerInGraph == false) newReferredObjects.add(graphTargetNodeObject); } else { changeOccurred = true; } } } // if there are no remaining valid targets for the current reference, // then stop here and backtrack the matching if (newReferredObjects.isEmpty()) // match cannot be completed - abort return false; if (changeOccurred) { performChange(newReferredObjects); boolean remainingTargetsExist = !target.getTemporaryDomain() .isEmpty(); if (!remainingTargetsExist) // match cannot be completed - abort return false; } // check was successful return true; } private Collection<EObject> retrieveCurrentReferencedObjects( Collection<EObject> currentReferredObjects) { // retrieve the currently possible matches of the target node to // possible target nodes in the graph if (target.isLocked()) { currentReferredObjects = new ArrayList<EObject>(1); if (target.getValue() != null) currentReferredObjects.add(target.getValue()); } else if (target.getDomain() != null) currentReferredObjects = target.getDomain(); else if (target.getTemporaryDomain() != null) currentReferredObjects = target.getTemporaryDomain(); return currentReferredObjects; } private void performChange(Collection<EObject> newReferredObjects) { DomainChange change = new DomainChange(target, target.getTemporaryDomain()); source.getRemoteChangeMap().put(this, change); target.setTemporaryDomain(new ArrayList<EObject>(newReferredObjects)); if (change.getOriginalValues() != null) target.getTemporaryDomain().retainAll(change.getOriginalValues()); } }