blob: c59b3af0ae5a692733f49a39668d05ddf4cecd23 [file] [log] [blame]
/**
* <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.List;
import java.util.Map;
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;
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) {
if (!initialize(variable, graph)) {
return false;
}
if (!setValueAndLock()) {
return false;
}
// Check the variable?
if (!checkedVariables.contains(variable)) {
if (!checkTypeConstraint(variable)) {
return false;
}
// Check the dangling constraints:
if (dangling) {
for (DanglingConstraint danglingConstraint : variable.danglingConstraints) {
if (!checkDanglingConstraint(danglingConstraint, graph)) {
return false;
}
}
}
// Check the attribute constraints:
for (AttributeConstraint attributeConstraint : variable.attributeConstraints) {
UnaryConstraint unaryUserConstraint = variable.attributeUserConstraints.get(attributeConstraint);
if (!checkAttributeConstraint(attributeConstraint, unaryUserConstraint)) {
return false;
}
}
// Check the containment constraints:
for (ContainmentConstraint containmentConstraint : variable.containmentConstraints) {
if (!checkContainmentConstraint(containmentConstraint, domainMap)) {
return false;
}
}
// Check the reference constraints:
for (ReferenceConstraint referenceConstraint : variable.referenceConstraints) {
BinaryConstraint binaryUserConstraint = variable.binaryUserConstraints.get(referenceConstraint);
if (!checkReferenceConstraint(referenceConstraint, binaryUserConstraint, domainMap)) {
return false;
}
}
// Check the path constraints:
for (PathConstraint pathConstraint : variable.pathConstraints) {
if (!checkPathConstraint(pathConstraint, domainMap)) {
return false;
}
}
// Check the user constraints:
for (UnaryConstraint userConstraint : variable.userConstraints) {
if (!checkUserConstraint(userConstraint)) {
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:
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);
}
}
// Unlock the variable:
if (locked && sender == owner) {
locked = false;
usedObjects.remove(value);
for (String parameterName : initializedParameters) {
conditionHandler.unsetParameter(parameterName);
}
initializedParameters.clear();
checkedVariables.clear();
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;
}
/**
* Initializes the domain slot, i.e., sets the slot's owner and domain
* under consideration of the {@link #deterministic} and {@link #injective} flags
* @param variable the variable which uses this domain slot
* @param graph the EGraph used to initialized this slot's domain
* @return <code>true</code> if the initialization was successful
*/
public boolean initialize(Variable variable, EGraph graph) {
// Already initialized?
if (initialized) {
return true;
}
initialized = true;
owner = variable;
// If temporaryDomain is not null, there are BinaryConstraints restricting this slot's domain.
if (temporaryDomain != null) {
domain = new ArrayList<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);
}
return true;
}
/**
* Tries to set the domainSlot's value and lock the domainSlot.
* has no effect if the slot is already locked.
* @return <code>true</code> if the slot is now (or already was) locked,
* <code>false</code> if the slot could not be locked.
*/
public boolean setValueAndLock() {
// Slot already locked?
if (locked) {
return true;
}
if (domain.isEmpty()) {
return false;
}
if (inverseMatchingOrder) {
value = domain.remove(domain.size() - 1);
} else {
value = domain.remove(0);
}
usedObjects.add(value);
locked = true;
return true;
}
public boolean checkTypeConstraint(Variable variable) {
return variable.typeConstraint.check(this);
}
public boolean checkDanglingConstraint(DanglingConstraint constraint, EGraph graph) {
return (constraint.postpone || constraint.check(value, graph));
}
public boolean checkAttributeConstraint(AttributeConstraint constraint, UnaryConstraint unaryUserConstraint) {
if (!constraint.isConstantValue) {
if (!conditionHandler.isSet((String) constraint.value)) {
initializedParameters.add((String) constraint.value);
}
}
if (!constraint.check(this)) {
return false;
}
if (unaryUserConstraint != null){
if (!unaryUserConstraint.check(this)){
return false;
}
}
return true;
}
public boolean checkContainmentConstraint(ContainmentConstraint constraint, Map<Variable, DomainSlot> domainMap) {
DomainSlot targetSlot = domainMap.get(constraint.targetVariable);
return constraint.check(this, targetSlot);
}
public boolean checkReferenceConstraint(ReferenceConstraint constraint,
BinaryConstraint binaryUserConstraint, Map<Variable, DomainSlot> domainMap) {
DomainSlot targetSlot = domainMap.get(constraint.targetVariable);
if (!constraint.check(this, targetSlot)) {
return false;
}
if (binaryUserConstraint != null){
if (!binaryUserConstraint.check(this, targetSlot)) {
return false;
}
}
return true;
}
public boolean checkPathConstraint(PathConstraint constraint, Map<Variable, DomainSlot> domainMap) {
DomainSlot targetSlot = domainMap.get(constraint.targetVariable);
return constraint.check(this, targetSlot);
}
public boolean checkUserConstraint(UnaryConstraint constraint) {
return constraint.check(this);
}
/**
* @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;
}
public ConditionHandler getConditionHandler() {
return conditionHandler;
}
}