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/Addon/Surrogate Queries"

< VIATRA‎ | Addon
Line 13: Line 13:
 
</source>
 
</source>
  
In order to create a surrogate query, simply add the @Surrogate annotation for a pattern and the generator fragment will take care of generating the correct extension points. When the query plug-in is included in the host, the EMF-IncQuery runtime will automatically replace path expressions including the feature with a suppattern call. In addition, if the plug-in is available in the host or target platform, the warning for a derived feature usage will be different (instead of warning about not representable feature, it will include the fully qualified name of the surrogate query).
+
In order to create a surrogate query, simply add the @Surrogate annotation for a pattern and the generator fragment will take care of generating the correct extension points. When the query plug-in is included in the host, the EMF-IncQuery runtime will automatically replace path expressions including the feature with a suppattern call. In addition, if the plug-in is available in the host or target platform, the warning for a derived feature usage will be different (instead of warning about not representable feature, it will include the fully qualified name of the surrogate query). So the following will work correctly during runtime:
 +
 
 +
<source lang="java">
 +
pattern superClassWithName(self : Classifier) {
 +
  Classifier.superClass(self, superClass);
 +
  Classifier.name(superClass, "mySuperClass");
 +
}
 +
</source>
  
 
== Example ==
 
== Example ==
Line 58: Line 65:
  
 
=== Definition of surrogate queries ===
 
=== Definition of surrogate queries ===
 +
 +
The '''@Surrogate''' annotation has a single, optional parameter ''feature'' which specifies the name of the EStructuralFeature that the surrogate query replaces. If omitted, the name of the pattern must match the name of the feature. The first parameter of the pattern is always the source, and the second parameter is the target.
 +
 +
Let's assume you want to surrogate a derived feature ''someExternalModelFeature'' in EClass ''ExternalClass'' with type ''OtherExternalClass''.
 +
 +
You can choose between:
 +
<source lang="java">
 +
@Surrogate(feature = "someExternalModelFeature")
 +
pattern mySurrogatePattern(this : ExternalClass, target : OtherExternalClass) {
 +
  [...] // pattern body
 +
}
 +
</source>
 +
 +
and:
 +
 +
<source lang="java">
 +
@Surrogate
 +
pattern someExternalModelFeature(this : ExternalClass, target : OtherExternalClass) {
 +
  [...] // pattern body
 +
}
 +
</source>
 +
 +
The annotation is defined by the ''querybasedfeatures.runtime'' plug-in and there is a validator also provided by the same plug-in, which checks several things:
 +
* the pattern has two parameters
 +
* the feature specified by the pattern name or the annotation parameter exists in the source EClass
 +
* the target type of the feature is compatible with the second parameter
 +
* there is only one Surrogate annotation for a pattern or each of them define different features
 +
 +
The code generator fragment is defined by the ''querybasedfeatures.tooling'' plug-in and it simply creates an extension for the surrogate query extension point in the plugin.xml:
 +
 +
<source lang="xml">
 +
<extension id="extension.surrogate.mySurrogates.mySurrogatePattern" point="org.eclipse.incquery.patternlanguage.emf.surrogatequeryemf">
 +
  <surrogate-query-emf class-name="ExternalClass" feature-name="someExternalModelFeature" package-nsUri="external.ecore.uri"
 +
    surrogate-query="org.eclipse.incquery.runtime.extensibility.PQueryExtensionFactory:mySurrogates.MySurrogatePatternQuerySpecification"/>
 +
</extension>
 +
</source>
  
 
=== Runtime behavior ===
 
=== Runtime behavior ===
 +
 +
During runtime, the surrogate queries are loaded into a surrogate query registry (defined in the ''runtime.matchers'' plug-in) by reading the extension registry of Eclipse.
 +
When a given pattern is loaded into an engine, path expressions including derived features with defined surrogate queries are replaced in the PSystem representation.
 +
 +
This means that the surrogate queries are only used if they are available and registered. Additionally, for query backends that can handle non well-behaving derived features (e.g. the [[EMFIncQuery/DeveloperDocumentation/LocalSearch|local search]] backend), this rewriting is skipped.
  
 
=== Usage outside of Eclipse ===
 
=== Usage outside of Eclipse ===
 +
 +
Since the extension registry is not available when running outside of Eclipse, users have to manually register surrogate queries before they can be used for query evaluation.
 +
 +
In addition to the instructions in [[EMFIncQuery/UserDocumentation/HeadlessExecution#Using_IncQuery_in_a_Java_application]], the following setup is required for each surrogate query:
 +
 +
<source lang="java">
 +
SurrogateQueryRegistry.instance().registerSurrogateQueryForFeature(
 +
  new EStructuralFeatureInstancesKey(ExternalPackage.Literals.EXTERNAL_CLASS_SOME_EXTERNAL_MODEL_FEATURE),
 +
  MySurrogatePatternQuerySpecification.instance.getInternalQueryRepresentation());
 +
</source>
 +
 +
See http://git.eclipse.org/c/incquery/org.eclipse.incquery.git/tree/plugins/org.eclipse.incquery.uml/src/org/eclipse/incquery/uml/IncQueryUMLStandaloneSetup.java for an example.

Revision as of 08:34, 21 April 2015

Surrogate queries for derived features

While query-based features have supported the definition of well-behaving derived features in Ecore models, users of Ecore models that could not be modified had no way for using derived features in queries. EMF-IncQuery 1.0.0 introduces surrogate queries for derived features, where a derived feature in a query is replaced by a subpattern call during runtime.

Usage

@Surrogate
pattern superClass(self : Classifier, super : Classifer) {
  Classifier.generalization(self, generalization);
  Generalization.general(generalization, classifier);
}

In order to create a surrogate query, simply add the @Surrogate annotation for a pattern and the generator fragment will take care of generating the correct extension points. When the query plug-in is included in the host, the EMF-IncQuery runtime will automatically replace path expressions including the feature with a suppattern call. In addition, if the plug-in is available in the host or target platform, the warning for a derived feature usage will be different (instead of warning about not representable feature, it will include the fully qualified name of the surrogate query). So the following will work correctly during runtime:

pattern superClassWithName(self : Classifier) {
  Classifier.superClass(self, superClass);
  Classifier.name(superClass, "mySuperClass");
}

Example

The UML metamodel used in EMF-UML contains a large number of derived features (see EMFIncQuery/UMLSupport#Status_of_derived_features for details), most of which are not well-behaving. This meant that defining patterns over UML models was often cumbersome.

Consider the following pattern:

pattern superClassWithQualifiedName(self : Classifier) {
  Classifier.superClass(self, superClass);
  Classifier.qualifiedName(superClass, "my::favorite::package::SuperSuperClass");
}

Both Classifer.superClass and NamedElement.qualifiedName are derived features, therefore

  • the pattern editor will display a warning about these features not being representable for incremental evaluation
  • the runtime will index the value of these features and no matches will be returned.

Since the value of these feature can be computed from the rest of the model, users often manually defined helper patterns, for example:

pattern superClass(self : Classifier, super : Classifer) {
  Classifier.generalization(self, generalization);
  Generalization.general(generalization, classifier);
}
 
pattern superClassWithQualifiedName(self : Classifier) {
  find superClass(self, superClass);
  Classifier.qualifiedName(superClass, "my::favorite::package::SuperSuperClass");
}

However, this approach has several drawbacks:

  • Everyone reinvents the wheel by specifying derived features again and again.
  • The definition is error-prone, you can easily overlook some detail in the computation and get unexpected results.
  • You cannot use the derived feature itself in the pattern, only the explicit find to your helper pattern.

Surrogate queries were introduced to help overcoming these issues.

Technical details

Surrogate query support is provided by the @Surrogate annotation in the pattern editor, the corresponding code generator fragment, the runtime loading and usage of surrogate query registry, the runtime replacement of derived feature usage in queries. Additionally, when running outside of Eclipse, some additional setup is required.

Definition of surrogate queries

The @Surrogate annotation has a single, optional parameter feature which specifies the name of the EStructuralFeature that the surrogate query replaces. If omitted, the name of the pattern must match the name of the feature. The first parameter of the pattern is always the source, and the second parameter is the target.

Let's assume you want to surrogate a derived feature someExternalModelFeature in EClass ExternalClass with type OtherExternalClass.

You can choose between:

@Surrogate(feature = "someExternalModelFeature")
pattern mySurrogatePattern(this : ExternalClass, target : OtherExternalClass) {
  [...] // pattern body
}

and:

@Surrogate
pattern someExternalModelFeature(this : ExternalClass, target : OtherExternalClass) {
  [...] // pattern body
}

The annotation is defined by the querybasedfeatures.runtime plug-in and there is a validator also provided by the same plug-in, which checks several things:

  • the pattern has two parameters
  • the feature specified by the pattern name or the annotation parameter exists in the source EClass
  • the target type of the feature is compatible with the second parameter
  • there is only one Surrogate annotation for a pattern or each of them define different features

The code generator fragment is defined by the querybasedfeatures.tooling plug-in and it simply creates an extension for the surrogate query extension point in the plugin.xml:

<extension id="extension.surrogate.mySurrogates.mySurrogatePattern" point="org.eclipse.incquery.patternlanguage.emf.surrogatequeryemf">
  <surrogate-query-emf class-name="ExternalClass" feature-name="someExternalModelFeature" package-nsUri="external.ecore.uri"
    surrogate-query="org.eclipse.incquery.runtime.extensibility.PQueryExtensionFactory:mySurrogates.MySurrogatePatternQuerySpecification"/>
</extension>

Runtime behavior

During runtime, the surrogate queries are loaded into a surrogate query registry (defined in the runtime.matchers plug-in) by reading the extension registry of Eclipse. When a given pattern is loaded into an engine, path expressions including derived features with defined surrogate queries are replaced in the PSystem representation.

This means that the surrogate queries are only used if they are available and registered. Additionally, for query backends that can handle non well-behaving derived features (e.g. the local search backend), this rewriting is skipped.

Usage outside of Eclipse

Since the extension registry is not available when running outside of Eclipse, users have to manually register surrogate queries before they can be used for query evaluation.

In addition to the instructions in EMFIncQuery/UserDocumentation/HeadlessExecution#Using_IncQuery_in_a_Java_application, the following setup is required for each surrogate query:

SurrogateQueryRegistry.instance().registerSurrogateQueryForFeature(
  new EStructuralFeatureInstancesKey(ExternalPackage.Literals.EXTERNAL_CLASS_SOME_EXTERNAL_MODEL_FEATURE),
  MySurrogatePatternQuerySpecification.instance.getInternalQueryRepresentation());

See http://git.eclipse.org/c/incquery/org.eclipse.incquery.git/tree/plugins/org.eclipse.incquery.uml/src/org/eclipse/incquery/uml/IncQueryUMLStandaloneSetup.java for an example.

Back to the top