package org.eclipse.emf.henshin.interpreter.matching.constraints;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
/**
* Path constraint class.
* @author Christian Krause
*/
public class PathConstraint implements BinaryConstraint {
// Integers:
static final Integer[] INTEGERS = new Integer[64];
static {
for (int i=0; i<INTEGERS.length; i++) {
INTEGERS[i] = i;
}
}
// Target variable:
final Variable targetVariable;
// References list representing a path:
final List<EReference> references;
// Number of paths:
final int numPaths;
/**
* Default constructor.
* @param target Target variable.
* @param references List of reference used for the path.
* @param numPaths Number of paths.
*/
public PathConstraint(Variable target, List<EReference> references, int numPaths) {
this.targetVariable = target;
this.references = references;
this.numPaths = numPaths;
if (references.isEmpty()) {
throw new IllegalArgumentException("Cannot create path constraint for empty paths");
}
}
private static Integer inc(Integer integer) {
if (integer==null) return INTEGERS[1];
int succ = integer.intValue() + 1;
if (succ < INTEGERS.length) {
return INTEGERS[succ];
}
return succ;
}
/*
* Get the targets for a list o sources and a reference.
*/
@SuppressWarnings("unchecked")
private static Map<EObject,Integer> getTargetObjects(Map<EObject,Integer> sources, EReference reference) {
Map<EObject,Integer> targets = new LinkedHashMap<EObject,Integer>();
for (Entry<EObject,Integer> source : sources.entrySet()) {
EObject src = source.getKey();
if (src.eClass().getEAllReferences().contains(reference)) {
if (reference.isMany()) {
for (EObject trg : (List<EObject>) src.eGet(reference)) {
targets.put(trg, inc(targets.get(trg)));
}
} else {
EObject trg = (EObject) src.eGet(reference);
if (trg!=null) {
targets.put(trg, inc(targets.get(trg)));
}
}
}
}
return targets;
}
/*
* (non-Javadoc)
* @see org.eclipse.emf.henshin.interpreter.matching.constraints.BinaryConstraint#check(org.eclipse.emf.henshin.interpreter.matching.constraints.DomainSlot, org.eclipse.emf.henshin.interpreter.matching.constraints.DomainSlot)
*/
@Override
public boolean check(DomainSlot source, DomainSlot target) {
// Source variable must be locked:
if (!source.locked) {
return false;
}
// Follow all paths and get the target objects:
Map<EObject,Integer> targetObjects = new HashMap<EObject,Integer>();
targetObjects.put(source.value, INTEGERS[1]);
for (EReference reference : references) {
targetObjects = getTargetObjects(targetObjects, reference);
}
// Target domain slot locked?
if (target.locked) {
Integer numTargets = targetObjects.get(target.value);
return (numTargets != null && numTargets >= numPaths);
} else {
// Target not locked yet. Create a domain change to restrict the target domain:
DomainChange change = new DomainChange(target, target.temporaryDomain);
source.remoteChangeMap.put(this, change);
// Calculated temporary domain:
target.temporaryDomain = new ArrayList<EObject>();
for (Entry<EObject,Integer> entry : targetObjects.entrySet()) {
if (entry.getValue() >= numPaths) {
target.temporaryDomain.add(entry.getKey());
}
}
// TODO: why does domain restriction not work for path constraints?
//if (change.originalValues!=null) {
// target.temporaryDomain.retainAll(change.originalValues);
//}
// Temporary domain must not be empty:
return !target.temporaryDomain.isEmpty();
}
}
}