| /******************************************************************************* |
| * 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); |
| } |
| } |
| } |
| } |
| |
| } |