Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
VIATRA/Addon/Surrogate Queries
Contents
Surrogate queries for derived features
Query-based features capture the definition of well-behaving derived features of Ecore models by queries, and allow the use of such derived features in the body of other queries. But when an Ecore model is not allowed to be modified, you could not use derived features in query bodies in the past. EMF-IncQuery 1.0.0 introduced surrogate queries for derived features, where a derived feature used in a query is replaced by a subpattern call during query execution time (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 a @Surrogate annotation for a pattern and the generator will take care of defining the correct extension points. When the query plug-in is included in the host, the VIATRA Query runtime will automatically replace path expressions including the feature with a subpattern 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"); }
Important information on developing surrogate queries
Surrogate queries defined in workspace projects are not yet visible to the Query Explorer, so loading queries that use those derived features will result in incorrect match results. If you want to try such queries in the Query Explorer, do the following:
- If the surrogate query definition and the pattern using it are in different projects, simply start a runtime Eclipse where at least the defining query is included.
- If the surrogate query definition and the pattern using it are in the same project, simply use a subpattern call (find) instead.
Example
The UML metamodel used in EMF-UML contains a large number of derived features (see VIATRA/Integration/UMLSupport#Status_of_derived_features for details), most of which are not well-behaving, which significantly complicated the definition of patterns over UML models in the past.
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 are not amenable to 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:
- Reinventing the wheel: derived features are redefined over and over again.
- Error-prone definition: you can easily overlook some detail in the computation and get unexpected results.
- Disallowed use in patterns: the derived feature cannot be used directly in other pattern bodies, you need to explicitly call the helper pattern (by the find construct).
Surrogate queries are introduced to help overcome these issues.
Technical details
Surrogate query support includes 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. However, 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 us 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 together with a validator (also provided by the same plug-in), which checks several things:
- the pattern has exactly two parameters
- the feature specified by the pattern name or the parameter of the annotation exists in the source EClass
- the target type of the feature is compatible with the second parameter of the pattern
- 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.viatra.query.patternlanguage.emf.surrogatequeryemf"> <surrogate-query-emf class-name="ExternalClass" feature-name="someExternalModelFeature" package-nsUri="external.ecore.uri" surrogate-query="org.eclipse.viatra.query.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 Using VIATRA Query 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 the VIATRA UML standalone setup for an example.