/*******************************************************************************
* 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.incquery.runtime.rete.matcher;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.incquery.runtime.rete.boundary.ReteBoundary;
import org.eclipse.incquery.runtime.rete.collections.CollectionsFactory;
import org.eclipse.incquery.runtime.rete.index.Indexer;
import org.eclipse.incquery.runtime.rete.network.Production;
import org.eclipse.incquery.runtime.rete.network.Receiver;
import org.eclipse.incquery.runtime.rete.remote.Address;
import org.eclipse.incquery.runtime.rete.single.TransformerNode;
import org.eclipse.incquery.runtime.rete.tuple.FlatTuple;
import org.eclipse.incquery.runtime.rete.tuple.Tuple;
import org.eclipse.incquery.runtime.rete.tuple.TupleMask;
/**
* @author Gabor Bergmann
*
*/
public class RetePatternMatcher extends TransformerNode {
protected ReteEngine<?> engine;
protected ReteBoundary<?> boundary;
protected Production productionNode;
protected Map<Object, Integer> posMapping;
protected Map<Object, Receiver> taggedChildren = //new HashMap<Object, Receiver>();
CollectionsFactory.getMap();
protected boolean connected = false; // is rete-wise connected to the
// production node?
/**
* @param productionNode
* a production node that matches this pattern without any parameter bindings
* @pre: Production must be local to the head container
*/
public RetePatternMatcher(ReteEngine<?> engine, Address<? extends Production> productionNode) {
super(engine.getReteNet().getHeadContainer());
this.engine = engine;
this.boundary = engine.getBoundary();
if (!engine.getReteNet().getHeadContainer().isLocal(productionNode))
throw new IllegalArgumentException("@pre: Production must be local to the head container");
this.productionNode = engine.getReteNet().getHeadContainer().resolveLocal(productionNode);
this.posMapping = this.productionNode.getPosMapping();
}
// /**
// * @return the productionNode
// */
// public Production getProductionNode() {
// return productionNode;
// }
public Tuple matchOneRandomly(Object[] inputMapping, boolean[] fixed) {
ArrayList<Tuple> allMatches = matchAll(inputMapping, fixed);
if (allMatches == null || allMatches.isEmpty())
return null;
else
return allMatches.get((int) (Math.random() * allMatches.size()));
}
public ArrayList<Tuple> matchAll(Object[] inputMapping, boolean[] fixed) {
// retrieving the projection
TupleMask mask = new TupleMask(fixed);
Tuple inputSignature = mask.transform(new FlatTuple(inputMapping));
AllMatchFetcher fetcher = new AllMatchFetcher(engine.accessProjection(productionNode, mask),
boundary.wrapTuple(inputSignature));
engine.reteNet.waitForReteTermination(fetcher);
ArrayList<Tuple> unscopedMatches = fetcher.getMatches();
// checking scopes
if (unscopedMatches == null)
return new ArrayList<Tuple>();
else
return unscopedMatches;
}
public Tuple matchOne(Object[] inputMapping, boolean[] fixed) {
// retrieving the projection
TupleMask mask = new TupleMask(fixed);
Tuple inputSignature = mask.transform(new FlatTuple(inputMapping));
SingleMatchFetcher fetcher = new SingleMatchFetcher(engine.accessProjection(productionNode, mask),
boundary.wrapTuple(inputSignature));
engine.reteNet.waitForReteTermination(fetcher);
return fetcher.getMatch();
}
/**
* Counts the number of occurrences of the pattern that match inputMapping on positions where fixed is true.
*
* @return the number of occurrences
*/
public int count(Object[] inputMapping, boolean[] fixed) {
TupleMask mask = new TupleMask(fixed);
Tuple inputSignature = mask.transform(new FlatTuple(inputMapping));
CountFetcher fetcher = new CountFetcher(engine.accessProjection(productionNode, mask),
boundary.wrapTuple(inputSignature));
engine.reteNet.waitForReteTermination(fetcher);
return fetcher.getCount();
}
/**
* Connects a new external receiver that will receive update notifications from now on. The receiver will
* practically connect to the production node, the added value is unwrapping the updates for external use.
*
* @param synchronize
* if true, the contents of the production node will be inserted into the receiver after the connection
* is established.
*/
public synchronized void connect(Receiver receiver, boolean synchronize) {
if (!connected) { // connect to the production node as a RETE-child
reteContainer.connect(productionNode, this);
connected = true;
}
if (synchronize)
reteContainer.connectAndSynchronize(this, receiver);
else
reteContainer.connect(this, receiver);
}
/**
* Connects a new external receiver that will receive update notifications from now on. The receiver will
* practically connect to the production node, the added value is unwrapping the updates for external use.
*
* The external receiver will be disconnectable later based on its tag.
*
* @param tag
* an identifier to recognize the child node by.
*
* @param synchronize
* if true, the contents of the production node will be inserted into the receiver after the connection
* is established.
*
*/
public synchronized void connect(Receiver receiver, Object tag, boolean synchronize) {
taggedChildren.put(tag, receiver);
connect(receiver, synchronize);
}
/**
* Disconnects a child node.
*/
public synchronized void disconnect(Receiver receiver) {
reteContainer.disconnect(this, receiver);
}
/**
* Disconnects the child node that was connected by specifying the given tag.
*
* @return if a child node was found registered with this tag.
*/
public synchronized boolean disconnectByTag(Object tag) {
final Receiver receiver = taggedChildren.remove(tag);
final boolean found = receiver != null;
if (found)
disconnect(receiver);
return found;
}
/**
* @return the posMapping
*/
public Map<Object, Integer> getPosMapping() {
return posMapping;
}
@Override
protected Tuple transform(Tuple input) {
return boundary.unwrapTuple(input);
}
abstract class AbstractMatchFetcher implements Runnable {
Indexer indexer;
Tuple signature;
public AbstractMatchFetcher(Indexer indexer, Tuple signature) {
super();
this.indexer = indexer;
this.signature = signature;
}
public void run() {
fetch(indexer.get(signature));
}
protected abstract void fetch(Collection<Tuple> matches);
}
class AllMatchFetcher extends AbstractMatchFetcher {
public AllMatchFetcher(Indexer indexer, Tuple signature) {
super(indexer, signature);
}
ArrayList<Tuple> matches = null;
public ArrayList<Tuple> getMatches() {
return matches;
}
@Override
protected void fetch(Collection<Tuple> matches) {
if (matches == null)
this.matches = null;
else {
this.matches = new ArrayList<Tuple>(matches.size());
int i = 0;
for (Tuple t : matches)
this.matches.add(i++, boundary.unwrapTuple(t));
}
}
}
class SingleMatchFetcher extends AbstractMatchFetcher {
public SingleMatchFetcher(Indexer indexer, Tuple signature) {
super(indexer, signature);
}
Tuple match = null;
public Tuple getMatch() {
return match;
}
@Override
protected void fetch(Collection<Tuple> matches) {
if (matches != null && !matches.isEmpty())
match = boundary.unwrapTuple(matches.iterator().next());
}
// public void run() {
// Collection<Tuple> unscopedMatches = indexer.get(signature);
//
// // checking scopes
// if (unscopedMatches != null) {
// for (Tuple um : /* productionNode */unscopedMatches) {
// match = boundary.unwrapTuple(um);
// return;
//
// // Tuple ps = boundary.unwrapTuple(um);
// // boolean ok = true;
// // if (!ignoreScope) for (int k = 0; (k < ps.getSize()) && ok; k++) {
// // if (pcs[k].getParameterMode() == ParameterMode.INPUT) {
// // // ok = ok && (inputMapping[k]==ps.elements[k]);
// // // should now be true
// // } else // ParameterMode.OUTPUT
// // {
// // IEntity scopeParent = (IEntity) pcs[k].getParameterScope().getParent();
// // Integer containmentMode = pcs[k].getParameterScope().getContainmentMode();
// // if (containmentMode == Scope.BELOW)
// // ok = ok && ((IModelElement) ps.get(k)).isBelowNamespace(scopeParent);
// // else
// // /* case Scope.IN: */
// // ok = ok && scopeParent.equals(((IModelElement) ps.get(k)).getNamespace());
// // // note: getNamespace returns null instead of the
// // // (imaginary) modelspace root entity for top level
// // // elements;
// // // this is not a problem here as Scope.IN implies
// // // scopeParent != root.
// //
// // }
// // }
// //
// // if (ok) {
// // reteMatching = new ReteMatching(ps, posMapping);
// // return;
// // }
// }
// }
//
// }
}
class CountFetcher extends AbstractMatchFetcher {
public CountFetcher(Indexer indexer, Tuple signature) {
super(indexer, signature);
}
int count = 0;
public int getCount() {
return count;
}
@Override
protected void fetch(Collection<Tuple> matches) {
count = matches == null ? 0 : matches.size();
}
}
}