/** * <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.interpreter.matching.constraints; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.henshin.interpreter.EGraph; import org.eclipse.emf.henshin.interpreter.impl.EGraphImpl; import org.eclipse.emf.henshin.interpreter.matching.conditions.ConditionHandler; import org.eclipse.emf.henshin.interpreter.util.DomainList; public class DomainSlot { /** * The variable which will initialize this domain slot. All other variables * which use this slot will only validate their constraints. */ Variable owner; /** * Flag that describes whether this domain slot is initialized. After * initialization the domain contains all possible values that are type * compatible with the type constraint of the owner variable. */ boolean initialized; /** * Flag that describes whether this domain slot is locked. A slot is locked * if a value from the domain is selected that fulfills the constraints of * the owner variable. */ boolean locked; /** * The current fixed value for this domain slot. Instantiate() will pick one * value from the domain. */ EObject value; /** * All possible values this domain slot might use for instantiation. */ List<EObject> domain; /** * A list of required values created by binary constraints from external * variables. */ List<EObject> temporaryDomain; /** * All changes done to other domain slots by ReferenceConstraints of * variables that use this domain slot. */ final Map<BinaryConstraint, DomainChange> remoteChangeMap; /** * A collection of parameters that were initialized by constraints belonging * to variables of this domain slot. */ final List<String> initializedParameters; /** * The handler for all attribute and index conditions. If a parameter constraints * fixes the value of a parameter, the handler checks all conditions. */ final ConditionHandler conditionHandler; /** * A collection of variables whose constraints were already validated * against the current value. */ final Set<Variable> checkedVariables; /** * A collection of the values of all domain slots that are currently locked. * Required to ensure injectivity. */ final Set<EObject> usedObjects; /** * Flag indicating whether the injective matching should be used. */ final boolean injective; /** * Flag indicating whether the the matcher should check for dangling edges. */ final boolean dangling; /** * Flag indicating whether the matching should be deterministic. */ final boolean deterministic; /** * Flag indicating whether to use inverse matching order. */ final boolean inverseMatchingOrder; /** * Constructor. * @param conditionHandler Condition handler to be used. * @param usedObjects Used objects. * @param options Options. */ public DomainSlot(ConditionHandler conditionHandler, Set<EObject> usedObjects, boolean injective, boolean dangling, boolean deterministic, boolean inverseMatchingOrder) { this.locked = false; this.initialized = false; this.conditionHandler = conditionHandler; this.usedObjects = usedObjects; this.remoteChangeMap = new HashMap<BinaryConstraint, DomainChange>(); this.initializedParameters = new ArrayList<String>(); this.checkedVariables = new HashSet<Variable>(); this.injective= injective; this.dangling = dangling; this.deterministic = deterministic; this.inverseMatchingOrder = inverseMatchingOrder; } /** * Sets the value of the domain slot. * @param variable Variable to be set. * @param domainMap The domain map to be used. * @param graph The target graph. * @return <code>true</code> if the instantiation was successful. */ public boolean instantiate(Variable variable, Map<Variable, DomainSlot> domainMap, EGraph graph) { // Initialize? if (!initialized) { initialized = true; owner = variable; // If temporaryDomain is not null, there are BinaryConstraints restricting this slot's domain. if (temporaryDomain != null) { domain = new DomainList<EObject>(temporaryDomain); } // Set the domain: variable.typeConstraint.initDomain(this, graph); if (domain.isEmpty()) { return false; } // Non-deterministic matching? if (!deterministic) { Collections.shuffle(domain); } // Injective matching? if (injective) { domain.removeAll(usedObjects); } } // Lock the slot? if (!locked) { if (domain.isEmpty()) { return false; } if (inverseMatchingOrder) { value = domain.remove(domain.size() - 1); } else { value = domain.remove(0); } usedObjects.add(value); locked = true; } // Check the variable? if (!checkedVariables.contains(variable)) { // Check the type constraint: if (!variable.typeConstraint.check(this)) { return false; } // Check the dangling constraints: if (dangling) { for (DanglingConstraint constraint : variable.danglingConstraints) { if (!constraint.check(value, graph)) { return false; } } } // Check the attribute constraints: for (AttributeConstraint constraint : variable.attributeConstraints) { if (!constraint.isConstantValue) { if (!conditionHandler.isSet((String) constraint.value)) { initializedParameters.add((String) constraint.value); } } if (!constraint.check(this)) { return false; } UnaryConstraint unaryUserConstraint = variable.attributeUserConstraints.get(constraint); if (unaryUserConstraint != null){ if (!unaryUserConstraint.check(this)){ return false; } } } // Check the containment constraints: for (ContainmentConstraint constraint : variable.containmentConstraints) { DomainSlot targetSlot = domainMap.get(constraint.targetVariable); if (!constraint.check(this, targetSlot)) { return false; } } // Check the reference constraints: for (ReferenceConstraint constraint : variable.referenceConstraints) { DomainSlot targetSlot = domainMap.get(constraint.targetVariable); if (!constraint.check(this, targetSlot)) { return false; } BinaryConstraint binaryUserConstraint = variable.binaryUserConstraints.get(constraint); if (binaryUserConstraint != null){ if (!binaryUserConstraint.check(this, targetSlot)) { return false; } } } // Check the path constraints: for (PathConstraint constraint : variable.pathConstraints) { DomainSlot targetSlot = domainMap.get(constraint.targetVariable); if (!constraint.check(this, targetSlot)) { return false; } } // Check the user constraints: for (UnaryConstraint constraint : variable.userConstraints){ if (!constraint.check(this)) { return false; } } // All checks were successful: checkedVariables.add(variable); } // Instantiation was successful: return true; } /** * Removes the lock on this domain slot. If the domain contains additional * objects {@link #instantiate(Variable, Map, EGraphImpl)} may be called * again. * @param sender * The variable which uses this domain slot. Only the variable * which originally initialized this domain slot is able to * unlock it. * @return <code>true</code> if another instantiation is possible. */ public boolean unlock(Variable sender) { // Revert the changes to the temporary domain: long t0 = System.nanoTime(); int refCount = sender.referenceConstraints.size(); int conCount = sender.containmentConstraints.size(); for (int i=refCount+conCount-1; i>=0; i--) { BinaryConstraint constraint = (i>=refCount) ? sender.containmentConstraints.get(i-refCount) : sender.referenceConstraints.get(i); DomainChange change = remoteChangeMap.get(constraint); if (change != null) { change.slot.temporaryDomain = change.originalValues; remoteChangeMap.remove(constraint); } } long t1 = System.nanoTime(); // Unlock the variable: if (locked && sender == owner) { locked = false; usedObjects.remove(value); for (String parameterName : initializedParameters) { conditionHandler.unsetParameter(parameterName); } initializedParameters.clear(); checkedVariables.clear(); long t2 = System.nanoTime(); return !(domain == null || domain.isEmpty()); } else { checkedVariables.remove(sender); } // Was not locked: return false; } /** * Clears this domain slot to the state before it was initialized. * Only the variable that originally initialized this domain slot * is able to clear it. * @param sender The variable which uses this domain slot. */ public void clear(Variable sender) { unlock(sender); if (sender == owner) { initialized = false; remoteChangeMap.clear(); owner = null; value = null; domain = null; } } /** * Resets this domain slot to the state before it was initialized. * Only the variable that originally initialized this domain slot * is able to reset it. Resetting means clearing and additionally * resetting the temporary domain. * @param sender The variable which uses this domain slot. */ public void reset(Variable sender) { if (sender == owner) { temporaryDomain = null; } clear(sender); } /** * Re-check the constraint of a variable. It is assumed that this * slot is initialized and locked. * @param variable Variable to be re-checked. * @param domainMap The domain map. * @return <code>true</code> if all constraint were successfully checked. */ public boolean recheck(Variable variable, Map<Variable, DomainSlot> domainMap) { checkedVariables.remove(variable); return instantiate(variable, domainMap, null); } /** * Checks whether the domain contains additional possible objects that may * be valid for a match. * @return <code>true</code>, if instantiation might be possible. */ public boolean instantiationPossible() { if (domain == null) { return false; } if (!locked) { return domain.size() > 0; } return false; } /** * Locks a specific value for this slot. The slot will also be locked and * marked as initialized and the value can only be changed by calling this * method again. * @param value The object this domain slot will be mapped to. */ public void fixInstantiation(EObject value) { this.locked = true; this.value = value; this.initialized = true; this.usedObjects.add(value); this.owner = null; } /** * @return the locked */ public boolean isLocked() { return locked; } /** * @return the locked */ public EObject getValue(){ return this.value; } /** * @return the domain */ public List<EObject> getDomain() { return domain; } /** * @param temporaryDomain the temporaryDomain to set */ public void setTemporaryDomain(List<EObject> temporaryDomain) { this.temporaryDomain = temporaryDomain; } /** * @return the temporaryDomain */ public List<EObject> getTemporaryDomain() { return temporaryDomain; } /** * @return the remoteChangeMap */ public Map<BinaryConstraint, DomainChange> getRemoteChangeMap() { return remoteChangeMap; } }