The abstraction layer on API basis is called a type system. It provides access to built-in types and different registered metamodel implementations. These registered metamodel implementations offer access to the types they provide. The first part of this documentation describes the type system. The expression sub-language is described afterwards in the second part of this documentation. This differentiation is necessary because the type system and the expression language are two different things. The type system is a kind of reflection layer, that can be extended with metamodel implementations. The expression language defines a concrete syntax for executable expressions, using the type system.
The Java API described here is located in the org.eclipse.xpand.type package and is a part of the subproject core.expressions.
Every object (e.g. model elements, values, etc.) has a type. A type contains properties and operations. In addition it might inherit from other types (multiple inheritance is also possible, depending on the underlying meta-meta-model).
Types have a simple name (e.g. String
)
and an optional namespace used to distingish between two types with
the same name (e.g. my::metamodel
). The
delimiter for name space fragments is a double colon
"::
". A fully qualified name looks like this:
my::fully::qualified::MetaType
The namespace and name used by a specific type is defined by the
corresponding MetaModel
implementation. The EmfMetaModel
, for instance, maps
EPackages
to namespace and
EClassifiers
to names. Therefore, the name of the Ecore element
EClassifier
is called:
ecore::EClassifier
If you do not want to use namespaces (for whatever reason), you can always implement your own metamodel and map the names accordingly.
The built-in type system also contains the following collection
types: Collection
,
List
and Set
. Because
the expressions language is statically type checked and we do not like
casts and ClassCastExceptions
, we introduced
the concept of
parameterized types
. The type
system does not support full featured generics, because we do not need
them.
The syntax is:
Collection[my::Type] List[my::Type] Set[my::Type]
Each type offers features. The type (resp. the metamodel) is responsible for mapping the features. There are three different kinds of features:
Properties
Operations
Static properties
Properties are straight forward: They have a name and a type. They can be invoked on instances of the corresponding type. The same is true for Operations . But in contrast to properties, they can have parameters. Static properties are the equivalent to enums or constants. They must be invoked statically and they do not have parameters.
As mentioned before, the expressions framework has several built-in types that define operations and properties. In the following, we will give a rough overview of the types and their features. We will not document all of the operations here, because the built-in types will evolve over time and we want to derive the documentation from the implementation (model-driven, of course). For a complete reference, consult the generated API documentation.
Object
defines a couple of basic
operations, like equals()
. The property
metaType provides access to Xpand's type for that Object. Every type
has to extend Object
.
The Void
type can be specified as the
return type for operations, although it is not recommended, because
whenever possible expressions should be free of side effects whenever
possible. The only possible value is null
.
Sometimes it might be useful to use Void
as an
parameter type, if you want to be able to call a function for
different argument types and also supply a valid implementation when
the function is invoked with null
.
The type system doesn't have a concept data type. Data types are
just types. As in OCL, we support the following types:
String
, Boolean
,
Integer
, Real
.
String
: A rich and convenient
String
library is especially important
for code generation. The type system supports the '+' operator
for concatenation, the usual
java.lang.String
operations
(length()
, etc.) and some special
operations (like toFirstUpper()
,
toFirstLower()
, regular expressions,
etc. often needed in code generation templates).
Boolean
: Boolean
offers the usual
operators (Java syntax): &&, ||, !, etc.
Integer
and Real
: Integer
and
Real
offer the usual compare operators
(<,>,<=,>=) and simple arithmetics (+,-,*,/). Note
that
Integer
extends
Real
!
The type system has three different Collection types.
Collection
is the base type, it provides several operations known
from java.util.Collection
. The other two types
(List
, Set
) correspond to their
java.util equivalents, too.
The type system describes itself, hence, there are types for the
different concepts. These types are needed for reflective programming.
To avoid confusion with metatypes with the same name (it is not
unusual to have a metatype called Operation
,
for instance) we have prefixed all of the types with the namespace
xpand2
. We have:
xpand2::Type
xpand2::Feature
xpand2::Property
xpand2::StaticProperty
xpand2::Operation
You should be aware that if you name a type by name in an
expression the object you get is in fact an
xpand2::Type
. A common use case is to prove
that an object is of some type or its subtype, using the
instanceOf()
operation or exactly of one
type.
// results to true, if the result of someExpression is of type MyType or its subtypes MyType.isInstance(someExpression) // results to true, if the result of someExpression is exactly of type MyType someExpression.metaType == MyType
Note that this should be only used when really required. The recommended way to handle alternative implementations for a type hierarchy is using Multiple Dispatch.
By default, the type system only knows the built-in types. In order to register your own
metatypes (e.g. Entity
or
State
), you need to register a respective
metamodel implementation with the type system. Within a metamodel
implementation the
Xpand
type system elements
(Type
, Property
,
Operation)
are mapped to an arbitrary other type system (e.g. Java
reflections, Ecore or XML Schema).
For instance, if you want to have the following JavaBean act as a metatype (i.e. your model contains instances of the type):
public class Attribute { private String name; private String type; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } }
You need to use the JavaMetaModel
implementation which uses the ordinary Java reflection
layer in order to map access to the model.
So, if you have the following expression in e.g. Xpand :
myattr.name.toFirstUpper()
and myattr
is the name of a local variable
pointing to an instance of Attribute
. The
Xpand
type system asks the metamodel
implementations, if they 'know' a type for the instance of Attribute.
If you have the JavaMetaModel
registered it
will return an xpand2::Type
which maps to the
underlying Java class. When the type is asked if it knows a property
'name
', it will inspect the Java class using the
Java reflection API.
The JavaMetaModel implementation shipped with
Xpand
can be configured with a strategy
[GOF95-Pattern] in order to control or change the mapping. For
instance, the JavaBeansStrategy
maps getter and
setter methods to simple properties, so we would use this strategy for
the example above.
You should know that for each Metamodel
implementation you use at runtime, you need to have a so
called MetamodelContributor
extension for the
plugins to work with. If you just use one of the standard metamodel
implementations (EMF, UML2 or Java) you don't have to worry about it,
since
Xpand
is shipped with respective
MetamodelContributors (see the corresponding docs for details). If you
need to implement your own
MetamodelContributor
you should have a look at the Eclipse plug-in reference
doc.
You need to configure your Xpand language components with the respective metamodel implementations.
A possible configuration of the Xpand2
generator component looks like this:
<component class="org.eclipse.xpand2.Generator"> <metaModel class="org.eclipse.type.emf.EmfMetaModel"> <metaModelPackage value="my.generated.MetaModel1Package"/> </metaModel> <metaModel class="org.eclipse.type.emf.EmfMetaModel"> <metaModelFile value="my/java/package/metamodel2.ecore"/> </metaModel> ... </component>
In this example the EmfMetaModel
implementation is configured two times. This means that we want to use
two metamodels at the same time, both based on EMF. The
metaModelPackage property is a property that is
specific to the EmfMetaModel
(located in the
org.eclipse.xtend.typesystem.emf
plugin). It
points to the generated EPackages
interface.
The second meta model is configured using the Ecore file. You do no
need to have a generated Ecore model for
Xpand
in
order to work. The EmfMetaModel
works with
dynamic EMF models just as it works with generated EMF models.
Note that is recommended to prefer the
EmfRegistryMetaModel
instead of the
EmfMetaModel
, although
EmfMetaModel
is derived from the
EmfRegistryMetaModel
. Further it is recommended
to use platform URIs (see API
Doc URI) to refer to EMF resources.
The use of platform URIs in the workflow requires setting up EMF
for standalone execution with the
StandaloneSetup
class from the
org.eclipse.emf.mwe.utils
plugin. Further,
StandaloneSetup is used to register known EMF packages. An equivalent
workflow configuration for the sample above would look like
this:
<bean class="org.eclipse.emf.mwe.utils.StandaloneSetup"> <platformUri value=".."/> <registerGeneratedEPackage value="my.generated.MetaModel1Package"/> <registerEcoreFile value="platform:/resource/my/java/package/metamodel2.ecore"/> </bean> ... <component class="org.eclipse.xpand2.Generator"> <metaModel class="org.eclipse.type.emf.EmfRegistryMetaModel"/> ... </component>
The StandaloneSetup
is given the path
to the platform. This is the path to which platform resource URIs are
resolved relative to. It usually points to the workspace or check out
location of the plugin project, which is usually one directory above
the working directory in which a workflow is executed.
Metamodel instances are often shared between different
components that make use of expressions (most notably the Xpand
Generator
,
XtendComponent
and
CheckComponent
). Normally you don't want that a
Metamodel instance configured and instantiated for each workflow
component. MWE lets you instantiate a class using the
<bean>
tag and by giving the bean an id
value, this same instance can be referred to using the idRef
attribute. This would lead to this workflow:
<bean class="org.eclipse.emf.mwe.utils.StandaloneSetup"> <platformUri value=".."/> <registerGeneratedEPackage value="my.generated.MetaModel1Package"/> <registerEcoreFile value="platform:/resource/my/java/package/metamodel2.ecore"/> </bean> <bean id="mm_emf" class="org.eclipse.type.emf.EmfRegistryMetaModel"/> ... <component class="org.eclipse.xpand2.Generator"> <metaModel idRef="mm_emf"/> ... </component>
With Xpand you can work on different kinds of Model representations at the same time in a transparent manner. One can work with EMF models, XML DOM models, and simple JavaBeans in the same Xpand template. You just need to configure the respective MetaModel implementations.
If you want to do so you need to know how the type lookup works. Let us assume that we have an EMF metamodel and a model based on some Java classes. Then the following would be a possible configuration:
<component class="org.eclipse.xpand2.Generator"> <metaModel class="org.eclipse.internal.xtend.type.impl.java.JavaMetaModel"/> <metaModel class="org.eclipse.xtend.typesystem.emf.EmfRegistryMetaModel"> <metaModelFile value="my/java/package/metamodel.ecore"/> </metaModel> ... </component>
When the runtime needs to access a property of a given object, it
asks the metamodels in the configured order. Let us assume that our
model element is an instance of the Java type
org.eclipse.emf.ecore.EObject
and it is a dynamic
instance of an EMF EClass MyType
.
We have three Metamodels:
The first one will return the type Object
(not java.lang.Object
but
Object
of
Xpand
!). At this
point the type Object
best fits the request, so
it will act as the desired type.
The second metamodel returns a type called
org::eclipse::emf::ecore::EObject
The type system
will check if the returned type is a specialization of the current
'best-fit' type (Object
). It is, because it
extends Object
(Every metatype has to extend
Object
). At this time the type system assumes
org::eclipse::emf::ecore::EObject
to be the
desired type.
The third metamodel will return
metamodel::MyType
which is the desired type. But
unfortunately it doesn't extend
org::eclipse::emf::ecore::EObject
as it has
nothing to do with those Java types. Instead it extends
emf::EObject
which extends
Object
.
We need to swap the configuration of the two metamodels to get the desired type.
<component class="org.eclipse.xpand2.Generator"> <metaModel class="org.eclipse.xtend.typesystem.emf.EmfMetaModel"> <metaModelFile value="my/java/package/metamodel.ecore"/> </metaModel> <metaModel class="org.eclipse.internal.xtend.type.impl.java.JavaMetaModel"/> ... </component>
The order of the metamodels is important for the work within the Xpand-editors. The metamodels to work with can be configured inside the Xtend/Xpand -properties dialog. The Activated metamodel contributors table is a ordered list. The more specific metamodels have to be placed at the top of the list.
In the following, each of the built-in metamodels that come with Xpand will be documented. Furthermore, there will be some guidelines on how to implement your own metamodel.
This section will describe the metamodels that can be used for
EMF models.
Please note that you have to
execute one of the setup utility classes, Setup
or StandaloneSetup
, in your workflow before you
can use one of the EMF metamodels.
This metamodel looks for the referenced metamodels in the global EMF model registry. This means, when using this metamodel, only models that are registered in this global EMF model registry will be accessible from within the metamodel.
This metamodel provides the following configuration property:
Table 1. Properties of
EmfRegistryMetaModel
Name of property | Description |
---|---|
useSingleGlobalResourceSet
|
This boolean property determines the way resource sets are used. If set to true , all model resources will be stored in a single global resource set. Otherwise, a separate resource set will be used for each model resource. |
This metamodel is a specialized version of the EMF registry metamodel. In addition to the features of the former, it allows to specify an unregistered model in different ways that will be added to the metamodel.
This metamodel provides the following configuration properties:
Table 2. Properties of EmfMetaModel
Name of property | Description |
---|---|
useSingleGlobalResourceSet
|
This boolean property determines the way resource sets are used. If set to true , all model resources will be stored in a single global resource set. Otherwise, a separate resource set will be used for each model resource. |
metaModelFile
|
Sets the path to the Ecore file that will be added to the metamodel. |
metaModelDescriptor
|
Adds a model to the metamodel by specifying the name of an EPackage descriptor class. |
metaModelPackage
|
Adds a model to the metamodel by specifying the name of an EPackage. |
Xpand
also provides several metamodels that
allow to use UML models in conjunction with this model-to-text
generation framework.
Please note that you
have to execute the setup utility class Setup
in your workflow before you can use one of the UML
metamodels
This metamodel is a specialized version of the EMF metamodel . It provides access to UML2 models, and it has the following configuration properties:
Table 3. Properties of UML2MetaModel
Name of property | Description |
---|---|
useSingleGlobalResourceSet
|
This boolean property determines the way resource sets are used. If set to true , all model resources will be stored in a single global resource set. Otherwise, a separate resource set will be used for each model resource. |
modelFile
|
Sets the path to the UML2 model file that will be added to the metamodel. |
This implementation will be rarely used, since usually profiled UML models will be used and therefore the Profile Metamodel is.
This metamodel allows to apply UML profiles to UML2 models, and extends the UML2 Metamodel. It has the following configuration properties:
Table 4. Properties of
ProfileMetaModel
Name of property | Description |
---|---|
useSingleGlobalResourceSet
|
This boolean property determines the way resource sets are used. If set to true , all model resources will be stored in a single global resource set. Otherwise, a separate resource set will be used for each model resource. |
modelFile
|
Sets the path to the UML2 model file that will be added to the metamodel. Use resource URIs for the values. |
profile
|
Sets the path to the UML profile that will be applied to the UML2 model. This property can be used multiple times if more than one profile is used. Use resource URIs for the values. |
The XMI reader component is important when working with UML models. It allows to read out a model stored in an XMI file and put its contents into a model slot.
The XMIReader
component provides the
following configurable properties:
Table 5. Properties of XMIReader
Name of property | Description |
---|---|
metaModelFile | Sets the path to the Ecore file that will be added to the metamodel. |
metaModelDescriptor | Adds a model to the metamodel by specifying the name of an EPackage descriptor class. |
metaModelPackage | Adds a model to the metamodel by specifying the name of an EPackage. |
outputSlot | Sets the name of the model slot where the read model will be stored in. |
firstElementOnly | This boolean property determines if only the first model element of the XMI file will be used. If set to true , only the first model element will be used, all other elements will be ignored. Otherwise, all model elements in the XMI file will be used. |
The Java metamodel allows normal Java classes as metatypes for
your metamodel. The JavaMetaClass
uses the
strategy pattern to define how the elements are exactly mapped to the
metamodel elements. There is a class called
org.eclipse.internal.xtend.type.impl.java.JavaBeansMetaModel
that is preconfigured with a strategy that maps simple Java beans onto
the metamodel elements.
The Java metamodel has no configurable properties.
The XSD metamodel provides access to models implemented in the XML Schema Definition language. It has the following configuration properties:
Table 6. Properties of XSDMetaModel
Name of property | Description |
---|---|
id
|
Sets the ID of the current model. |
registerPackagesGlobally
|
This boolean property determines if the model packages will be registered globally. If set to true , the model packages will be registered in the global registry. Otherwise, packages will not be registered. |
savePackagesPath
|
Sets the path where model packages will be saved (in XMI format). |
The Xpand framework also allows you to integrate new metamodel implementations. This section quickly outlines the steps that have to be taken in order to implement a metamodel:
Create a class that implements the
MetaModel
interface.
In order to be able to integrate your metamodel into the
Eclipse UI, you also have to provide a metamodel contributor class
for your metamodel implementation that implements either the
MetaModelContributor
or the
MetaModelContributor2
interface.
Finally, you have to extend the
org.eclipse.xtend.shared.ui.metaModelContributors
extension point in order to register your metamodel contributor
with the Eclipse UI.