Many parts of a VSM require you to provide interpreted expressions, which will be evaluated at runtime to provide a behavior specific to your domain and representations. Some of these expressions return model elements (in which case we call them queries), while others simply produce text (for example the expressions used for labels), but the principles stay the same.
Viewpoint does not force the use of a specific language to write these queries. Currently, three different languages are provided by default, and if you have specific needs, you can extend the system and provide your own. You can use several different languages inside the same VSM (but only one in each expression).
Whatever the language(s) you use, because they are all optional from Viewpoint’s point of view, make sure the Viewpoint Specification Project which contains your VSMs explicitly depends on the Viewpoint plug-in which provides supports for these languages. Otherwise when you modelers are deployed to end-users you can not be sure the proper support will be available. The Viewpoint Specification Projects which are created using the standard wizard are pre-configured to use Acceleo 3. See the sections below for each of the standard languages for the name of the Viewpoint support plug-ins to depend on if you use the language.
Some general rules are independent of the language details.
Auto-completion. First, in the VSM editor, any field in the property view which has a yellow background is an interpreted expression. In these fields, auto-completion can be triggered by hitting Ctrl+Space. The first completion proposal will correspond to the variables which are available in the expression’s context, and the rest correspond to all the features, services, etc. which are available on the current element.
Sometimes Viewpoint is not able to statically determine the precise type of the model elements on which an expression will be evaluated. In that case, it assumes a plain
EObject. This means you will not get completion proposals specific to your custom types, and that if you use features or services which are not available on
EObject, the VSM’s validation will fail. To prevent this, if you know the precise type of the elements and the language supports it, add a cast operation to tell Viewpoint the actual type (e.g. [filter(ExpectedType).theRestOfTheExpression/]
if using Acceleo 3.
The Interpreter view. The Interpreter view can be used to develop complex expressions interactively. The view can work in either the native Acceleo mode (corresponding to Acceleo 3/MTL) or in the Viewpoint mode. In the Viewpoint mode, you can use any of the languages supported by Viewpoint, but you lose the benefits of syntax coloring and auto-completion.
To use the Interpreter view, simply open a Viewpoint representation, select and element in it (for example a shape on a diagram), and type some expression in the view’s upper text field. The result of the expression (a set of model elements or a string) will appear in the lower part of the view. If you select a different element, the result will automatically be re-evaluated in the context of the new element.
Warning: When using the
Interpreter view from an element selected in a Viewpoint representation, the context of the expression is
not the
semantic element, but the
view model element used internally by Viewpoint. To access the semantic element you must use the view’s
target
reference. More concretely, imagine you have a Viewpoint diagram representing UML classes with nodes. If you select a class and enter [self/]
in the interpreter view, the result will not be a UML
Class
element, but a
DNode
(the type used internally by Viewpoint to represent graphical nodes). To get the UML class, you must enter [self.target/]
. Keep this in mind when using the
Interpreter view to develop expressions you want to use inside a VSM: most expressions defined in the VSM will be evaluated in the context of a semantic element, so you will need to add/remove
target
references when switching between the two.
Determinism. In general, you should try to ensure that your queries are deterministic, i.e. from the same input model they should always return the same result, and in the same order (if returning collections of elements). Otherwise, each time a representation is refreshed it might become dirty (as Viewpoint will see the order difference as a change) or even visually unstable (i.e. elements shown in a different order). This is particularly important when using collaborative modeling, as indeterminism can lead to the so-called «ping-pong effect», where each user gets different results from the same queries, leading to an unstable state with no possible global synchronization. There are several things to be aware of in order to ensure determinism in your queries:
nMinimize()
standard service on the legacy query language is not. Unfortunately, this information is not often documented in the query languages semantics, so some trial and error experimentation is required for now.
LinkedHashSet
instead of
HashSet
for example.
Acceleo is the recommended language to use for new VSMs. It provides better static validation than the rest, and more precise auto-completion when editing your expressions. Viewpoint Specification Projects created with the default wizard will be already setup to use it. Otherwise you need to add a dependency to the
fr.obeo.dsl.common.acceleo.mtl
plug-in to ensure Acceleo support will be available wherever your modelers are used.
Acceleo expressions are enclosed in brackets: [theExpression/]
. Inside the brackets you can write any valid Acceleo expression, including using
if
and
let
statements, for example. See
the Acceleo documentation for the exact syntax and semantics of the language.
Note that currently Acceleo expressions used inside VSMs
must be enclosed inside a single bracket. It is not currently possible to mix Acceleo expressions and fixed text (like
"prefix[someExpression/]suffix"
). Instead, you can use Acceleo’s string manipulation operations to obtain the same result: [‹prefix› + someExpression + ‹suffix›/]
). Related to this, auto-completion only works if you are inside a well-formed bracket (i.e. [<cursor>/]
); if you have simply opened the bracket but not closed it (i.e. [<cursor>
), completion will not be available.
Acceleo expressions can transparently invoke methods from Java classes which follow the
service methods conventions and have been properly declared in the VSM. Note that currently this only works if the Java service class is in the same project as the
VSM. You can also invoke Acceleo
queries defined in
.mtl
files in your Viewpoint Specification Project. For this to work, your Viewpoint Specification Project should be an Acceleo Generator project. This is the default for projects created with the Viewpoint wizard. If you want to use queries from
.mtl
files in you Viewpoint project, the parent
Viewpoint element must have a
Java Extension element which references the MTL file, using the
com::example::domain::design::module
syntax. Also make sure to read
http://www.obeonetwork.com/page/building-an-acceleo-generator to ensure the project is built correctly, or the queries defined in the
.mtl
files may not be available when the project is deployed as a plug-in.
Acceleo is more precise than the legacy language, but also more demanding, about the types of elements your queries are executed on. This generally means you get better auto-completion and more useful validation and diagnostics. However sometimes Viewpoint is not able to statically determine the precise type of the model elements on which an expression will be evaluated. In that case, it assumes a plain
EObject. If you know the actual type which will be used, prefix your expression with
filter(ExpectedType)
to help Viewpoint. Note that if the actual type at runtime is not compatible with
ExpectedType
, the rest of your expression will silently be ignored.
In the context of Viewpoint, you have access to a special feature which can be used to follow «back-links» or «cross-references». From a given model element, this allows you to easily (and efficiently) find all the elements which refer to it in the scope of the models and representations in the same modeling project. This feature is available through the
eInverse()
method, which can be used on any model element inside an Acceleo expression.
Note that in most cases, expressions in Viewpoint are evaluated in a context where
variables are defined. For example, most expressions inside tool definitions have access to variables telling them which elements the tool has been applied on. In some cases, the names of these variable can conflict with names of features in your meta-model. The evaluation rules of Acceleo (OCL actually) give precedence to variables, so it is recommended to always prefix accesses to your meta-model features with
self
(or another expression) to avoid ambiguity. As a concrete example, say you have an expression
"[target/]"
that you expect to access the
target
feature of one of your object. If evaluated in a context where a
target
variable exist, the value of the expression will be the value of the variable,
not the value of the object’s property. To avoid ambiguity, always use expressions of the form
"[self.target/]"
in such cases.
Before Viewpoint 6.3, the main query language used in Viewpoint was based on Acceleo 2, which is an older, incompatible version of Acceleo and is not maintained anymore. It is still supported and not currently deprecated. We refer to it as the «legacy language» to avoid confusion with the newer Acceleo (version 3 and above). If you use it your Viewpoint project must declare a dependency to the
fr.obeo.dsl.common.acceleo
plug-in to ensure Acceleo 2 support will be available wherever your modelers are used.
Expressions in this language are enclosed between
<%
and
%>
strings. For example,
<%name%>
. You can mix fixed strings and evaluated parts inside a single expression, for example
Some text <%aComputedPart%>. Some other <%computedText%>.
See the language’s
reference manual for more details.
In the context of Viewpoint, you have access to a special feature which can be used to follow «back-links» or «cross-references». From a given model element, this allows you to easily (and efficiently) find all the elements which refer to it in the scope of the models and representations in the same modeling project. This feature is available through the
~
operator, which has two forms:
anExpression.~
: if
anExpression
evaluates to the model element
M, then the expression will return the set of all model elements which have a direct reference to
M.
anExpression.~ref
, where
ref
can be any valid name: if
anExpression
evaluates to the model element
M, then the expression will return the set of all model elements which have a direct reference to
M through a reference named
ref
.
For example if model element
A has a reference named
r1 to model element
C, and
B has a reference
r2 to
C, then
C.~r1
will return
A but not
B, while
C.~
would return both
A and
B.
Viewpoint also supports raw OCL expressions, but this support is deprecated and will be removed in future versions. OCL expressions must be prefixed with
ocl:
. If you use it your Viewpoint project must declare a dependency to the
fr.obeo.dsl.common.ocl
plug-in to ensure OCL support will be available wherever your modelers are used.
It is highly recommended that you use Acceleo, which implemented the MTL standard and is a super-set of the OCL language, instead of raw OCL.
Viewpoint allows you to provide your own language implementations, if you have very specific needs or want to reuse an existing custom language. Note that Viewpoint must be able to syntactically and unambiguously determine from an expression which language it is written in (to send it to the appropriate interpreter). The simplest way to achieve this is to define a prefix (like
ocl:
for OCL) so that expressions written in your language can be distinguished from expressions written in any of the others.
To provide your custom language, you must
at least implement the
fr.obeo.dsl.common.expressionInterpreter
extension point, and provide an implementation of the
fr.obeo.dsl.common.tools.api.interpreter.IInterpreter
interface. The example below shows how the Acceleo interpreter is registered. The
AcceleoMTLInterpreterProvider
is the one which implements the
IInterpreter
interface.
<extension point="fr.obeo.dsl.common.expressionInterpreter"
id="fr.obeo.dsl.common.acceleo.mtl.AcceleoMTLInterpreter">
<expressionInterpreterProvider
interpreterProviderClass="fr.obeo.dsl.common.acceleo.mtl.business.internal.interpreter.AcceleoMTLInterpreterProvider" />
</extension>
You can also optionally provide support for auto-completion for your language by implementing the
fr.obeo.dsl.common.proposalProvider
extension point and providing an implementation of
fr.obeo.dsl.common.tools.api.contentassist.IProposalProvider
interface. For example, here is how the completion support for Acceleo is registered (
AcceleoProposalProvider
is the class which implements the
IProposalProvider
interface):
<extension point="fr.obeo.dsl.common.proposalProvider">
<proposalProvider
class="fr.obeo.dsl.common.acceleo.mtl.ide.AcceleoProposalProvider"
interpreter="fr.obeo.dsl.common.acceleo.mtl.AcceleoMTLInterpreter" />
</extension>
Both Acceleo and the legacy language (which is an older, incompatible version of Acceleo) support the notion of Java services, which are methods written in Java that can be transparently invoked from interpreted expressions in Viewpoint. All you need to do is to create a Java class whose methods follow some conventions (described below) in your Viewpoint Specification Project, and declare the class (using its fully qualified Java name) in the VSM. You can then use the services defined in that class in any of you interpreted expressions written in either Acceleo or the legacy language.
A service is simply a public Java methods which follows some conventions:
EObject
or a sub-type of
EObject
).
EObject
or a sub-type)
Here is an example of a Java service taken from the
com.obeodesigner.sample.family.design
example (you can obtain the project’s sources using
New... > Example... > Obeo Designer > Family Designer):
public class FamilyServices {
public List<Family> getFamiliesContainingParents(Family currentFamily) {
List<Family> familiesContainingParents = new ArrayList<Family>();
// The service code.
return familiesContainingParents;
}
}
Once a service has been defined and its class is registered in your VSM, you can invoke it in the expressions of languages which support it (Acceleo and the legacy language) like this: [aFamily.getFamiliesContainingParents()/]
. The invocation looks as if the service was a normal feature of the
Family
type. When the service is invoked, the model element on which it is invoked is used as the first argument to the Java method. If arguments are passed in the expression, they are mapped to the second, third, etc. parameters of the Java method, assuming the types are compatible. The result of evaluating a service invocation is the result of the Java method.
Warning: Java service methods should be stateless. There is no guarantee that two successive invocations of the same service method on two model elements (or even on the same one) will use the same instance of the service class.
Warning: To test a Java service invoked through a legacy language expression, you have to launch an Eclipse runtime or to type its parameters with
EObject
and then cast them to your domain types in the service body.The legacy language interpreter is not able to retrieve non-deployed service with domain types parameters.
Warning: There are currently some limitations on the use of Java services from Acceleo:
Integer
instead of
int
).
void
. You can return any value instead, for example target argument (the service method’s first parameter).