/*******************************************************************************
* Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro
* 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:
* Gabor Bergmann - initial API and implementation
*******************************************************************************/
package org.eclipse.incquery.runtime.internal.matcherbuilder;
import java.util.List;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.incquery.patternlanguage.emf.eMFPatternLanguage.ClassType;
import org.eclipse.incquery.patternlanguage.emf.eMFPatternLanguage.EClassifierConstraint;
import org.eclipse.incquery.patternlanguage.emf.eMFPatternLanguage.EnumValue;
import org.eclipse.incquery.patternlanguage.emf.eMFPatternLanguage.ReferenceType;
import org.eclipse.incquery.patternlanguage.helper.CorePatternLanguageHelper;
import org.eclipse.incquery.patternlanguage.patternLanguage.AggregatedValue;
import org.eclipse.incquery.patternlanguage.patternLanguage.AggregatorExpression;
import org.eclipse.incquery.patternlanguage.patternLanguage.BoolValue;
import org.eclipse.incquery.patternlanguage.patternLanguage.CheckConstraint;
import org.eclipse.incquery.patternlanguage.patternLanguage.CompareConstraint;
import org.eclipse.incquery.patternlanguage.patternLanguage.Constraint;
import org.eclipse.incquery.patternlanguage.patternLanguage.CountAggregator;
import org.eclipse.incquery.patternlanguage.patternLanguage.DoubleValue;
import org.eclipse.incquery.patternlanguage.patternLanguage.IntValue;
import org.eclipse.incquery.patternlanguage.patternLanguage.ParameterRef;
import org.eclipse.incquery.patternlanguage.patternLanguage.PathExpressionConstraint;
import org.eclipse.incquery.patternlanguage.patternLanguage.PathExpressionHead;
import org.eclipse.incquery.patternlanguage.patternLanguage.PathExpressionTail;
import org.eclipse.incquery.patternlanguage.patternLanguage.Pattern;
import org.eclipse.incquery.patternlanguage.patternLanguage.PatternBody;
import org.eclipse.incquery.patternlanguage.patternLanguage.PatternCall;
import org.eclipse.incquery.patternlanguage.patternLanguage.PatternCompositionConstraint;
import org.eclipse.incquery.patternlanguage.patternLanguage.StringValue;
import org.eclipse.incquery.patternlanguage.patternLanguage.Type;
import org.eclipse.incquery.patternlanguage.patternLanguage.ValueReference;
import org.eclipse.incquery.patternlanguage.patternLanguage.Variable;
import org.eclipse.incquery.patternlanguage.patternLanguage.VariableReference;
import org.eclipse.incquery.patternlanguage.patternLanguage.VariableValue;
import org.eclipse.incquery.runtime.rete.construction.Buildable;
import org.eclipse.incquery.runtime.rete.construction.RetePatternBuildException;
import org.eclipse.incquery.runtime.rete.construction.psystem.PSystem;
import org.eclipse.incquery.runtime.rete.construction.psystem.PVariable;
import org.eclipse.incquery.runtime.rete.construction.psystem.basicdeferred.Equality;
import org.eclipse.incquery.runtime.rete.construction.psystem.basicdeferred.ExportedParameter;
import org.eclipse.incquery.runtime.rete.construction.psystem.basicdeferred.Inequality;
import org.eclipse.incquery.runtime.rete.construction.psystem.basicdeferred.NegativePatternCall;
import org.eclipse.incquery.runtime.rete.construction.psystem.basicdeferred.PatternMatchCounter;
import org.eclipse.incquery.runtime.rete.construction.psystem.basicenumerables.BinaryTransitiveClosure;
import org.eclipse.incquery.runtime.rete.construction.psystem.basicenumerables.PositivePatternCall;
import org.eclipse.incquery.runtime.rete.construction.psystem.basicenumerables.TypeBinary;
import org.eclipse.incquery.runtime.rete.construction.psystem.basicenumerables.TypeTernary;
import org.eclipse.incquery.runtime.rete.construction.psystem.basicenumerables.TypeUnary;
import org.eclipse.incquery.runtime.rete.matcher.IPatternMatcherContext;
import org.eclipse.incquery.runtime.rete.matcher.IPatternMatcherContext.EdgeInterpretation;
import org.eclipse.incquery.runtime.rete.tuple.FlatTuple;
import org.eclipse.incquery.runtime.rete.tuple.Tuple;
import org.eclipse.xtext.xbase.XExpression;
/**
* @author Bergmann Gábor
*
*/
public class EPMBodyToPSystem<StubHandle, Collector> {
protected Pattern pattern;
protected PatternBody body;
protected IPatternMatcherContext<Pattern> context;
protected Buildable<Pattern, StubHandle, Collector> buildable;
protected PSystem<Pattern, StubHandle, Collector> pSystem;
String patternFQN;
/**
* @param pattern
* @param body
* @param builder
* @param buildable
*/
public EPMBodyToPSystem(Pattern pattern, PatternBody body, IPatternMatcherContext<Pattern> context,
Buildable<Pattern, StubHandle, Collector> buildable) {
super();
this.pattern = pattern;
this.body = body;
this.context = context;
this.buildable = buildable;
patternFQN = CorePatternLanguageHelper.getFullyQualifiedName(pattern);
}
public PSystem<Pattern, StubHandle, Collector> toPSystem() throws RetePatternBuildException {
try {
if (this.pSystem == null) {
this.pSystem = new PSystem<Pattern, StubHandle, Collector>(context, buildable, pattern);
// TODO
// preProcessAssignments();
preProcessParameters();
gatherBodyConstraints();
}
return pSystem;
} catch (RetePatternBuildException e) {
e.setPatternDescription(pattern);
throw e;
}
}
public PVariable[] symbolicParameterArray() throws RetePatternBuildException {
toPSystem();
EList<Variable> symParameters = pattern.getParameters();
int arity = symParameters.size();
PVariable[] result = new PVariable[arity];
for (int i = 0; i < arity; ++i)
result[i] = getPNode(symParameters.get(i));
return result;
}
// protected PVariable getPNode(String name) {
// return pSystem.getOrCreateVariableByName(name);
// }
protected PVariable getPNode(Variable variable) {
if (variable instanceof ParameterRef) // handle referenced parameter variables
return getPNode(((ParameterRef) variable).getReferredParam()); // assumed to be non-null
else
return pSystem.getOrCreateVariableByName(variable);
}
protected PVariable getPNode(VariableReference variable) {
// Warning! variable.getVar() does not differentiate between
// multiple anonymous variables ('_')
return getPNode(variable.getVariable());
}
protected Tuple getPNodeTuple(List<? extends ValueReference> variables) throws RetePatternBuildException {
PVariable[] pNodeArray = getPNodeArray(variables);
return new FlatTuple(pNodeArray);
}
public PVariable[] getPNodeArray(List<? extends ValueReference> variables) throws RetePatternBuildException {
int k = 0;
PVariable[] pNodeArray = new PVariable[variables.size()];
for (ValueReference varRef : variables) {
pNodeArray[k++] = getPNode(varRef);
}
return pNodeArray;
}
protected PVariable getPNode(ValueReference reference) throws RetePatternBuildException {
if (reference instanceof VariableValue)
return getPNode(((VariableValue) reference).getValue());
else if (reference instanceof AggregatedValue)
return aggregate((AggregatedValue) reference);
else if (reference instanceof IntValue)
return pSystem.newConstantVariable(((IntValue) reference).getValue());
else if (reference instanceof StringValue)
return pSystem.newConstantVariable(((StringValue) reference).getValue());
else if (reference instanceof EnumValue) // EMF-specific
return pSystem.newConstantVariable(((EnumValue) reference).getLiteral().getInstance());
else if (reference instanceof DoubleValue) {
return pSystem.newConstantVariable(((DoubleValue) reference).getValue());
} else if (reference instanceof BoolValue) {
Boolean b = ((BoolValue) reference).isValue();
return pSystem.newConstantVariable(b);
} else
throw new RetePatternBuildException(
"Unsupported value reference of type {1} from EPackage {2} currently unsupported by pattern builder in pattern {3}.",
new String[] { reference != null ? reference.eClass().getName() : "(null)",
reference != null ? reference.eClass().getEPackage().getNsURI() : "(null)",
pattern.getName() }, "Unsupported value expression", pattern);
}
protected PVariable newVirtual() {
return pSystem.newVirtualVariable();
}
// protected Tuple getPNodeTuple(List<? extends ValueReference> references) throws RetePatternBuildException {
// PVariable[] pNodeArray = getPNodeArray(references);
// return new FlatTuple(pNodeArray);
// }
// public PVariable[] getPNodeArray(List<? extends ValueReference> references) throws RetePatternBuildException {
// int k = 0;
// PVariable[] pNodeArray = new PVariable[references.size()];
// for (ValueReference varRef : references) {
// pNodeArray[k++] = getPNode(varRef);
// }
// return pNodeArray;
// }
private void preProcessParameters() {
EList<Variable> parameters = pattern.getParameters();
for (Variable variable : parameters) {
new ExportedParameter<Pattern, StubHandle>(pSystem, getPNode(variable), variable.getName());
if (variable.getType() != null && variable.getType() instanceof ClassType) {
EClassifier classname = ((ClassType) variable.getType()).getClassname();
PVariable pNode = getPNode(variable);
new TypeUnary<Pattern, StubHandle>(pSystem, pNode, classname);
}
}
// final EList<Variable> bodyVariables = body.getVariables();
// for (Variable bodyVariable : bodyVariables) {
// if (bodyVariable instanceof ParameterRef) {
// final Variable referredParam = ((ParameterRef) bodyVariable).getReferredParam();
// new Equality<Pattern, StubHandle>(pSystem,
// getPNode(referredParam), getPNode(bodyVariable));
// }
// }
}
private void gatherBodyConstraints() throws RetePatternBuildException {
EList<Constraint> constraints = body.getConstraints();
for (Constraint constraint : constraints) {
gatherConstraint(constraint);
}
}
/**
* @param constraint
* @throws RetePatternBuildException
*/
protected void gatherConstraint(Constraint constraint) throws RetePatternBuildException {
if (constraint instanceof EClassifierConstraint) { // EMF-specific
EClassifierConstraint constraint2 = (EClassifierConstraint) constraint;
gatherClassifierConstraint(constraint2);
} else if (constraint instanceof PatternCompositionConstraint) {
PatternCompositionConstraint constraint2 = (PatternCompositionConstraint) constraint;
gatherCompositionConstraint(constraint2);
} else if (constraint instanceof CompareConstraint) {
CompareConstraint compare = (CompareConstraint) constraint;
gatherCompareConstraint(compare);
} else if (constraint instanceof PathExpressionConstraint) {
// TODO advanced features here?
PathExpressionConstraint pathExpression = (PathExpressionConstraint) constraint;
gatherPathExpression(pathExpression);
} else if (constraint instanceof CheckConstraint) {
final CheckConstraint check = (CheckConstraint) constraint;
gatherCheckConstraint(check);
// TODO OTHER CONSTRAINT TYPES
} else {
throw new RetePatternBuildException("Unsupported constraint type {1} in pattern {2}.", new String[] {
constraint.eClass().getName(), patternFQN }, "Unsupported constraint type", pattern);
}
}
/**
* @param check
*/
protected void gatherCheckConstraint(final CheckConstraint check) {
XExpression expression = check.getExpression();
new XBaseCheck<StubHandle>(this, expression, pattern);
}
/**
* @param pathExpression
* @throws RetePatternBuildException
*/
protected void gatherPathExpression(PathExpressionConstraint pathExpression) throws RetePatternBuildException {
PathExpressionHead head = pathExpression.getHead();
PVariable currentSrc = getPNode(head.getSrc());
PVariable finalDst = getPNode(head.getDst());
PathExpressionTail currentTail = head.getTail();
// type constraint on source
Type headType = head.getType();
if (headType instanceof ClassType) {
EClassifier headClassname = ((ClassType) headType).getClassname();
new TypeUnary<Pattern, StubHandle>(pSystem, currentSrc, headClassname);
} else {
throw new RetePatternBuildException("Unsupported path expression head type {1} in pattern {2}: {3}",
new String[] { headType.eClass().getName(), patternFQN, typeStr(headType) },
"Unsupported navigation source", pattern);
}
// process each segment
while (currentTail != null) {
Type currentPathSegmentType = currentTail.getType();
currentTail = currentTail.getTail();
PVariable intermediate = newVirtual();
gatherPathSegment(currentPathSegmentType, currentSrc, intermediate);
currentSrc = intermediate;
}
// link the final step to the overall destination
new Equality<Pattern, StubHandle>(pSystem, currentSrc, finalDst);
}
/**
* @param compare
* @throws RetePatternBuildException
*/
protected void gatherCompareConstraint(CompareConstraint compare) throws RetePatternBuildException {
PVariable left = getPNode(compare.getLeftOperand());
PVariable right = getPNode(compare.getRightOperand());
switch (compare.getFeature()) {
case EQUALITY:
new Equality<Pattern, StubHandle>(pSystem, left, right);
break;
case INEQUALITY:
new Inequality<Pattern, StubHandle>(pSystem, left, right, false);
}
}
/**
* @param constraint
* @throws RetePatternBuildException
*/
protected void gatherCompositionConstraint(PatternCompositionConstraint constraint)
throws RetePatternBuildException {
PatternCall call = constraint.getCall();
Pattern patternRef = call.getPatternRef();
Tuple pNodeTuple = getPNodeTuple(call.getParameters());
if (!call.isTransitive()) {
if (constraint.isNegative())
new NegativePatternCall<Pattern, StubHandle>(pSystem, pNodeTuple, patternRef);
else
new PositivePatternCall<Pattern, StubHandle>(pSystem, pNodeTuple, patternRef);
} else {
if (pNodeTuple.getSize() != 2)
throw new RetePatternBuildException(
"Transitive closure of {1} in pattern {2} is unsupported because called pattern is not binary.",
new String[] { CorePatternLanguageHelper.getFullyQualifiedName(patternRef), patternFQN },
"Transitive closure only supported for binary patterns.", pattern);
else if (constraint.isNegative())
throw new RetePatternBuildException("Unsupported negated transitive closure of {1} in pattern {2}",
new String[] { CorePatternLanguageHelper.getFullyQualifiedName(patternRef), patternFQN },
"Unsupported negated transitive closure", pattern);
else
new BinaryTransitiveClosure<Pattern, StubHandle>(pSystem, pNodeTuple, patternRef);
// throw new RetePatternBuildException(
// "Unsupported positive transitive closure of {1} in pattern {2}",
// new String[]{CorePatternLanguageHelper.getFullyQualifiedName(patternRef), patternFQN},
// pattern);
}
}
/**
* @param constraint
*/
protected void gatherClassifierConstraint(EClassifierConstraint constraint) {
EClassifier classname = ((ClassType) constraint.getType()).getClassname();
PVariable pNode = getPNode(constraint.getVar());
new TypeUnary<Pattern, StubHandle>(pSystem, pNode, classname);
}
protected void gatherPathSegment(Type segmentType, PVariable src, PVariable trg) throws RetePatternBuildException {
if (segmentType instanceof ReferenceType) { // EMF-specific
EStructuralFeature typeObject = ((ReferenceType) segmentType).getRefname();
if (context.edgeInterpretation() == EdgeInterpretation.TERNARY) {
new TypeTernary<Pattern, StubHandle>(pSystem, context, newVirtual(), src, trg, typeObject);
} else {
new TypeBinary<Pattern, StubHandle>(pSystem, context, src, trg, typeObject);
}
} else
throw new RetePatternBuildException("Unsupported path segment type {1} in pattern {2}: {3}", new String[] {
segmentType.eClass().getName(), patternFQN, typeStr(segmentType) }, "Unsupported navigation step",
pattern);
}
protected PVariable aggregate(AggregatedValue reference) throws RetePatternBuildException {
PVariable result = newVirtual();
PatternCall call = reference.getCall();
Pattern patternRef = call.getPatternRef();
Tuple pNodeTuple = getPNodeTuple(call.getParameters());
AggregatorExpression aggregator = reference.getAggregator();
if (aggregator instanceof CountAggregator) {
new PatternMatchCounter<Pattern, StubHandle>(pSystem, pNodeTuple, patternRef, result);
} else
throw new RetePatternBuildException("Unsupported aggregator expression type {1} in pattern {2}.",
new String[] { aggregator.eClass().getName(), patternFQN }, "Unsupported aggregator expression",
pattern);
return result;
}
/**
* @return the string describing a metamodel type, for debug / exception purposes
*/
private String typeStr(Type type) {
return type.getTypename() == null ? "(null)" : type.getTypename();
}
}