blob: 8ff2a917b7652cd6ac3fbe43df70673c56e71d90 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004-2008 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.viatra2.gtasm.patternmatcher.incremental.simple.legacy;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.adapters.GTASMBuildable;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.construction.RetePatternBuildException;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.construction.Stub;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.tuple.FlatTuple;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.tuple.Tuple;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.tuple.TupleMask;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.rete.util.Options;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.simple.PatternScaffold;
import org.eclipse.viatra2.gtasm.patternmatcher.incremental.simple.SimpleReteBuilder;
import org.eclipse.viatra2.gtasmmodel.gtasm.metamodel.asm.terms.Term;
import org.eclipse.viatra2.gtasmmodel.gtasm.metamodel.gt.GTPattern;
import org.eclipse.viatra2.gtasmmodel.gtasm.metamodel.gt.GTPatternBody;
import org.eclipse.viatra2.gtasmmodel.gtasm.metamodel.gt.PatternVariable;
public class BodyScaffold<StubHandle, Collector> {
protected SimpleReteBuilder<StubHandle, Collector> builder;
protected GTASMBuildable<StubHandle, Collector> buildable;
protected PatternScaffold<StubHandle, Collector> patternScaffold;
protected GTPatternBody body;
protected Collector collector;
protected PatternGraph pGraph;
protected Set<PatternNode> visitedNodes = new HashSet<PatternNode>();
protected Set<PatternEdge> visitedEdges = new HashSet<PatternEdge>();
protected Set<PatternEdge> edgeQueue = new TreeSet<PatternEdge>();
/**
* @param builder
* @param patternScaffold
* @param body
*/
public BodyScaffold(SimpleReteBuilder<StubHandle, Collector> builder,
GTASMBuildable<StubHandle, Collector> buildable,
PatternScaffold<StubHandle, Collector> patternScaffold, GTPatternBody body,
Collector collector) {
super();
this.builder = builder;
// TODO select container?
this.buildable = buildable;
this.patternScaffold = patternScaffold;
this.body = body;
this.collector = collector;
}
public void run() throws RetePatternBuildException {
builder.getContext().logDebug("patternbody build started");
pGraph = new PatternGraph(patternScaffold.getGtPattern(), body, builder);
// put all edges into the queue
for (PatternEdge edge : pGraph.pEdges)
edgeQueue.add(edge);
// extracting constants
int constants = pGraph.getConstantValues().size();
Object[] constantValues = new Object[constants];
PatternNode[] constantNames = new PatternNode[constants];
int currentConstant = 0;
for (Map.Entry<PatternNode, Object> entry : pGraph.getConstantValues()
.entrySet()) {
constantNames[currentConstant] = entry.getKey();
constantValues[currentConstant] = entry.getValue();
currentConstant++;
}
for (PatternNode constant : constantNames)
bindPNode(constant);
// stub initialization
Stub<StubHandle> stub = buildable.buildStartStub(constantValues, constantNames);
while (!edgeQueue.isEmpty()) {
PatternEdge edge = edgeQueue.iterator().next();
edgeQueue.remove(edge);
visitedEdges.add(edge);
// negative patterns that are not completely bound are not to be
// checked
if (edge.isUnsafeQuantification()) {
String[] args = {edge.originText, patternScaffold.getGtPattern().getName()};
String msg = "The incremental engine currently does not support unresolvable variables. " +
"Keep also in mind that certain constructs (e.g. negative patterns or check expressions) cannot output symbolic parameters - [{1}] in [{2}]";
throw new RetePatternBuildException(msg, args, patternScaffold.getGtPattern());
}
// check expressions are not to be checked before without matching the types of nodes
if (edge.isUnsafeTypeless()) {
String[] args = {edge.originText, patternScaffold.getGtPattern().toString()};
String msg = "[INTERNAL ERROR] Non-typesafe check operation attempted by incremental pattern matcher without matching types - [{1}] in [{2}]";
throw new RetePatternBuildException(msg, args, patternScaffold.getGtPattern());
}
// account for expressed type information on affected nodes
for (int index : edge.conveysTypeInformationIndices())
expressTypeOnPNode(edge.nodes.get(index));
if (edge.edgeClass.isJoinBased()) {
int oldCalibrationSize = stub.getVariablesTuple().getSize();
/* pNode correspondence */
int oldNodes = /* edge.boundNodes.size() */edge.degreeOfBindingWithMultiplicity;
int[] primaryIndices = new int[oldNodes];
final int[] sideIndices = new int[oldNodes];
int k = 0;
for (int i = 0; i < edge.nodes.size(); ++i) {
PatternNodeBase oldNode = edge.nodes.get(i);
if (edge.boundNodes.contains(oldNode)) {
primaryIndices[k] = stub.getVariablesIndex().get(oldNode);
sideIndices[k] = i;
k++;
}
}
int derivedValueIndex = (edge.edgeClass==HyperEdgeClass.COUNT_PATTERN_CALL) ? edge.nodes.size()-1 : -1;
/* side processing STARTS HERE */
Stub<StubHandle> sideStub = extractSideStub(edge);
/* complementer mask and equalities */
/*
* Generates a complementer mask that maps those elements that
* were NOT mapped by the original sideIndices mask.
*/
HashSet<Object> mapped = new HashSet<Object>();
LinkedList<Integer> unmapped = new LinkedList<Integer>();
Map<PatternNode, LinkedList<Integer>> occurences = new HashMap<PatternNode, LinkedList<Integer>>();
for (int index : sideIndices)
mapped.add(edge.nodes.get(index));
for (int index = 0; index < edge.nodes.size(); ++index) {
PatternNode newNode = edge.nodes.get(index);
if (newNode.isTouched && mapped.add(newNode))
unmapped.addLast(index); // for the complementer
// for the equality check
LinkedList<Integer> indices;
indices = occurences.get(newNode);
if (indices == null) {
indices = new LinkedList<Integer>();
occurences.put(newNode, indices);
}
indices.add(index); // reverse order
}
int[] complementerIndices = new int[unmapped.size()];
int l = 0;
for (Integer integer : unmapped)
complementerIndices[l++] = integer;
TupleMask complementer = new TupleMask(complementerIndices,
edge.nodes.size());
/* equality checker(s) */
/* asserts equalities among side pNodes */
for (Collection<Integer> toBeChecked : occurences.values()) {
if (toBeChecked.size() > 1 && toBeChecked.contains(derivedValueIndex)) {
toBeChecked.remove(derivedValueIndex);
// EQUALITY solved by the join
// int other = toBeChecked.iterator().next();
// // the result of the aggregation can only be equality-checked after the count-join
// int[] indices = {derivedValueIndex, other -- LOOKUP TODO};
// postponedEqualityCheckIndices = new int[2];
// for (int i=0; i<sideIndices.length; i++)
// if (sideIndices[i])
}
if (toBeChecked.size() > 1) // there really is an equality among a priori checkable nodes
{
int[] indices = new int[toBeChecked.size()];
int m = 0;
for (Integer index : toBeChecked)
indices[m++] = index;
sideStub = buildable.buildEqualityChecker(sideStub, indices);
}
}
/* side processing ENDS HERE */
/* the actual pEdge checker */
TupleMask primaryMask = new TupleMask(primaryIndices, stub.getVariablesTuple().getSize());
TupleMask effectiveSideMask = new TupleMask(sideIndices, edge.nodes.size());
if (edge.edgeClass!=HyperEdgeClass.COUNT_PATTERN_CALL)
stub = buildable.buildBetaNode(
stub,
sideStub,
primaryMask,
effectiveSideMask,
complementer,
(edge.edgeClass == HyperEdgeClass.NEGATIVE_PATTERN_CALL));
else {
//PatternNode aggregate = edge.nodes[derivedValueIndex];
Integer resultPositionInSignature = null;
for (int i=sideIndices.length-1; i>=0; i--)
if (sideIndices[i]==derivedValueIndex) {
resultPositionInSignature = i;
break;
}
TupleMask originalSideMask = (resultPositionInSignature==null) ? effectiveSideMask :
TupleMask.omit(resultPositionInSignature, effectiveSideMask.indices.length).transform(effectiveSideMask);
if (resultPositionInSignature!=null)
stub = buildable.buildCountCheckBetaNode(
stub,
sideStub,
primaryMask,
originalSideMask,
resultPositionInSignature);
else
stub = buildable.buildCounterBetaNode(
stub,
sideStub,
primaryMask,
originalSideMask,
complementer,
edge.nodes.get(edge.nodes.size()-1));
}
/* marking new nodes as bound */
Collection<PatternNode> newNodes = new LinkedList<PatternNode>(
edge.unboundNodes);
for (PatternNode currentNode : newNodes) // iterating a clone =>
// no concurrency!
{
// account for binding of node variable
bindPNode(currentNode);
}
/* eager injectivity check */
if (Options.injectivityStrategy == Options.InjectivityStrategy.EAGER)
stub = constructInjectivityCheckers(stub, oldCalibrationSize);
} else // pEdge is not join-based, ie. CHECK_EXPRESSION
{
Map<String, Integer> variableIndices = new HashMap<String, Integer>();
for (PatternNodeBase pNode : edge.nodes) {
Integer index = stub.getVariablesIndex().get(pNode);
variableIndices.put(pNode.name, index);
}
Integer rhsIndex = (edge.extra == null) ? null : stub.getVariablesIndex().get(edge.extra);
stub = buildable.buildGTASMTermChecker((Term)edge.supplierKey,
variableIndices, pGraph.variableEquivalence, rhsIndex, stub);
}
/* postponed equality check */
}
/* lazy injectivity check */
if (Options.injectivityStrategy == Options.InjectivityStrategy.LAZY)
stub = constructInjectivityCheckers(stub, 0);
// last check
for (PatternNodeBase node : pGraph.pNodeByName.values())
if (!visitedNodes.contains(node) && node.isTouched) {
String[] args = {node.name, patternScaffold.getGtPattern().getName()};
String msg = "Pattern Graph Search terminated incompletely, "
+ "pattern variable {1} in pattern {2} could not be reached";
throw new RetePatternBuildException(msg, args, patternScaffold.getGtPattern());
}
// output
int paramNum = patternScaffold.getGtPattern().getSymParameters().size();
int[] tI = new int[paramNum];
int tiW = stub.getVariablesTuple().getSize();
for (int i = 0; i < paramNum; i++) {
PatternVariable variable = patternScaffold.getGtPattern().getSymParameters().get(i);
// for (Object o : variable.getElementInPattern()) // in all bodies
// {
PatternNodeBase pNode = pGraph.getPNode(variable);
// if (stub.calibrationIndex.containsKey(pNode))
tI[i] = stub.getVariablesIndex().get(pNode);
// }
}
TupleMask trim = new TupleMask(tI, tiW);
Stub<StubHandle> trimmer = buildable.buildTrimmer(stub, trim);
buildable.buildConnection(trimmer, collector);
}
protected Stub<StubHandle> constructInjectivityCheckers(Stub<StubHandle> stub,
int oldCalibrationSize) {
int newCalibrationSize = stub.getVariablesTuple().getSize();
Stub<StubHandle> accumulator = stub;
for (int subject = oldCalibrationSize; subject < newCalibrationSize; subject++) {
PatternNodeBase newNode = (PatternNodeBase) stub.getVariablesTuple()
.get(subject);
if (!newNode.isExemptFromInjectivity()) {
boolean injective = patternScaffold.getGtPattern().isDistinctMatching();
Set<PatternNode> explicit = pGraph.explicitInequality.get(newNode);
Set<PatternNode> toBeOmitted = pGraph.permittableEquality.get(newNode);
LinkedList<Integer> toBeChecked = new LinkedList<Integer>();
for (int j = 0; j < subject; j++) {
PatternNodeBase oldNode = (PatternNodeBase) stub.getVariablesTuple().get(j);
if (!oldNode.isVirtual() &&!toBeOmitted.contains(oldNode)
&& (injective || explicit.contains(oldNode))) toBeChecked.add(j);
}
if (!toBeChecked.isEmpty()) {
int[] indices = new int[toBeChecked.size()];
int l = 0;
for (Integer index : toBeChecked)
indices[l++] = index;
accumulator = buildable.buildInjectivityChecker(accumulator, subject, indices);
}
}
}
return accumulator;
}
protected Stub<StubHandle> extractSideStub(PatternEdge edge) throws RetePatternBuildException
{
Tuple nodes = new FlatTuple(edge.nodes.toArray());
Object supplierKey = edge.supplierKey;
switch (edge.edgeClass) {
case TYPE_UNARY:
return buildable.unaryTypeStub(nodes, supplierKey);
case TYPE_TERNARY_EDGE:
return buildable.ternaryEdgeTypeStub(nodes, supplierKey);
case TYPE_BINARY_EDGE:
return buildable.binaryEdgeTypeStub(nodes, supplierKey);
case CONTAINMENT_DIRECT:
return buildable.containmentDirectStub(nodes);
case CONTAINMENT_TRANSITIVE:
return buildable.containmentTransitiveStub(nodes);
case GENERALIZATION_DIRECT:
return buildable.generalizationDirectStub(nodes);
case GENERALIZATION_TRANSITIVE:
return buildable.generalizationTransitiveStub(nodes);
case INSTANTIATION_DIRECT:
return buildable.instantiationDirectStub(nodes);
case INSTANTIATION_TRANSITIVE:
return buildable.instantiationTransitiveStub(nodes);
case COUNT_PATTERN_CALL:
case NEGATIVE_PATTERN_CALL: // fall-through intentional
case PATTERN_CALL:
return buildable.patternCallStub(nodes, (GTPattern) supplierKey);
case CHECK_EXPRESSION:
String[] args = {edge.edgeClass.toString()}; // internal error
String msg = "Cannot build standard checker, no supplier for this edge class: {1}";
throw new RetePatternBuildException(msg, args, patternScaffold.getGtPattern());
default:
String[] args1 = {edge.edgeClass.toString()}; // internal error
String msg1 = "No such HyperEdgeClass: {1}";
throw new RetePatternBuildException(msg1, args1, patternScaffold.getGtPattern());
}
}
protected void bindPNode(PatternNode currentNode) {
if (!visitedNodes.contains(currentNode)) {
visitedNodes.add(currentNode);
for (PatternEdge e : pGraph.getEdgeList(currentNode)) {
edgeQueue.remove(e);
e.bindNode(currentNode);
if (!visitedEdges.contains(e)) {
edgeQueue.add(e);
}
}
}
}
protected void expressTypeOnPNode(PatternNodeBase currentNode) {
currentNode.lingeringTypeInfoCounter--;
for (PatternEdge e : pGraph.getEdgeList(currentNode)) {
if (e.isUnsafeTypeless()) {
edgeQueue.remove(e);
e.totalLingeringTypeInfoCounter--;
if (!visitedEdges.contains(e)) {
edgeQueue.add(e);
}
}
}
}
}