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).
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 (http://www.openarchitectureware.org/api/built-ins/).
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 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
xpand
. We have:
xpand2::Type
xpand2::Feature
xpand2::Property
xpand2::StaticProperty
xpand2::Operation
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 (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
core.emftools
project). 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.
With Xpad 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.EmfMetaModel"> <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>