A Usage
scenario
Create an
EMF project and an ecore model, say a Bees.ecore meta-model
containing a bees package, with classes Hive and Bee with a
containment relationship. You can access the bees package
with an xmiLoadModelElement :
js>
bees =
loadModelElement(
'platform:/resource/MyProject/model/Bees.ecore'
);
(EPackage) Bees
If some Java code has been generated for Bees.ecore and
this code is installed as a plugin in Eclipse, you can also
use getModelDef with the declared URI of the metamodel.
js>
bees =
getModelDef( 'http://metamodel/bee' );
(EPackage) bees
You
can create an instance model this way :
js>
hive =
bees.$Hive.create();
(Hive)
js> bee1 =
bees.$Bee.create();
(Bee)
js> bee2 =
bees.$Bee.create();
(Bee)
js> hive.bees.add( bee1
);
true
js> hive.bees.add( bee2
);
true
You
can save the instance model this way :
js>
saveModelElement(
'platform:/resource/MyProject/model/hive1.xmi', hive
);
However, if you want to load back a hive from hive.xmi, you
need to have generated code and installed it as a plugin
inside Eclipse.
js>
hive =
loadModelElement(
'platform:/resource/MyProject/model/hive1.xmi'
);
(Hive)
Commands specific to
EMFScript
listModelDef(
)
List all registered EMF packages. Such
a list is useful to use the getModelDef command.
Example :
js> listModelDef();
http://www.eclipse.org/emf/2004/Ecore2Ecore
http://www.eclipse.org/emf/2002/GenModel
http://www.eclipse.org/gmf/2005/ToolDefinition
http://www.eclipse.org/uml2/1.1.0/GenModel
http://www.eclipse.org/gmf/2005/GenModel
http://www.eclipse.org/emf/2003/XMLType
http://www.eclipse.org/uml2/2.0.0/UML
http://www.eclipse.org/emf/2003/Change
http://www.eclipse.org/xsd/2002/XSD
http://www.w3.org/XML/1998/namespace
http://www.eclipse.org/emf/2002/Tree
http://www.eclipse.org/gmf/2005/GraphicalDefinition
http://www.eclipse.org/emf/2002/Mapping
http://www.eclipse.org/emf/2005/Ecore2XML
http://www.eclipse.org/emf/2002/Ecore
http://www.eclipse.org/OCL2/1.0.0/ocl/query
http://www.eclipse.org/gmf/2005/mappings
http://www.eclipse.org/OCL2/1.0.0/ocl/expressions
getModelDef(
packageURI ) : EPackage
Import a model package from a bundle
installed in the Eclipse environment.
• packageURI is the URI of the model package to load. This
is generally one the string returned by listModelDef.
• The result is the package containing the model or null if
the package was not found.
Examples :
js> ecore =
getModelDef( 'http://www.eclipse.org/emf/2002/Ecore' );
(EPackage)
ecore
js> uml2 = getModelDef(
'http://www.eclipse.org/uml2/2.0.0/UML);
(EPackage) uml
registerModelDef( ePackage )
register a model package (model
definition) in the Eclipse environment.
• ePackage is the package to register
js> registerModelDef( uml2 );
http://www.eclipse.org/uml2/2.0.0/UML
loadModelElement(
strResName ) : EObject
Load a model element from its persisted
XMI or XML representation. Such a model element uses
dynamic EMF.
• strResName is a URI for an XMI resource. Any
kind of URIs can be used, included Eclipse platform URIs
such as :
'platform:/plugin/org.eclipse.emf.ecore/model/Ecore.ecore'
or
'platform:/resource/eu.telecomlille1.MyProject/model/MyModel.ecore'
• The result is the model element.
An exception stack trace appears if the resource was not
found, or was not a valid XMI/XML resource. An exception
stack trace also appears if attempt to load an instance
model for which no meta-model code was found.
Examples :
js>
eom = loadModelElement(
'platform:/resource/eu.telecomlille1.emf.eom/model/eom.ecore'
);
(EPackage) eom
saveModelElement(
strResName, modelElement )
Save a model element to an XMI
persisted representation.
• strResName is a URI for an XMI resource. Any
kind of URIs can be used, included Eclipse platform URIs
such as :
'platform:/resource/eu.telecomlille1.emf.eom/model/prj.xmi'
• modelElement is the model element to save
Examples :
js> saveModelElement(
'platform:/resource/eu.telecomlille1.emf.eom/model/prj.xmi',
prj );
MyProject
How to script a model
?
Navigation in a model
The knowledge of the meta-model is all you need to navigate
inside a model. For instance, if you want to navigate
inside a Bees.ecore meta-model, you must know about its
meta-model Ecore.ecore. For instance, if you want to
display the name of the bees package, you must know that
EPackage inherits from ENamedElement which has a name
attribute. If you want to access the factory associated
with the bees package, you must know that EPackage defines
a eFactoryInstance reference to EFactory.
js> bees =loadModelElement(
'platform:/resource/MyProject/model/Bees.ecore' );
(EPackage) Bees
js> bees.name;
bees
js> bees.eFactoryInstance;
(EFactory)
The JavaScript syntax allows the alternative following
syntax which is especially useful when the attribute or
reference name is a computed string.
js> bees['name'];
bees
js> bees['eFactoryInstance'];
(EFactory)
Multiple reference
Multivalued references are seen as some kind
of JavaScript arrays called EList. For instance, Ecore
defines the eClassifiers reference between EPackage and
EClassifier.
js> bees.eClassifiers;
(EList)
(EClass) Hive
(EClass) Bees
js> bees.eClassifiers[0];
(EClass)
Hive
Remind that, in JavaScript, the index of a for loop is not
an element of a set but the index of this element :
js> for (i in bees.eClassifiers)
print(i);
0
1
js>
for (i in bees.eClassifiers) {
print(bees.eClassifiers[i]);
}
(EClass) Hive
(EClass) Bees
An EList also supports OCL-like operators such as select.
Let's first declare a function which tells if an
EClassifier is an EClass.
js> function isEClass(clsf){
return clsf instanceof ecore.$EClass; // see below 'The $
facility'
}
You can use this function as an argument for the EList's
select operator :
js> bees.eClassifiers.select(isEClass);
(EList)
(EClass) Hive
(EClass) Bee
Current available operation on EList are:
any(function)
: returns any element from the input
Collection matching the filter
condition.
js> elt = eList.any( function(elt) { return
elt.value >= 0; } );
collect(function)
: builds an output collection by applying
a filter on each element from the input collection. The
returned
collection comprises the filter returned
objects.
js>
list = eList.collect(
function(elt) { return elt.name;} );
reject(function)
: builds an output collection from the
input collection elements NOT matching the filter
condition.
js> list = eList.reject( function(elt) {
return elt.value < 0;} );
exists(function)
: returns true if at least one element
from the input collection matches the filter
condition.
js> binit = eList.exists(function (elt) {
return elt.name == 'init'; } );
forAll(function)
: returns true if all the elements from
the input collection match the filter condition.
js> bAll = eList.forAll( function(elt) {
return elt.name != '';} );
apply(function)
: execute the procedure argument to every
elements from the input collection.
js> eList.apply( function(elt) {
print(elt.name); } );
includesAll(list)
: return true if all the elements from the
argument collection are included in this collection.
js> bAll = eList.includesAll( otherList);
excludesAll(list)
: return true if all the elements from the
argument collection are excluded from this
collection.
js> bAll = eList.excludesAll( otherList);
count(function)
: returns the number of elements matching
the filter condition.
js>
iCount = eList.count(
function(elt) { return elt.name=='init';} );
one(function)
: returns true if the number of elements
matching the filter condition is exactly 1.
js> bOne = eList.one(function(elt) { return
elt.name == 'init'; } );
isUnique(value,
function) : returns true if
the number of elements matching the value is exactly
1.
js> bInit = eList.isUnique( 'init', function
(elt) { return elt.name; } );
sum()
: returns the sum of this collection
values that must be numeric.
js> result = eList.sum();
iterate(resultValue,
function) : The OCL-like
'iterate' function implementation. The iterate function has
2 parameters : an initial 'result' value and a 'filter'
function. The filter function itself has 2 parameters : a
result and a list 'element'. The filter is applied to each
list element, it returns a new result computed from its
'result' and 'element' parameters. The filter is applied to
the first element with the initial result. The result
returned by a filter application becomes the result
parameter for application to the next element. The iterate
function returns the final 'result' value.
js> result = eList.iterate('',
function(result, e) { return result + e;} );
The $ facility
The $ facility provides an easy designation
mechanism for sub-elements. For instance, the eClassifier
reference and its opposite reference ePackage define an
association between EClassifier and EPackage. Because this
association is a containment relation, a classifier (i.e.
Bee) is a sub-element of a package (i.e. bees) and you can
navigate from bees to Bee by specifying bees.$Bee.
js> bees.$Bee;
(EClass)
Bee
In fact, this facility is restricted to named sub-elements.
For instance, eClassifiers is a containment reference to an
EClassifier and EClassifier inherits from ENamedElement.
Notice that the name of the containment reference
(eClassifiers) does'nt appear in the $ expression
(bees.$Bee) : any containment reference is involved in this
facility. Also notice that $Bee can be a computed string :
js> str='Bee';
js>
bees['$'+str];
(EClass) Bee
The create facility
The create facility abbreviates the
following creation script :
js> myEClass = ecore.eFactoryInstance.create(
ecore.$EClass );
(EClass) null
to :
js> myEClass =
ecore.$EClass.create();
(EClass) null
Invoking operations
Models described with Ecore define classes that may have
operations. If some Java code has been generated for this
meta-model, these operations can be invoked from
JavaScript.
js> bees =
getModelDef( 'http://metamodel/bee'
);
(EPackage)
bees
js> bee1 =
bees.$Bee.create(); // see below 'The create facility'
(Bee)
js> bee1.fly();
Buzz...
Notice that you must have imported the model with
importModelDef. An xmiLoadModel is not appropriate in this
case.
Providing operations behavior
If no code was generated for the Bees model,
you may want to prototype some operations behavior thanks
to the _body notation and a JavaScript function :
js> bees =loadModelElement(
'platform:/resource/MyProject/model/Bees.ecore' );
(EPackage) bees
js>
bee1 = bees.$Bee.create();
(Bee)
js>
bees.$Bee.$fly._body =
function() { print( 'Buzz, Buzz...' ); }
function () {
print("Buzz, Buzz...");
}
js>
bee2 = bees.$Bee.create();
(Bee)
js>
bee1.fly();
Buzz,
Buzz...
js> bee2.fly();
Buzz,
Buzz...
Notice that a bee can have been created previously to the
fly operation body definition.
Providing operations behavior through the
'emfscript/body' annotation
If you consider that the operation behavior should be part
of the model, you can want to provide it through an
annotation. EMFScript defines the
http://www.telecom-lille1.eu/2006/emfscript annotation
source. An annotation detail, key of which is body allows a
JavaScript function as a value. So, you should annotate an
eOperation this way :
js> eAnn = ecore.$EAnnotation.create();
(EAnnotation)
js> bees.$Bee.$fly.eAnnotations.add(eAnn);
true
js>
eAnn.source =
'http://www.telecom-lille1.eu/2006/emfscript';
http://www.telecom-lille1.eu/2006/emfscript
js> entry =
ecore.$EStringToStringMapEntry.create();
(EStringToStringMapEntry)
js>
eAnn.details.add( entry );
true
js>
entry.key='body';
body
js>
entry.value= "print( 'Buzz,
Buzz...' );";
print( 'Buzz,
Buzz...' );
Notice that the value is a string, not a function. Also
notice that you must not provide the function () {...}
wrapper for the body annotation code.
Then you can invoke the operation as usual :
js> bee1.fly();
Buzz,
Buzz...
After the first execution, the function is set as a _body
property of the EOperation. If the eOperation has
parameters, their name will appear in the function
parameters list.
js> bees.$Bee.$fly._body;
function () {
print( 'Buzz, Buzz...' );
}
You can also mix generated Java code and EMFScript/body
annotations. By default, Java code generated for an
operation is just a throw new
UnsupportedOperationException(); instruction :
/**
*
*
* @generated
*/
public
void fly() {
// TODO: implement this method
// Ensure that you remove @generated or mark it @generated
NOT
throw new
UnsupportedOperationException();
}
In this case, if a body annotation exists, it is executed.
No modification is needed to generated code.
Invoking EMFScript evaluation from generated Java code
The last possibility is to modify Java code generation so
that the evaluation of EMFScript is invoked :
/**
*
*
* @generated
*/
public
void fly() {
String strBody = "function() {
print( 'Buzz, Buzz...' ); }";
Context cx = Context.getCurrentContext();
Scriptable thisObj = (Scriptable)this;
Scriptable global = ScriptRuntime.getGlobal(cx);
Callable callable = cx.compileFunction( global, strBody,
"", 1, null );
callable.call(cx, global, thisObj, new Object[0] );
}
Ecore to JavaScript mappings
constructor
The JavaScript constructor clause provides
the object used to build an object. So :
js> tab = [];
js>
tab.constructor
function Array()
{ [native code for Array.Array, arity=1] }
Applied to an ecore object, constructor provides the same
information as the eClass() operation :
js> ecore.constructor
(EClass) EPackage
instanceof
In JavaScript, instanceof telles if an object was built
from another or not :
js> tab =
[];
js> tab.instanceof
Array
true
Applied to an ecore object, instanceof tells if it is an
instance of its meta-object or not :
js> ecore instanceof
ecore.$EPackage
true
Inheritance links are taken into account, for instance, the
result takes into consideration the fact that EPackage
inherits from ENamedElement :
js> ecore instanceof
ecore.$ENamedElement
true
Model decoration
EMFShell includes a decoration mechanism for model elements
based on JavaScript prototypes.
For instance, ecore can be decorated with a function which
displays an ENamedElement's name attribute. To do this, an
affName() function is attached to the ecore.$ENamedElement
prototype :
js> ecore.$ENamedElement.prototype.affName =
function() {
print( this.name );
}
function ()
{
print(this.name);
}
Every ENamedElement will benefit from the affName()
function. So, since the ecore package is itself a
ENamedElement :
js> ecore.affName();
ecore
Nothe the use of this to designate a ENamedElement instance
and from there to access its name attribute.
TrimPath templates with EMFShell
TrimPath JavaScript Templates (JST) is a template engine
written in JavaScript. It allows to generate code from
templates. Once compiled, a template is a JavaScript. The
EMFShell model decoration mechanism enables execution of
compiled templates attached to different model elements.
js> ecore.$ENamedElement.prototype.affName =
'Element name is ${name |ucfirst}'.asTemplate();
js>
ecore.affName();
Element name is Ecore
However, a TrimPath JST syntax limitation prevents the use
of braces as ordinary text inside a template source. Such a
template is not correct :
js> ecore.$EPackage.prototype.classes = '{for
c in eClassifiers.select(function(clsf){return clsf
instanceof ecore.$EClass;})} ${c.name}
{/for}'.asTemplate();
js:
"bundleentry://8/template.js#46(eval)", line 1: uncaught
JavaScript runtime exception: SyntaxError: il manque ')'
après une liste d'arguments ...
A workaround consists in replacing the argument of select
(an unnamed function) by a named function defined somewhere
else :
js> fClasses = function(clsf){return clsf
instanceof ecore.$EClass;};
function (clsf) {
return clsf instanceof ecore.$EClass;
}
js> ecore.$EPackage.prototype.classes = '{for
c in eClassifiers.select(fClasses)} ${c.name}
{/for}'.asTemplate();
...
js> ecore.classes();
EAttribute EAnnotation EClass EClassifier EDataType EEnum
EEnumLiteral EFactory EModelElement ENamedElement EObject
EOperation
EPackage EParameter EReference EStructuralFeature
ETypedElement EStringToStringMapEntry
In case of numerous functions such as fClasses, collision
risks can be limited by embedding these functions as model
decorations :
js> ecore.$EModelElement.prototype.fClasses =
function(clsf){return clsf instanceof ecore.$EClass;};
function (clsf) {
return clsf instanceof ecore.$EClass;
}
js> ecore.$EPackage.prototype.classes = '{for
c in eClassifiers.select(this.fClasses)} ${c.name}
{/for}'.asTemplate();
...
js> ecore.classes();
EAttribute
EAnnotation EClass EClassifier EDataType EEnum EEnumLiteral
EFactory EModelElement ENamedElement EObject EOperation
EPackage EParameter EReference EStructuralFeature
ETypedElement EStringToStringMapEntry
If the template source is long, it can located in a text
file that can be read thanks to the readUrl command.
A complete description of the TrimPath JST syntax can be
found at http://trimpath.com/project/wiki/JavaScriptTemplateSyntax.
Commands from the Rhino shell
The genuine Rhino shell also provides useful
functions such as readUrl : which can use any kind of URL,
included Eclipse-specific ones :
js> HiveMetamodel = readUrl(
'platform:/resource/MyProject/model/Bees.ecore' );
js>
serializedHive = readUrl(
'platform:/resource/MyProject/model/hive1.xmi' );
An exhaustive list of these commands can be found at :
http://www.mozilla.org/rhino/shell.html