Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "VIATRA/Query/UserDocumentation/API"

< VIATRA‎ | Query
(Overview)
Line 4: Line 4:
  
 
*http://incquery.net/incquery/new/examples/application (Headless example)  
 
*http://incquery.net/incquery/new/examples/application (Headless example)  
*http://incquery.net/incquery/documentation/api (Pattern Matcher API documentation)
+
*http://incquery.net/incquery/documentation/api (Pattern Matcher API documentation)  
 
*http://incquery.net/incquery/new/examples/school#Generated_code_overview (The latter part of the School example)
 
*http://incquery.net/incquery/new/examples/school#Generated_code_overview (The latter part of the School example)
  
Line 120: Line 120:
 
There are two ways you can use the EMF-IncQuery Pattern Matcher in your application. Either you can use the generic pattern matcher components, or the pattern-specific generated components. In most cases you won’t need the generic pattern matcher, which is a bit more complex to use. However they conform to the same reflective interfaces, and there is no performance difference between the two (a notable exception for this rule of thumb are '''check() expressions''', where the generated code that is invoked through the generated API will execute the Java code instead of interpreting Xbase). Here we will present a simple introduction to the basics of both APIs, to introduce features that help you to integrate it into your java application.  
 
There are two ways you can use the EMF-IncQuery Pattern Matcher in your application. Either you can use the generic pattern matcher components, or the pattern-specific generated components. In most cases you won’t need the generic pattern matcher, which is a bit more complex to use. However they conform to the same reflective interfaces, and there is no performance difference between the two (a notable exception for this rule of thumb are '''check() expressions''', where the generated code that is invoked through the generated API will execute the Java code instead of interpreting Xbase). Here we will present a simple introduction to the basics of both APIs, to introduce features that help you to integrate it into your java application.  
  
=== <span style="font-size: 15px; line-height: 1.5em;">Most important generated classes and their relationships &nbsp;</span> ===
+
=== Generic API  ===
 +
 
 +
The "generic" API differs from the generated one in two key aspects:
 +
 
 +
*it can be used to apply queries and use other IncQuery features '''without''' generating code and loading the resulting bundles into the running configuration. In other words, you just need to supply the EMF-based in-memory representation (an instance of the Pattern class)
 +
*the generic API is not "type safe" in the sense that the Java types of your pattern variables is not known and needs to be handled dynamically (e.g. by instanceof - typecase combos).
 +
 
 +
==== Match base interface  ====
 +
 
 +
<source lang="java">
 +
/**
 +
* Generic interface for a single match of a pattern. Each instance is a (partial) substitution of pattern parameters,
 +
* essentially a parameter to value mapping.
 +
*
 +
* Can also represent a partial match; unsubstituted parameters are assigned to null. Pattern matchers must never return
 +
* a partial match, but they accept partial matches as method parameters.
 +
*
 +
*/
 +
public interface IPatternMatch extends Cloneable /* , Map<String, Object> */{
 +
    /** @return the pattern for which this is a match. */
 +
    public Pattern pattern();
 +
    /** Identifies the name of the pattern for which this is a match. */
 +
    public String patternName();
 +
    /** Returns the list of symbolic parameter names. */
 +
    public String[] parameterNames();
 +
    /** Returns the value of the parameter with the given name, or null if name is invalid. */
 +
    public Object get(String parameterName);
 +
    /** Returns the value of the parameter at the given position, or null if position is invalid. */
 +
    public Object get(int position);
 +
    /** Sets the parameter with the given name to the given value.    */
 +
    public boolean set(String parameterName, Object newValue);
 +
    /** Sets the parameter at the given position to the given value.    */
 +
    public boolean set(int position, Object newValue);
 +
    /** Returns whether the match object can be further modified after its creation. Setters work only if the match is mutable.      */
 +
    public boolean isMutable();
 +
    /** Converts the match to an array representation, with each pattern parameter at their respective position.      */
 +
    public Object[] toArray();
 +
    /** Prints the list of parameter-value pairs. */
 +
    public String prettyPrint();
 +
}
 +
</source>
 +
 
 +
<br>
 +
 
 +
==== Matcher base interface  ====
 +
 
 +
<source lang="java">
 +
/**
 +
* Interface for an EMF-IncQuery matcher associated with a graph pattern.
 +
*
 +
* @param <Match>
 +
*            the IPatternMatch type representing a single match of this pattern.
 +
* @author Bergmann Gábor
 +
*/
 +
public interface IncQueryMatcher<Match extends IPatternMatch> {
 +
    // REFLECTION
 +
    /** The pattern that will be matched. */
 +
    public abstract Pattern getPattern();
 +
 
 +
    /** Fully qualified name of the pattern. */
 +
    public abstract String getPatternName();
 +
 
 +
    /** Returns the index of the symbolic parameter with the given name. */
 +
    public abstract Integer getPositionOfParameter(String parameterName);
 +
 
 +
    /** Returns the array of symbolic parameter names. */
 +
    public abstract String[] getParameterNames();
 +
 
 +
    // ALL MATCHES
 +
    /**
 +
    * Returns the set of all pattern matches.
 +
    *
 +
    * @return matches represented as a Match object.
 +
    */
 +
    public abstract Collection<Match> getAllMatches();
 +
 
 +
    /**
 +
    * Returns the set of all matches of the pattern that conform to the given fixed values of some parameters.
 +
    *
 +
    * @param partialMatch
 +
    *            a partial match of the pattern where each non-null field binds the corresponding pattern parameter to
 +
    *            a fixed value.
 +
    * @return matches represented as a Match object.
 +
    */
 +
    public abstract Collection<Match> getAllMatches(Match partialMatch);
 +
 
 +
    // variant(s) with input binding as pattern-specific parameters: not declared in interface
 +
 
 +
    // SINGLE MATCH
 +
    /**
 +
    * Returns an arbitrarily chosen pattern match. Neither determinism nor randomness of selection is guaranteed.
 +
    *
 +
    * @return a match represented as a Match object, or null if no match is found.
 +
    */
 +
    public abstract Match getOneArbitraryMatch();
 +
 
 +
    /**
 +
    * Returns an arbitrarily chosen match of the pattern that conforms to the given fixed values of some parameters.
 +
    * Neither determinism nor randomness of selection is guaranteed.
 +
    *
 +
    * @param partialMatch
 +
    *            a partial match of the pattern where each non-null field binds the corresponding pattern parameter to
 +
    *            a fixed value.
 +
    * @return a match represented as a Match object, or null if no match is found.
 +
    */
 +
    public abstract Match getOneArbitraryMatch(Match partialMatch);
 +
 
 +
    // variant(s) with input binding as pattern-specific parameters: not declared in interface
 +
 
 +
    // MATCH CHECKING
 +
    /**
 +
    * Indicates whether the given combination of specified pattern parameters constitute a valid pattern match, under
 +
    * any possible substitution of the unspecified parameters (if any).
 +
    *
 +
    * @param partialMatch
 +
    *            a (partial) match of the pattern where each non-null field binds the corresponding pattern parameter
 +
    *            to a fixed value.
 +
    * @return true if the input is a valid (partial) match of the pattern.
 +
    */
 +
    public abstract boolean hasMatch(Match partialMatch);
 +
 
 +
    // variant(s) with input binding as pattern-specific parameters: not declared in interface
 +
 
 +
    // NUMBER OF MATCHES
 +
    /**
 +
    * Returns the number of all pattern matches.
 +
    *
 +
    * @return the number of pattern matches found.
 +
    */
 +
    public abstract int countMatches();
 +
 
 +
    /**
 +
    * Returns the number of all matches of the pattern that conform to the given fixed values of some parameters.
 +
    *
 +
    * @param partialMatch
 +
    *            a partial match of the pattern where each non-null field binds the corresponding pattern parameter to
 +
    *            a fixed value.
 +
    * @return the number of pattern matches found.
 +
    */
 +
    public abstract int countMatches(Match partialMatch);
 +
 
 +
    // variant(s) with input binding as pattern-specific parameters: not declared in interface
 +
 
 +
    // FOR EACH MATCH
 +
    /**
 +
    * Executes the given processor on each match of the pattern.
 +
    *
 +
    * @param action
 +
    *            the action that will process each pattern match.
 +
    */
 +
    public abstract void forEachMatch(IMatchProcessor<? super Match> processor);
 +
 
 +
    /**
 +
    * Executes the given processor on each match of the pattern that conforms to the given fixed values of some
 +
    * parameters.
 +
    *
 +
    * @param parameters
 +
    *            array where each non-null element binds the corresponding pattern parameter to a fixed value.
 +
    * @param processor
 +
    *            the action that will process each pattern match.
 +
    */
 +
    public abstract void forEachMatch(Match partialMatch, IMatchProcessor<? super Match> processor);
 +
 
 +
    // variant(s) with input binding as pattern-specific parameters: not declared in interface
 +
 
 +
    // FOR ONE ARBITRARY MATCH
 +
    /**
 +
    * Executes the given processor on an arbitrarily chosen match of the pattern. Neither determinism nor randomness of
 +
    * selection is guaranteed.
 +
    *
 +
    * @param processor
 +
    *            the action that will process the selected match.
 +
    * @return true if the pattern has at least one match, false if the processor was not invoked
 +
    */
 +
    public abstract boolean forOneArbitraryMatch(IMatchProcessor<? super Match> processor);
 +
 
 +
    /**
 +
    * Executes the given processor on an arbitrarily chosen match of the pattern that conforms to the given fixed
 +
    * values of some parameters. Neither determinism nor randomness of selection is guaranteed.
 +
    *
 +
    * @param parameters
 +
    *            array where each non-null element binds the corresponding pattern parameter to a fixed value.
 +
    * @param processor
 +
    *            the action that will process the selected match.
 +
    * @return true if the pattern has at least one match with the given parameter values, false if the processor was
 +
    *        not invoked
 +
    */
 +
    public abstract boolean forOneArbitraryMatch(Match partialMatch, IMatchProcessor<? super Match> processor);
 +
 
 +
    // variant(s) with input binding as pattern-specific parameters: not declared in interface
 +
 
 +
    // CHANGE MONITORING
 +
    // attach delta monitor for high-level change detection
 +
    /**
 +
    * Registers low-level callbacks for match appearance and disappearance on this pattern matcher.
 +
    *
 +
    * <p>
 +
    * This is a low-level callback that is invoked when the pattern matcher is not necessarily in a consistent state
 +
    * yet. Importantly, no model modification permitted during the callback. Most users should use the agenda and trigger engine instead. TODO reference
 +
    *
 +
    * <p>
 +
    * Performance note: expected to be much more efficient than polling at {@link #addCallbackAfterUpdates(Runnable)},
 +
    * but prone to "signal hazards", e.g. spurious match appearances that will disappear immediately afterwards.
 +
    *
 +
    * <p>
 +
    * The callback can be unregistered via {@link #removeCallbackOnMatchUpdate(IMatchUpdateListener)}.
 +
    *
 +
    * @param fireNow
 +
    *            if true, appearCallback will be immediately invoked on all current matches as a one-time effect. See
 +
    *            also {@link IncQueryMatcher#forEachMatch(IMatchProcessor)}.
 +
    * @param listener
 +
    *            the listener that will be notified of each new match that appears or disappears, starting from now.
 +
    */
 +
    public abstract void addCallbackOnMatchUpdate(IMatchUpdateListener<Match> listener, boolean fireNow);
 +
 
 +
    /**
 +
    * Unregisters a callback registered by {@link #addCallbackOnMatchUpdate(IMatchUpdateListener, boolean)}.
 +
    *
 +
    * @param listener
 +
    *            the listener that will no longer be notified.
 +
    */
 +
    public abstract void removeCallbackOnMatchUpdate(IMatchUpdateListener<Match> listener);
 +
 
 +
    /**
 +
    * Registers a new delta monitor on this pattern matcher. The DeltaMonitor can be used to track changes (delta) in
 +
    * the set of pattern matches from now on. It can also be reset to track changes from a later point in time, and
 +
    * changes can even be acknowledged on an individual basis. See {@link DeltaMonitor} for details.
 +
    *
 +
    * @param fillAtStart
 +
    *            if true, all current matches are reported as new match events; if false, the delta monitor starts
 +
    *            empty.
 +
    * @return the delta monitor.
 +
    */
 +
    public abstract DeltaMonitor<Match> newDeltaMonitor(boolean fillAtStart);
 +
 
 +
    /**
 +
    * Registers a new filtered delta monitor on this pattern matcher. The DeltaMonitor can be used to track changes
 +
    * (delta) in the set of filtered pattern matches from now on, considering those matches only that conform to the
 +
    * given fixed values of some parameters. It can also be reset to track changes from a later point in time, and
 +
    * changes can even be acknowledged on an individual basis. See {@link DeltaMonitor} for details.
 +
    *
 +
    * @param fillAtStart
 +
    *            if true, all current matches are reported as new match events; if false, the delta monitor starts
 +
    *            empty.
 +
    * @param partialMatch
 +
    *            a partial match of the pattern where each non-null field binds the corresponding pattern parameter to
 +
    *            a fixed value.
 +
    * @return the delta monitor.
 +
    */
 +
    public abstract DeltaMonitor<Match> newFilteredDeltaMonitor(boolean fillAtStart, Match partialMatch);
 +
 
 +
    /**
 +
    * Registers a callback that will be run each time EMF-IncQuery match sets are refreshed after a model update.
 +
    * Typically useful to check delta monitors. When the callback is issued, the pattern match sets are guaranteed to
 +
    * reflect the post-state after the update.
 +
    * <p>
 +
    * Callbacks are issued after each elementary change (i.e. possibly at incomplete transient states). This can have a
 +
    * negative effect on performance, therefore clients are advised to use it as a last resort only. Consider
 +
    * coarser-grained timing (e.g EMF Transaction pre/post-commit) instead, whenever available.
 +
    *
 +
    * @param callback
 +
    *            a Runnable to execute after each update.
 +
    * @return false if the callback was already registered.
 +
    */
 +
    public boolean addCallbackAfterUpdates(Runnable callback);
 +
 
 +
    /**
 +
    * Removes a previously registered callback. See addCallbackAfterUpdates().
 +
    *
 +
    * @param callback
 +
    *            the callback to remove.
 +
    * @return false if the callback was not registered.
 +
    */
 +
    public boolean removeCallbackAfterUpdates(Runnable callback);
 +
 
 +
    /**
 +
    * Registers a callback that will be run each time the EMF-IncQuery engine is wiped or disposed. Typically useful if
 +
    * delta monitors are used, especially of the {@link IncQueryEngine} is managed.
 +
    *
 +
    * <p>
 +
    * When the callback is issued, the wipe has already occurred and pattern matchers will continue to return stale
 +
    * results.
 +
    *
 +
    * @param callback
 +
    *            a Runnable to execute after each wipe.
 +
    * @return false if the callback was already registered.
 +
    */
 +
    public boolean addCallbackAfterWipes(Runnable callback);
 +
 
 +
    /**
 +
    * Removes a previously registered callback. See {@link #addCallbackAfterWipes()}.
 +
    *
 +
    * @param callback
 +
    *            the callback to remove.
 +
    * @return false if the callback was not registered.
 +
    */
 +
    public boolean removeCallbackAfterWipes(Runnable callback);
 +
 
 +
    /**
 +
    * Returns an empty, mutable Match for the matcher.
 +
    * Fields of the mutable match can be filled to create a partial match, usable as matcher input.
 +
    * This can be used to call the matcher with a partial match
 +
    *  even if the specific class of the matcher or the match is unknown.
 +
    *
 +
    * @return the empty match
 +
    */
 +
    public abstract Match newEmptyMatch();
 +
 
 +
    /**
 +
    * Returns a new (partial) Match object for the matcher.
 +
    * This can be used e.g. to call the matcher with a partial
 +
    * match.
 +
    *
 +
    * <p>The returned match will be immutable. Use {@link #newEmptyMatch()} to obtain a mutable match object.
 +
    *
 +
    * @param parameters
 +
    *            the fixed value of pattern parameters, or null if not bound.
 +
    * @return the (partial) match object.
 +
    */
 +
    public abstract Match newMatch(Object... parameters);
 +
 
 +
    /**
 +
    * Retrieve the set of values that occur in matches for the given parameterName.
 +
    *
 +
    * @param parameterName
 +
    *            name of the parameter for which values are returned
 +
    * @return the Set of all values for the given parameter, null if the parameter with the given name does not exists,
 +
    *        empty set if there are no matches
 +
    */
 +
    public abstract Set<Object> getAllValues(final String parameterName);
 +
 
 +
    /**
 +
    * Retrieve the set of values that occur in matches for the given parameterName, that conforms to the given fixed
 +
    * values of some parameters.
 +
    *
 +
    * @param parameterName
 +
    *            name of the parameter for which values are returned
 +
    * @param partialMatch
 +
    *            a partial match of the pattern where each non-null field binds the corresponding pattern parameter to
 +
    *            a fixed value.
 +
    * @return the Set of all values for the given parameter, null if the parameter with the given name does not exists
 +
    *        or if the parameter with the given name is set in partialMatch, empty set if there are no matches
 +
    */
 +
    public abstract Set<Object> getAllValues(final String parameterName, Match partialMatch);
 +
 
 +
    /**
 +
    * Returns the engine that the matcher uses.
 +
    *
 +
    * @return the engine
 +
    */
 +
    public abstract IncQueryEngine getEngine();
 +
}
 +
</source>
 +
 
 +
==== Matcher Factory base interface  ====
 +
 
 +
<source lang="java">
 +
/**
 +
* Interface for an IncQuery matcher factory. Each factory is associated with a pattern. Methods instantiate a matcher
 +
* of the pattern with various parameters.
 +
*
 +
* @author Bergmann Gábor
 +
*
 +
*/
 +
public interface IMatcherFactory<Matcher extends IncQueryMatcher<? extends IPatternMatch>> {
 +
 
 +
    /**
 +
    * @throws IncQueryException
 +
    *            if there was an error loading the pattern definition
 +
    * @returns the pattern for which matchers can be instantiated.
 +
    */
 +
    public Pattern getPattern();
 +
 
 +
    /**
 +
    * Identifies the pattern for which matchers can be instantiated.
 +
    */
 +
    public String getPatternFullyQualifiedName();
 +
 
 +
    /**
 +
    * Initializes the pattern matcher over a given EMF model root (recommended: Resource or ResourceSet). If a pattern
 +
    * matcher is already constructed with the same root, only a lightweight reference is created.
 +
    *
 +
    * <p>
 +
    * The scope of pattern matching will be the given EMF model root and below (see FAQ for more precise definition).
 +
    * <p>
 +
    * The match set will be incrementally refreshed upon updates from this scope.
 +
    *
 +
    * <p>
 +
    * The matcher will be created within the managed {@link IncQueryEngine} belonging to the EMF model root, so
 +
    * multiple matchers will reuse the same engine and benefit from increased performance and reduced memory footprint.
 +
    *
 +
    * @param emfRoot
 +
    *            the root of the EMF tree where the pattern matcher will operate. Recommended: Resource or ResourceSet.
 +
    * @throws IncQueryException
 +
    *            if an error occurs during pattern matcher creation
 +
    */
 +
    public Matcher getMatcher(Notifier emfRoot) throws IncQueryException;
 +
 
 +
    /**
 +
    * Initializes the pattern matcher within an existing {@link IncQueryEngine}. If the pattern matcher is already
 +
    * constructed in the engine, only a lightweight reference is created.
 +
    * <p>
 +
    * The match set will be incrementally refreshed upon updates.
 +
    *
 +
    * @param engine
 +
    *            the existing EMF-IncQuery engine in which this matcher will be created.
 +
    * @throws IncQueryException
 +
    *            if an error occurs during pattern matcher creation
 +
    */
 +
    public Matcher getMatcher(IncQueryEngine engine) throws IncQueryException;
 +
}
 +
</source>
 +
 
 +
==== Example  ====
 +
 
 +
<source lang="java">
 +
public String executeGeneric(String modelPath, String patternFQN) {
 +
StringBuilder results = new StringBuilder();
 +
Resource resource = loadModel(modelPath);
 +
if (resource != null) {
 +
  try {
 +
  // get all matches of the pattern
 +
  IncQueryMatcher matcher = MatcherFactoryRegistry.getMatcherFactory(patternFQN).getMatcher(resource);
 +
  Collection<IPatternMatch> matches = matcher.getAllMatches();
 +
  prettyPrintMatches(results, matches);
 +
  } catch (IncQueryException e) {
 +
  e.printStackTrace();
 +
  results.append(e.getMessage());
 +
  }
 +
} else {
 +
  results.append("Resource not found");
 +
}
 +
return results.toString();
 +
}
 +
</source>
 +
 
 +
=== Most important generated classes and their relationships  ===
  
 
For every pattern a Match, a Matcher, a MatcherFactory, a Processor and optionally several Evaluator classes are available through both the generic and generated APIs. Let’s look into what these classes are responsible for:&nbsp;  
 
For every pattern a Match, a Matcher, a MatcherFactory, a Processor and optionally several Evaluator classes are available through both the generic and generated APIs. Let’s look into what these classes are responsible for:&nbsp;  
Line 292: Line 728:
 
</source>  
 
</source>  
  
===== Tracking changes in match sets efficiently =====
+
===== Tracking changes in match sets efficiently =====
  
 
There are some usecases where you don’t want to follow every change of a pattern’s match, just gather them together and process them when you’re ready.  
 
There are some usecases where you don’t want to follow every change of a pattern’s match, just gather them together and process them when you’re ready.  
  
'''TODO''' the technique below are now deprecated:
+
'''TODO''' the technique below are now deprecated:  
  
 
*The '''DeltaMonitor''' can do this for you in a convenient way. It is a monitoring object that connects to the rete network as a receiver to reflect changes since an arbitrary state acknowledged by the client.&nbsp;If a new matching is found, it appears in the matchFoundEvents collection, and disappears when that particular matching cannot be found anymore. If the event of finding a match has been processed by the client, it can be removed manually. In this case, when a previously found matching is lost, the Tuple will appear in the matchLostEvents collection, and disappear upon finding the same matching again. "Matching lost" events can also be acknowledged by removing a Tuple from the collection. If the matching is found once again, it will return to matchFoundEvents.
 
*The '''DeltaMonitor''' can do this for you in a convenient way. It is a monitoring object that connects to the rete network as a receiver to reflect changes since an arbitrary state acknowledged by the client.&nbsp;If a new matching is found, it appears in the matchFoundEvents collection, and disappears when that particular matching cannot be found anymore. If the event of finding a match has been processed by the client, it can be removed manually. In this case, when a previously found matching is lost, the Tuple will appear in the matchLostEvents collection, and disappear upon finding the same matching again. "Matching lost" events can also be acknowledged by removing a Tuple from the collection. If the matching is found once again, it will return to matchFoundEvents.
Line 304: Line 740:
 
</source>  
 
</source>  
  
=== Generic API  ===
+
<br>
 
+
The "generic" API differs from the generated one in two key aspects:
+
 
+
*it can be used to apply queries and use other IncQuery features '''without''' generating code and loading the resulting bundles into the running configuration. In other words, you just need to supply the EMF-based in-memory representation (an instance of the Pattern class)
+
*the generic API is not "type safe" in the sense that the Java types of your pattern variables is not known and needs to be handled dynamically (e.g. by instanceof - typecase combos).
+
 
+
<source lang="java">
+
public String executeGeneric(String modelPath, String patternFQN) {
+
StringBuilder results = new StringBuilder();
+
Resource resource = loadModel(modelPath);
+
if (resource != null) {
+
  try {
+
  // get all matches of the pattern
+
  IncQueryMatcher matcher = MatcherFactoryRegistry.getMatcherFactory(patternFQN).getMatcher(resource);
+
  Collection<IPatternMatch> matches = matcher.getAllMatches();
+
  prettyPrintMatches(results, matches);
+
  } catch (IncQueryException e) {
+
  e.printStackTrace();
+
  results.append(e.getMessage());
+
  }
+
} else {
+
  results.append("Resource not found");
+
}
+
return results.toString();
+
}
+
</source>
+

Revision as of 04:33, 27 March 2013

Overview

This page presents the basics of EMF-IncQuery's Java API. It supersedes the following contents of the original documentation on IncQuery.net:

Javadoc

The most up-to-date Javadocs for the EMF-IncQuery API can be found online at http://eclipse.org/incquery/javadoc/

Headless Execution Example

Overview

EMF-IncQuery can be used without any graphical user interface (in a headless RCP configuration - note: for now, running outside of the RCP environment is not supported). In this example, we take an existing IncQuery project and based on it, we create a (headless) Eclipse Application that can be executed from a console (command prompt) to print the matches for an arbitrary input model file.
First, the IncQuery project headlessQueries.incquery can be downloaded from: https://github.com/ujhelyiz/EMF-IncQuery-Examples/tree/master/headless

The project contains simple patterns that match on Ecore models (i.e. .ecore files):

package headless;
 
import "http://www.eclipse.org/emf/2002/Ecore"
 
pattern eClassNames(C: EClass, N : EString)= {
	EClass.name(C,N);
}
 
pattern eObject(O) {
	EObject(O);
}

Pattern eObject() will match on any EMF model.

Using IncQuery in a Java application

The headlessQueries.incquery bundle can be embedded into any Eclipse application through IncQuery's Java API. The org.eclipse.incquery.application bundle project demonstrates such usage.

The project includes several class files:

  • GenericSimpleIncQueryApplication and PatternSpecificSimpleIncQueryApplication: by implementing the IApplication interface, these classes provide an RCP entrypoint that is also capable of handling command line parameters. It checks that the input model is provided using the -m <modelPath> switch, and the Generic variant is also able to accept the query name provided using the -p <patternFQN> switch and then invokes the pattern matcher (the PatternSpecific variant always applies the eObject query).
    • To execute the example (on Windows), call as follows (assuming the current folder contains eclipse.exe and the model can be found at c:/test/input.ecore): eclipse.exe -m c:/test/input.ecore - p headless.eClassNames
    • Pattern fully qualified names are the package fully qualified name of a pattern + "." + the local name of the pattern, e.g. headless.eClassNames or headless.eObject in the example above.
  • IncQueryHeadless: utility class with two public methods called executeGeneric() and executePatternsSpecific() that demonstrate the basic usage of IncQuery's Java API. Both will
    • first try to load the model found at modelPath into an EMF Resource, and if that was successful,
    • then create a matcher (based on a pattern definition) and
    • then retrieve the matches as a collection.

The actual code also includes some additional fragments to illustrate performance measurements (timed execution for the EMF loading, IncQuery initialization and matchset retrieval phases). Finally, the matches are printed using IPatternMatch.prettyPrint(). In the latter part of this document, this API is explained in detail.

RCP applications are registered through the org.eclipse.core.runtime.applications extension point. The plugin.xml file defines the extension.

<extension
         id="org.eclipse.incquery.application.app.generic"
         point="org.eclipse.core.runtime.applications">
      <application
            cardinality="singleton-global"
            thread="main"
            visible="true">
         <run
               class="org.eclipse.incquery.application.generic.GenericSimpleIncQueryApplication">
         </run>
      </application>
   </extension>
   <extension
         id="org.eclipse.incquery.application.app.patternspecific"
         point="org.eclipse.core.runtime.applications">
      <application
            cardinality="singleton-global"
            thread="main"
            visible="true">
         <run
               class="org.eclipse.incquery.application.patternspecific.PatternSpecificSimpleIncQueryApplication">
         </run>
      </application>
   </extension>

Finally, a product configuration is required in order to run this application as an Eclipse product, and to be able to export it into a standalone application that can be called from the console. Apart from adding the required plugins to the configuration, an org.eclipse.core.runtime.products extension is required as well (also found in plugin.xml):

 <extension
         id="incquery.generic"
         point="org.eclipse.core.runtime.products">
      <product
            application="org.eclipse.incquery.application.app.generic"
            name="Generic IncQuery Application">
         <property
               name="appName"
               value="Generic IncQuery Application">
         </property>
      </product>
   </extension>
   <extension
         id="incquery.specific"
         point="org.eclipse.core.runtime.products">
   	<product
            application="org.eclipse.incquery.application.app.patternspecific"
            name="PatternSpecific IncQuery Application">
         <property
               name="appName"
               value="PatternSpecific IncQuery Application">
         </property>
      </product>
	</extension>

If only the minimum required plugins are exported, the resulting eclipse folder is around 30 MB, which is quite small considering that an Eclipse Modeling distribution is around 300 MB.
Note that you may have to remove the platform-specific features that are for different platforms (e.g. Linux and MacOS X when using Windows).

For further help on RCP applications, we recommend to check out:

EMF-IncQuery Java API

There are two ways you can use the EMF-IncQuery Pattern Matcher in your application. Either you can use the generic pattern matcher components, or the pattern-specific generated components. In most cases you won’t need the generic pattern matcher, which is a bit more complex to use. However they conform to the same reflective interfaces, and there is no performance difference between the two (a notable exception for this rule of thumb are check() expressions, where the generated code that is invoked through the generated API will execute the Java code instead of interpreting Xbase). Here we will present a simple introduction to the basics of both APIs, to introduce features that help you to integrate it into your java application.

Generic API

The "generic" API differs from the generated one in two key aspects:

  • it can be used to apply queries and use other IncQuery features without generating code and loading the resulting bundles into the running configuration. In other words, you just need to supply the EMF-based in-memory representation (an instance of the Pattern class)
  • the generic API is not "type safe" in the sense that the Java types of your pattern variables is not known and needs to be handled dynamically (e.g. by instanceof - typecase combos).

Match base interface

/**
 * Generic interface for a single match of a pattern. Each instance is a (partial) substitution of pattern parameters,
 * essentially a parameter to value mapping.
 * 
 * Can also represent a partial match; unsubstituted parameters are assigned to null. Pattern matchers must never return
 * a partial match, but they accept partial matches as method parameters.
 * 
 */
public interface IPatternMatch extends Cloneable /* , Map<String, Object> */{
    /** @return the pattern for which this is a match. */
    public Pattern pattern();
    /** Identifies the name of the pattern for which this is a match. */
    public String patternName();
    /** Returns the list of symbolic parameter names. */
    public String[] parameterNames();
    /** Returns the value of the parameter with the given name, or null if name is invalid. */
    public Object get(String parameterName);
    /** Returns the value of the parameter at the given position, or null if position is invalid. */
    public Object get(int position);
    /** Sets the parameter with the given name to the given value.     */
    public boolean set(String parameterName, Object newValue);
     /** Sets the parameter at the given position to the given value.     */
    public boolean set(int position, Object newValue);
    /** Returns whether the match object can be further modified after its creation. Setters work only if the match is mutable.      */
    public boolean isMutable();
    /** Converts the match to an array representation, with each pattern parameter at their respective position.      */
    public Object[] toArray();
    /** Prints the list of parameter-value pairs. */
    public String prettyPrint();
}


Matcher base interface

/**
 * Interface for an EMF-IncQuery matcher associated with a graph pattern.
 * 
 * @param <Match>
 *            the IPatternMatch type representing a single match of this pattern.
 * @author Bergmann Gábor
 */
public interface IncQueryMatcher<Match extends IPatternMatch> {
    // REFLECTION
    /** The pattern that will be matched. */
    public abstract Pattern getPattern();
 
    /** Fully qualified name of the pattern. */
    public abstract String getPatternName();
 
    /** Returns the index of the symbolic parameter with the given name. */
    public abstract Integer getPositionOfParameter(String parameterName);
 
    /** Returns the array of symbolic parameter names. */
    public abstract String[] getParameterNames();
 
    // ALL MATCHES
    /**
     * Returns the set of all pattern matches.
     * 
     * @return matches represented as a Match object.
     */
    public abstract Collection<Match> getAllMatches();
 
    /**
     * Returns the set of all matches of the pattern that conform to the given fixed values of some parameters.
     * 
     * @param partialMatch
     *            a partial match of the pattern where each non-null field binds the corresponding pattern parameter to
     *            a fixed value.
     * @return matches represented as a Match object.
     */
    public abstract Collection<Match> getAllMatches(Match partialMatch);
 
    // variant(s) with input binding as pattern-specific parameters: not declared in interface
 
    // SINGLE MATCH
    /**
     * Returns an arbitrarily chosen pattern match. Neither determinism nor randomness of selection is guaranteed.
     * 
     * @return a match represented as a Match object, or null if no match is found.
     */
    public abstract Match getOneArbitraryMatch();
 
    /**
     * Returns an arbitrarily chosen match of the pattern that conforms to the given fixed values of some parameters.
     * Neither determinism nor randomness of selection is guaranteed.
     * 
     * @param partialMatch
     *            a partial match of the pattern where each non-null field binds the corresponding pattern parameter to
     *            a fixed value.
     * @return a match represented as a Match object, or null if no match is found.
     */
    public abstract Match getOneArbitraryMatch(Match partialMatch);
 
    // variant(s) with input binding as pattern-specific parameters: not declared in interface
 
    // MATCH CHECKING
    /**
     * Indicates whether the given combination of specified pattern parameters constitute a valid pattern match, under
     * any possible substitution of the unspecified parameters (if any).
     * 
     * @param partialMatch
     *            a (partial) match of the pattern where each non-null field binds the corresponding pattern parameter
     *            to a fixed value.
     * @return true if the input is a valid (partial) match of the pattern.
     */
    public abstract boolean hasMatch(Match partialMatch);
 
    // variant(s) with input binding as pattern-specific parameters: not declared in interface
 
    // NUMBER OF MATCHES
    /**
     * Returns the number of all pattern matches.
     * 
     * @return the number of pattern matches found.
     */
    public abstract int countMatches();
 
    /**
     * Returns the number of all matches of the pattern that conform to the given fixed values of some parameters.
     * 
     * @param partialMatch
     *            a partial match of the pattern where each non-null field binds the corresponding pattern parameter to
     *            a fixed value.
     * @return the number of pattern matches found.
     */
    public abstract int countMatches(Match partialMatch);
 
    // variant(s) with input binding as pattern-specific parameters: not declared in interface
 
    // FOR EACH MATCH
    /**
     * Executes the given processor on each match of the pattern.
     * 
     * @param action
     *            the action that will process each pattern match.
     */
    public abstract void forEachMatch(IMatchProcessor<? super Match> processor);
 
    /**
     * Executes the given processor on each match of the pattern that conforms to the given fixed values of some
     * parameters.
     * 
     * @param parameters
     *            array where each non-null element binds the corresponding pattern parameter to a fixed value.
     * @param processor
     *            the action that will process each pattern match.
     */
    public abstract void forEachMatch(Match partialMatch, IMatchProcessor<? super Match> processor);
 
    // variant(s) with input binding as pattern-specific parameters: not declared in interface
 
    // FOR ONE ARBITRARY MATCH
    /**
     * Executes the given processor on an arbitrarily chosen match of the pattern. Neither determinism nor randomness of
     * selection is guaranteed.
     * 
     * @param processor
     *            the action that will process the selected match.
     * @return true if the pattern has at least one match, false if the processor was not invoked
     */
    public abstract boolean forOneArbitraryMatch(IMatchProcessor<? super Match> processor);
 
    /**
     * Executes the given processor on an arbitrarily chosen match of the pattern that conforms to the given fixed
     * values of some parameters. Neither determinism nor randomness of selection is guaranteed.
     * 
     * @param parameters
     *            array where each non-null element binds the corresponding pattern parameter to a fixed value.
     * @param processor
     *            the action that will process the selected match.
     * @return true if the pattern has at least one match with the given parameter values, false if the processor was
     *         not invoked
     */
    public abstract boolean forOneArbitraryMatch(Match partialMatch, IMatchProcessor<? super Match> processor);
 
    // variant(s) with input binding as pattern-specific parameters: not declared in interface
 
    // CHANGE MONITORING
    // attach delta monitor for high-level change detection
    /**
     * Registers low-level callbacks for match appearance and disappearance on this pattern matcher.
     * 
     * <p>
     * This is a low-level callback that is invoked when the pattern matcher is not necessarily in a consistent state
     * yet. Importantly, no model modification permitted during the callback. Most users should use the agenda and trigger engine instead. TODO reference
     * 
     * <p>
     * Performance note: expected to be much more efficient than polling at {@link #addCallbackAfterUpdates(Runnable)},
     * but prone to "signal hazards", e.g. spurious match appearances that will disappear immediately afterwards.
     * 
     * <p>
     * The callback can be unregistered via {@link #removeCallbackOnMatchUpdate(IMatchUpdateListener)}.
     * 
     * @param fireNow
     *            if true, appearCallback will be immediately invoked on all current matches as a one-time effect. See
     *            also {@link IncQueryMatcher#forEachMatch(IMatchProcessor)}.
     * @param listener
     *            the listener that will be notified of each new match that appears or disappears, starting from now.
     */
    public abstract void addCallbackOnMatchUpdate(IMatchUpdateListener<Match> listener, boolean fireNow);
 
    /**
     * Unregisters a callback registered by {@link #addCallbackOnMatchUpdate(IMatchUpdateListener, boolean)}.
     * 
     * @param listener
     *            the listener that will no longer be notified.
     */
    public abstract void removeCallbackOnMatchUpdate(IMatchUpdateListener<Match> listener);
 
    /**
     * Registers a new delta monitor on this pattern matcher. The DeltaMonitor can be used to track changes (delta) in
     * the set of pattern matches from now on. It can also be reset to track changes from a later point in time, and
     * changes can even be acknowledged on an individual basis. See {@link DeltaMonitor} for details.
     * 
     * @param fillAtStart
     *            if true, all current matches are reported as new match events; if false, the delta monitor starts
     *            empty.
     * @return the delta monitor.
     */
    public abstract DeltaMonitor<Match> newDeltaMonitor(boolean fillAtStart);
 
    /**
     * Registers a new filtered delta monitor on this pattern matcher. The DeltaMonitor can be used to track changes
     * (delta) in the set of filtered pattern matches from now on, considering those matches only that conform to the
     * given fixed values of some parameters. It can also be reset to track changes from a later point in time, and
     * changes can even be acknowledged on an individual basis. See {@link DeltaMonitor} for details.
     * 
     * @param fillAtStart
     *            if true, all current matches are reported as new match events; if false, the delta monitor starts
     *            empty.
     * @param partialMatch
     *            a partial match of the pattern where each non-null field binds the corresponding pattern parameter to
     *            a fixed value.
     * @return the delta monitor.
     */
    public abstract DeltaMonitor<Match> newFilteredDeltaMonitor(boolean fillAtStart, Match partialMatch);
 
    /**
     * Registers a callback that will be run each time EMF-IncQuery match sets are refreshed after a model update.
     * Typically useful to check delta monitors. When the callback is issued, the pattern match sets are guaranteed to
     * reflect the post-state after the update.
     * <p>
     * Callbacks are issued after each elementary change (i.e. possibly at incomplete transient states). This can have a
     * negative effect on performance, therefore clients are advised to use it as a last resort only. Consider
     * coarser-grained timing (e.g EMF Transaction pre/post-commit) instead, whenever available.
     * 
     * @param callback
     *            a Runnable to execute after each update.
     * @return false if the callback was already registered.
     */
    public boolean addCallbackAfterUpdates(Runnable callback);
 
    /**
     * Removes a previously registered callback. See addCallbackAfterUpdates().
     * 
     * @param callback
     *            the callback to remove.
     * @return false if the callback was not registered.
     */
    public boolean removeCallbackAfterUpdates(Runnable callback);
 
    /**
     * Registers a callback that will be run each time the EMF-IncQuery engine is wiped or disposed. Typically useful if
     * delta monitors are used, especially of the {@link IncQueryEngine} is managed.
     * 
     * <p>
     * When the callback is issued, the wipe has already occurred and pattern matchers will continue to return stale
     * results.
     * 
     * @param callback
     *            a Runnable to execute after each wipe.
     * @return false if the callback was already registered.
     */
    public boolean addCallbackAfterWipes(Runnable callback);
 
    /**
     * Removes a previously registered callback. See {@link #addCallbackAfterWipes()}.
     * 
     * @param callback
     *            the callback to remove.
     * @return false if the callback was not registered.
     */
    public boolean removeCallbackAfterWipes(Runnable callback);
 
    /**
     * Returns an empty, mutable Match for the matcher. 
     * Fields of the mutable match can be filled to create a partial match, usable as matcher input. 
     * This can be used to call the matcher with a partial match 
     *  even if the specific class of the matcher or the match is unknown.
     * 
     * @return the empty match
     */
    public abstract Match newEmptyMatch();
 
    /**
     * Returns a new (partial) Match object for the matcher. 
     * This can be used e.g. to call the matcher with a partial
     * match. 
     * 
     * <p>The returned match will be immutable. Use {@link #newEmptyMatch()} to obtain a mutable match object.
     * 
     * @param parameters
     *            the fixed value of pattern parameters, or null if not bound.
     * @return the (partial) match object.
     */
    public abstract Match newMatch(Object... parameters);
 
    /**
     * Retrieve the set of values that occur in matches for the given parameterName.
     * 
     * @param parameterName
     *            name of the parameter for which values are returned
     * @return the Set of all values for the given parameter, null if the parameter with the given name does not exists,
     *         empty set if there are no matches
     */
    public abstract Set<Object> getAllValues(final String parameterName);
 
    /**
     * Retrieve the set of values that occur in matches for the given parameterName, that conforms to the given fixed
     * values of some parameters.
     * 
     * @param parameterName
     *            name of the parameter for which values are returned
     * @param partialMatch
     *            a partial match of the pattern where each non-null field binds the corresponding pattern parameter to
     *            a fixed value.
     * @return the Set of all values for the given parameter, null if the parameter with the given name does not exists
     *         or if the parameter with the given name is set in partialMatch, empty set if there are no matches
     */
    public abstract Set<Object> getAllValues(final String parameterName, Match partialMatch);
 
    /**
     * Returns the engine that the matcher uses.
     * 
     * @return the engine
     */
    public abstract IncQueryEngine getEngine();
}

Matcher Factory base interface

/**
 * Interface for an IncQuery matcher factory. Each factory is associated with a pattern. Methods instantiate a matcher
 * of the pattern with various parameters.
 * 
 * @author Bergmann Gábor
 * 
 */
public interface IMatcherFactory<Matcher extends IncQueryMatcher<? extends IPatternMatch>> {
 
    /**
     * @throws IncQueryException
     *             if there was an error loading the pattern definition
     * @returns the pattern for which matchers can be instantiated.
     */
    public Pattern getPattern();
 
    /**
     * Identifies the pattern for which matchers can be instantiated.
     */
    public String getPatternFullyQualifiedName();
 
    /**
     * Initializes the pattern matcher over a given EMF model root (recommended: Resource or ResourceSet). If a pattern
     * matcher is already constructed with the same root, only a lightweight reference is created.
     * 
     * <p>
     * The scope of pattern matching will be the given EMF model root and below (see FAQ for more precise definition).
     * <p>
     * The match set will be incrementally refreshed upon updates from this scope.
     * 
     * <p>
     * The matcher will be created within the managed {@link IncQueryEngine} belonging to the EMF model root, so
     * multiple matchers will reuse the same engine and benefit from increased performance and reduced memory footprint.
     * 
     * @param emfRoot
     *            the root of the EMF tree where the pattern matcher will operate. Recommended: Resource or ResourceSet.
     * @throws IncQueryException
     *             if an error occurs during pattern matcher creation
     */
    public Matcher getMatcher(Notifier emfRoot) throws IncQueryException;
 
    /**
     * Initializes the pattern matcher within an existing {@link IncQueryEngine}. If the pattern matcher is already
     * constructed in the engine, only a lightweight reference is created.
     * <p>
     * The match set will be incrementally refreshed upon updates.
     * 
     * @param engine
     *            the existing EMF-IncQuery engine in which this matcher will be created.
     * @throws IncQueryException
     *             if an error occurs during pattern matcher creation
     */
    public Matcher getMatcher(IncQueryEngine engine) throws IncQueryException;
}

Example

public String executeGeneric(String modelPath, String patternFQN) {
 StringBuilder results = new StringBuilder();
 Resource resource = loadModel(modelPath);
 if (resource != null) {
  try {
   // get all matches of the pattern
   IncQueryMatcher matcher = MatcherFactoryRegistry.getMatcherFactory(patternFQN).getMatcher(resource);
   Collection<IPatternMatch> matches = matcher.getAllMatches();
   prettyPrintMatches(results, matches);
  } catch (IncQueryException e) {
   e.printStackTrace();
   results.append(e.getMessage());
  }
 } else {
  results.append("Resource not found");
 }
 return results.toString();
}

Most important generated classes and their relationships

For every pattern a Match, a Matcher, a MatcherFactory, a Processor and optionally several Evaluator classes are available through both the generic and generated APIs. Let’s look into what these classes are responsible for: 

  • Match: This represents a match of the pattern. The generated fields represent the pattern header parameters. Basically it is used to transfer data to and from the pattern matcher, and thus it can be used in conjunction with the Matcher class (see below). You can use it to specify fixed input parameters to a query (while other fields can be left unspecified), and the results of you queries will also be instances of this class (where all parameters already have given values). The example below shows the EClassNamesMatch class generated for the eClassNames pattern above.
public abstract class EClassNamesMatch extends BasePatternMatch {
  /**
   * members and constructor
   */
  private EClass fC;
  private String fN;
  private static String[] parameterNames = {"C", "N"};
  private EClassNamesMatch(final EClass pC, final String pN) {}
 
  /** getters and setters **/
  public Object get(final String parameterName) { }
  public EClass getC() {}
  public String getN() {}
  public boolean set(final String parameterName, final Object newValue) {}
  public void setC(final EClass pC) {}
  public void setN(final String pN) {}
 
  /** utility functions **/
  public String patternName() {}
  public String[] parameterNames() {}
  public Object[] toArray() {}
  public String prettyPrint() {}
  public int hashCode() {}
  public boolean equals(final Object obj) {}
 
  /** access to the internal EMF-based representation, as a sort of "reflection" **/
  public Pattern pattern() {}
 
  /** Mutable inner subclass so that matches can be "setted" - for parameter passing **/
  static final class Mutable extends EClassNamesMatch {}
}
  • Matcher: This is the main entry point in our API, with pattern-specific query methods. First of all it provides means to initialize a pattern matcher for a given EMF instance model which can either be a Resource, a ResourceSet, or an EObject (in this latter case, the scope of the matching will be the containment tree under the passed EObject). We recommend the use of ResourceSets if possible to avoid cross-reference related issues. After the initialization of the engine the Matcher provides getter methods to retrieve the contents of the match set anytime. For easy iteration over the match set it provides a convenience method (forEachMatch) as well, as this is the most frequent use case in our observation. Of course it contains other handy features (e.g.: countMatches, hasMatch) to help integration. Finally, it provides a DeltaMonitor which can be used to track the changes in the match set in an efficient, event-driven fashion. The example generated source code below demonstrates the EClassNamesMatcher class generated for the eClassNames pattern above.
public class EClassNamesMatcher extends BaseGeneratedMatcher<EClassNamesMatch> {
 
  /** initializer methods **/
  public EClassNamesMatcher(final Notifier emfRoot) throws IncQueryException {}
  public EClassNamesMatcher(final IncQueryEngine engine) throws IncQueryException {}
 
  /** access to match set **/
  public Collection<EClassNamesMatch> getAllMatches(final EClass pC, final String pN) {}
  public EClassNamesMatch getOneArbitraryMatch(final EClass pC, final String pN) {}
  public boolean hasMatch(final EClass pC, final String pN) {}
  public int countMatches(final EClass pC, final String pN) {}
 
  /** iterate over matches, like a lambda **/
  public void forEachMatch(final EClass pC, final String pN, final IMatchProcessor<? super EClassNamesMatch> processor) {}
  public boolean forOneArbitraryMatch(final EClass pC, final String pN, final IMatchProcessor<? super EClassNamesMatch> processor) {}
 
  /** process match set changes in an event-driven way **/
  public DeltaMonitor<EClassNamesMatch> newFilteredDeltaMonitor(final boolean fillAtStart, final EClass pC, final String pN) {}
 
  /** Returns a new (partial) Match object for the matcher. This can be used e.g. to call the matcher with a partial match. **/
   public EClassNamesMatch newMatch(final EClass pC, final String pN) {}
 
  /** Retrieve the set of values that occur in matches for C or N.**/
  public Set<EClass> getAllValuesOfC() {}
  public Set<EClass> getAllValuesOfC(final EClassNamesMatch partialMatch) {}
  public Set<EClass> getAllValuesOfC(final String pN) {}
  public Set<String> getAllValuesOfN() {}
  public Set<String> getAllValuesOfN(final EClassNamesMatch partialMatch) {}
  public Set<String> getAllValuesOfN(final EClass pC) {}
 
  /** @return the singleton instance of the factory of this pattern **/
  public static IMatcherFactory<EClassNamesMatcher> factory() throws IncQueryException {}
}
  • MatcherFactory: A pattern-specific factory that can instantiate a Matcher class in a type-safe way. You can get an instance of it via the Matcher class’s factory() method. There are two ways to instantiate a Matcher, with a Notifier (e.g.: Resource, ResourceSet and EObject) as we mentioned already, or with an IncQueryEngine. In both cases if the pattern is already registered (with the same root in the case of the Notifier method) then only a lightweight reference is created which points to the existing engine.
/**
 * A pattern-specific matcher factory that can instantiate EClassNamesMatcher in a type-safe way.
 */
public final class EClassNamesMatcherFactory extends BaseGeneratedMatcherFactory<EClassNamesMatcher> {
  /** @return the singleton instance of the matcher factory **/
  public static EClassNamesMatcherFactory instance() throws IncQueryException {}
}
  • MatchProcessor: The Matcher provides a function to iterate over the match set and invoke the process() method of the IMatchProcessor interface with every match. To help with the processing an abstract processor class is generated, which you can override to implement the logic you would like to use. The abstract class unpacks the match variables so it can be used directly in the process() method.
/**
 * A match processor tailored for the headless.eClassNames pattern.
 * 
 * Clients should derive an (anonymous) class that implements the abstract process().
 * 
 */
public abstract class EClassNamesProcessor implements IMatchProcessor<EClassNamesMatch> {
  /**
   * Defines the action that is to be executed on each match.
   * @param pC the value of pattern parameter C in the currently processed match 
   * @param pN the value of pattern parameter N in the currently processed match 
   * 
   */
  public abstract void process(final EClass C, final String N);
 
  public void process(final EClassNamesMatch match) {}
}
  • Evaluator: If your pattern contains check expressions an evaluator java code is generated from it. It is used by the engine during a query to evaluate the expression’s result. In most cases you don’t need to deal with these classes.

Lifecycle management

We have an EngineManager singleton class to orchestrate the lifecycle of the IncQueryEngines. There are two types of engines: managed and unmanaged. We recommend the use of managed engines, this is the default behavior, as these engines can share common indices and caches to save memory and cpu time. The EngineManager ensures that there will be no duplicated engine for the same root object. The managed engines can be disposed from the manager if needed. On the other hand creating an unmanaged engine will give you the power and responsibility to use it correctly. It will have no common part with other engines. 

The IncQueryEngine is attached to an EMF resource (Resource, ResourceSet or EObject) and hosts the pattern matchers. It will listen on EMF update notifications stemming from the given model in order to maintain live results. Pattern matchers can be registered in the following ways: 

  • Instantiate the specific matcher class generated for the pattern, by passing to the constructor either this engine or the EMF model root. 
  • Use the matcher factory associated with the generated matcher class to achieve the same. 
  • Use the GenericPatternMatcher or the GenericMatcherFactory instead of the various generated classes.

If you want to remove the matchers from the engine you can call the wipe() method on it. It discards any pattern matcher caches and forgets the known patterns. The base index built directly on the underlying EMF model, however, is kept in memory to allow reuse when new pattern matchers are built. If you don’t want to use it anymore call the dispose() instead, to completely disconnect and dismantle the engine. 

Typical programming patterns

Note: We recommend putting the @Handler on any pattern, because it will generate a project that contains code segments that illustrate the basic usage of the IncQuery Java API. The sample code will contain an Eclipse command handler and a dialog that shows the matches of the query in a selected file resource (you can try them in a runtime application run configuration, with right-clicking on the instance model file in e.g. the Project Explorer). 

Loading an instance model and executing a query
public String executePatternSpecific(String modelPath) {
 StringBuilder results = new StringBuilder();
 Resource resource = loadModel(modelPath);
 if (resource != null) {
  try {
   // get all matches of the pattern
   EObjectMatcher matcher = EObjectMatcher.factory().getMatcher(resource);
   Collection<EObjectMatch> matches = matcher.getAllMatches();
   prettyPrintMatches(results, matches);
  } catch (IncQueryException e) {
   e.printStackTrace();
   results.append(e.getMessage());
  }
  } else {
   results.append("Resource not found");
  }
  return results.toString();
}
Using the MatchProcessor

With the MatchProcessor you can iterate over the matches of a pattern quite easily:

try {
   // get all matches of the pattern
   EObjectMatcher matcher = EObjectMatcher.factory().getMatcher(resource);
   matcher.forEachMatch(new EObjectProcessor() {
	@Override
	public void process(EObject o) {
	    // Do something with a match
	}
    });
} catch (IncQueryException e) {
    e.printStackTrace();
}
Tracking changes in match sets efficiently

There are some usecases where you don’t want to follow every change of a pattern’s match, just gather them together and process them when you’re ready.

TODO the technique below are now deprecated:

  • The DeltaMonitor can do this for you in a convenient way. It is a monitoring object that connects to the rete network as a receiver to reflect changes since an arbitrary state acknowledged by the client. If a new matching is found, it appears in the matchFoundEvents collection, and disappears when that particular matching cannot be found anymore. If the event of finding a match has been processed by the client, it can be removed manually. In this case, when a previously found matching is lost, the Tuple will appear in the matchLostEvents collection, and disappear upon finding the same matching again. "Matching lost" events can also be acknowledged by removing a Tuple from the collection. If the matching is found once again, it will return to matchFoundEvents.
TODO


Back to the top