/*******************************************************************************
* 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.api.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.incquery.patternlanguage.helper.CorePatternLanguageHelper;
import org.eclipse.incquery.patternlanguage.patternLanguage.Pattern;
import org.eclipse.incquery.runtime.api.IMatchProcessor;
import org.eclipse.incquery.runtime.api.IMatchUpdateListener;
import org.eclipse.incquery.runtime.api.IPatternMatch;
import org.eclipse.incquery.runtime.api.IncQueryEngine;
import org.eclipse.incquery.runtime.api.IncQueryMatcher;
import org.eclipse.incquery.runtime.base.api.NavigationHelper;
import org.eclipse.incquery.runtime.exception.IncQueryException;
import org.eclipse.incquery.runtime.internal.boundary.CallbackNode;
import org.eclipse.incquery.runtime.rete.matcher.ReteEngine;
import org.eclipse.incquery.runtime.rete.matcher.RetePatternMatcher;
import org.eclipse.incquery.runtime.rete.misc.DeltaMonitor;
import org.eclipse.incquery.runtime.rete.tuple.Tuple;
/**
* Base implementation of IncQueryMatcher.
*
* @author Bergmann Gábor
*
* @param <Match>
*/
public abstract class BaseMatcher<Match extends IPatternMatch> implements IncQueryMatcher<Match> {
// FIELDS AND CONSTRUCTOR
protected IncQueryEngine engine;
protected RetePatternMatcher patternMatcher;
protected ReteEngine<Pattern> reteEngine;
protected NavigationHelper baseIndex;
public BaseMatcher(IncQueryEngine engine, RetePatternMatcher patternMatcher, Pattern pattern)
throws IncQueryException {
super();
this.engine = engine;
this.patternMatcher = patternMatcher;
this.reteEngine = engine.getReteEngine();
this.baseIndex = engine.getBaseIndex();
}
// HELPERS
/**
* Call this to sanitize the pattern before usage.
*
* @throws IncQueryException
* if the pattern has errors
*/
protected static void checkPattern(IncQueryEngine engine, Pattern pattern) throws IncQueryException {
final boolean admissible = engine.getSanitizer().admit(pattern);
if (!admissible)
throw new IncQueryException(
String.format(
"Could not initialize matcher for pattern %s because sanity check failed; see Error Log for details.",
CorePatternLanguageHelper.getFullyQualifiedName(pattern)), "Pattern contains errors");
}
protected abstract Match tupleToMatch(Tuple t);
private static Object[] fEmptyArray;
protected Object[] emptyArray() {
if (fEmptyArray == null)
fEmptyArray = new Object[getPattern().getParameters().size()];
return fEmptyArray;
}
private boolean[] notNull(Object[] parameters) {
boolean[] notNull = new boolean[parameters.length];
for (int i = 0; i < parameters.length; ++i)
notNull[i] = parameters[i] != null;
return notNull;
}
// REFLECTION
private Map<String, Integer> posMapping;
protected Map<String, Integer> getPosMapping() {
if (posMapping == null) {
posMapping = CorePatternLanguageHelper.getParameterPositionsByName(getPattern());
}
return posMapping;
}
@Override
public Integer getPositionOfParameter(String parameterName) {
return getPosMapping().get(parameterName);
}
private String[] parameterNames;
@Override
public String[] getParameterNames() {
if (parameterNames == null) {
Map<String, Integer> rawPosMapping = getPosMapping();
parameterNames = new String[rawPosMapping.size()];
for (Entry<String, Integer> entry : rawPosMapping.entrySet()) {
parameterNames[entry.getValue()] = entry.getKey();
}
}
return parameterNames;
}
// BASE IMPLEMENTATION
@Override
public Collection<Match> getAllMatches() {
return rawGetAllMatches(emptyArray());
}
@Override
public Collection<Match> rawGetAllMatches(Object[] parameters) {
ArrayList<Tuple> m = patternMatcher.matchAll(parameters, notNull(parameters));
ArrayList<Match> matches = new ArrayList<Match>();
// clones the tuples into a match object to protect the Tuples from modifications outside of the ReteMatcher
for (Tuple t : m)
matches.add(tupleToMatch(t));
return matches;
}
@Override
public Collection<Match> getAllMatches(Match partialMatch) {
return rawGetAllMatches(partialMatch.toArray());
}
// with input binding as pattern-specific parameters: not declared in interface
@Override
public Match getOneArbitraryMatch() {
return rawGetOneArbitraryMatch(emptyArray());
}
@Override
public Match rawGetOneArbitraryMatch(Object[] parameters) {
Tuple t = patternMatcher.matchOne(parameters, notNull(parameters));
if (t != null)
return tupleToMatch(t);
else
return null;
}
@Override
public Match getOneArbitraryMatch(Match partialMatch) {
return rawGetOneArbitraryMatch(partialMatch.toArray());
}
// with input binding as pattern-specific parameters: not declared in interface
@Override
public boolean rawHasMatch(Object[] parameters) {
return patternMatcher.count(parameters, notNull(parameters)) > 0;
}
@Override
public boolean hasMatch(Match partialMatch) {
return rawHasMatch(partialMatch.toArray());
}
// with input binding as pattern-specific parameters: not declared in interface
@Override
public int countMatches() {
return rawCountMatches(emptyArray());
}
@Override
public int rawCountMatches(Object[] parameters) {
return patternMatcher.count(parameters, notNull(parameters));
}
@Override
public int countMatches(Match partialMatch) {
return rawCountMatches(partialMatch.toArray());
}
// with input binding as pattern-specific parameters: not declared in interface
@Override
public void rawForEachMatch(Object[] parameters, IMatchProcessor<? super Match> processor) {
ArrayList<Tuple> m = patternMatcher.matchAll(parameters, notNull(parameters));
// clones the tuples into match objects to protect the Tuples from modifications outside of the ReteMatcher
for (Tuple t : m)
processor.process(tupleToMatch(t));
}
@Override
public void forEachMatch(IMatchProcessor<? super Match> processor) {
rawForEachMatch(emptyArray(), processor);
};
@Override
public void forEachMatch(Match match, IMatchProcessor<? super Match> processor) {
rawForEachMatch(match.toArray(), processor);
};
// with input binding as pattern-specific parameters: not declared in interface
@Override
public boolean forOneArbitraryMatch(IMatchProcessor<? super Match> processor) {
return rawForOneArbitraryMatch(emptyArray(), processor);
}
@Override
public boolean forOneArbitraryMatch(Match partialMatch, IMatchProcessor<? super Match> processor) {
return rawForOneArbitraryMatch(partialMatch.toArray(), processor);
};
@Override
public boolean rawForOneArbitraryMatch(Object[] parameters, IMatchProcessor<? super Match> processor) {
Tuple t = patternMatcher.matchOne(parameters, notNull(parameters));
if (t != null) {
processor.process(tupleToMatch(t));
return true;
} else {
return false;
}
}
// with input binding as pattern-specific parameters: not declared in interface
@Override
public void addCallbackOnMatchUpdate(IMatchUpdateListener<Match> listener, boolean fireNow) {
final CallbackNode<Match> callbackNode = new CallbackNode<Match>(reteEngine.getReteNet().getHeadContainer(),
engine, listener) {
@Override
public Match statelessConvert(Tuple t) {
return tupleToMatch(t);
}
};
patternMatcher.connect(callbackNode, listener, fireNow);
}
@Override
public void removeCallbackOnMatchUpdate(IMatchUpdateListener<Match> listener) {
patternMatcher.disconnectByTag(listener);
}
@Override
public DeltaMonitor<Match> newDeltaMonitor(boolean fillAtStart) {
DeltaMonitor<Match> dm = new DeltaMonitor<Match>(reteEngine.getReteNet().getHeadContainer()) {
@Override
public Match statelessConvert(Tuple t) {
return tupleToMatch(t);
}
};
patternMatcher.connect(dm, fillAtStart);
return dm;
}
@Override
public DeltaMonitor<Match> rawNewFilteredDeltaMonitor(boolean fillAtStart, final Object[] parameters) {
final int length = parameters.length;
DeltaMonitor<Match> dm = new DeltaMonitor<Match>(reteEngine.getReteNet().getHeadContainer()) {
@Override
public boolean statelessFilter(Tuple tuple) {
for (int i = 0; i < length; ++i) {
final Object positionalFilter = parameters[i];
if (positionalFilter != null && !positionalFilter.equals(tuple.get(i)))
return false;
}
return true;
}
@Override
public Match statelessConvert(Tuple t) {
return tupleToMatch(t);
}
};
patternMatcher.connect(dm, fillAtStart);
return dm;
}
@Override
public DeltaMonitor<Match> newFilteredDeltaMonitor(boolean fillAtStart, Match partialMatch) {
return rawNewFilteredDeltaMonitor(fillAtStart, partialMatch.toArray());
}
@Override
public boolean addCallbackAfterUpdates(Runnable callback) {
return baseIndex.getAfterUpdateCallbacks().add(callback);
}
@Override
public boolean removeCallbackAfterUpdates(Runnable callback) {
return baseIndex.getAfterUpdateCallbacks().remove(callback);
}
@Override
public boolean addCallbackAfterWipes(Runnable callback) {
return engine.getAfterWipeCallbacks().add(callback);
}
@Override
public boolean removeCallbackAfterWipes(Runnable callback) {
return engine.getAfterWipeCallbacks().remove(callback);
}
@Override
public Object[] matchToArray(Match partialMatch) {
return partialMatch.toArray();
}
@Override
public Match newEmptyMatch() {
return arrayToMatch(new Object[getParameterNames().length]);
}
@Override
public Match newMatch(Object... parameters) {
return arrayToMatch(parameters);
}
@Override
public Set<Object> getAllValues(final String parameterName) {
return rawGetAllValues(getPositionOfParameter(parameterName), emptyArray());
}
@Override
public Set<Object> getAllValues(final String parameterName, Match partialMatch) {
return rawGetAllValues(getPositionOfParameter(parameterName), partialMatch.toArray());
};
@Override
public Set<Object> rawGetAllValues(final int position, Object[] parameters) {
if (position >= 0 && position < getParameterNames().length) {
if (parameters.length == getParameterNames().length) {
if (parameters[position] == null) {
final Set<Object> results = new HashSet<Object>();
rawAccumulateAllValues(position, parameters, results);
return results;
}
}
}
return null;
}
/**
* Uses an existing set to accumulate all values of the parameter with the given name. Since it is a protected
* method, no error checking or input validation is performed!
*
* @param position
* position of the parameter for which values are returned
* @param parameters
* a parameter array corresponding to a partial match of the pattern where each non-null field binds the
* corresponding pattern parameter to a fixed value.
* @param accumulator
* the existing set to fill with the values
*/
protected <T> void rawAccumulateAllValues(final int position, Object[] parameters, final Set<T> accumulator) {
rawForEachMatch(parameters, new IMatchProcessor<Match>() {
@SuppressWarnings("unchecked")
@Override
public void process(Match match) {
accumulator.add((T) match.get(position));
}
});
}
@Override
public IncQueryEngine getEngine() {
return engine;
}
}