openArchitectureWare delivers a set of small useful utility
extensions and components in the
org.openarchitectureware.util.stdlib
package. A
dependency to this plugin is already added and reexported by the
org.openarchitectureware.dependencies
plugin, so you
don't need to add an explicit dependency to stdlib.
This section describes the components and extensions provided by
Stdlib. We use the shortcut "oaw.util.stdlib...
"
for component classes in package
"org.openarchitectureware.util.stdlib
" in workflow
configurations for convenience.
Note that many functions of the Stdlib make use of static variables in their Java implementation, thus the values are kept through a complete oAW workflow. Also, because of the static implementation, the features are not threadsafe.
This is an extremely useful library to print information to the logging facility. It is really valuable through transformation processes or for complex expressions to know what exactly expressions are evaluated to.
Extension:
org::openarchitectureware::util::stdlib::io
Logs an object with DEBUG level to the logger.
Parameters:
o - The object to dump.
Returns: The object o
Logs an object with INFO level to the logger.
Parameters:
o - The object to dump.
Returns: The object o
Logs an object with ERROR level to the logger.
Parameters:
o - The object to dump.
Returns: The object o
Prints an object to System.err.
Parameters:
o - The object that should be printed. null is allowed.
Returns: The object o
Prints an Object to stderr with a prefix string.
Parameters:
o - The object that should be printed. null is allowed.
Returns: The object o
import data; extension org::openarchitectureware::util::stdlib::io; create DataModel this duplicate(DataModel s): entity.addAll( s.entity.duplicate() ) -> setName(s.name); create Entity this duplicate(Entity old): (old.name+" has "+old.reference.size+" references").info() -> old.reference.name.info() ->
This leads to the following output on the console:
922 INFO - Person has 1 references 923 INFO - [autos] 926 INFO - Vehicle has 0 references 926 INFO - []
Of course IO extension functions can also be used within Xpand, but if used for logging purposes you have to deal with one side effect: Since the functions return the passed object (the result of an expression, in the simplest case just a string) and Xpand prints out expression results to the opened file, the message will be shown on the console, but also be in the result file. This you might want to avoid, so you can use a small trick for this: after calling a log function use the chaining operator and let the result of the expression be an empty string:
«EXTENSION org::openarchitectureware::util::stdlib::io» ... «DEFINE javaClass FOR Entity» «REM»The following expression will dump the feature names without producing output as side effect«ENDREM» «features.name.info() -> ""»
This will produce this output on the console:
1122 INFO IOExtensions - [name, age, address] 1740 INFO IOExtensions - [street, zip, city]
Each function returns the object on which they have been called, so you can build chain expressions. Or, in other words, if you have some expression like
element.x.y.z.select(t|t.someProp).a
you can always embed one of these io functions anywhere such as in
element.x.syserr().y.z.select(t|t.someProp.info()).a
You may want to control the logging level for the messages which are printed via the logging facility. How this is configured in detail depends on the underlying logging framework. openArchitectureWare uses the Apache Commons Logging library, which may dispatches to another logging framework, mostly Log4J.
To control the logging level exactly for the IO extensions you
have to know the category to which the messages are logged to. It is
common to use the class names of the classes that use the logger. In
the case of the IO extensions this class is
org.openarchitectureware.util.stdlib.IOExtensions
.
The following example shows a Log4J configuration file which
would disable log levels below warning. This example would only work
if the properties file is found at the beginning of the classpath.
Make sure that the file would be found before any other Log4J
configurations on your classpath. The file must be named
log4j.properties
.
log4j.appender.CONSOLE = org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout = org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern = %p %C{1} %m %n log4j.rootLogger = INFO, CONSOLE # suppress info messages from IOExtensions log4j.logger.org.openarchitectureware.util.stdlib.IOExtensions=WARN, CONSOLE log4j.additivity.org.openarchitectureware.util.stdlib.IOExtensions=false
Sometimes it is necessary to have counters within transformation code. The counter extensions enable to initialize, manipulate and retrieve counters.
Extension:
org::openarchitectureware::util::stdlib::counter
Increments a counter.
Parameters:
o - A key for this counter. If this function is called with a Null argument an anonymous counter is used. If no counter was registered for the key a new counter instance will be created and initialized with 0.
Returns: The incremented counter.
Decrements a counter.
Parameters:
o - A key for this counter. If this function is called with a Null argument an anonymous counter is used. If no counter was registered for the key a new counter instance will be created and initialized with 0.
Returns: The decremented counter.
Resets a counter.
Parameters:
o - A key for this counter. If this function is called with a Null argument an anonymous counter is used. If no counter was registered for the key a new counter instance will be created and initialized with 0.
Returns: Always 0.
«DEFINE CounterExtensionsDemo FOR Object» «FILE "CounterExtensions.txt"» First counter: get : «counterGet()» inc : «counterInc()» inc : «counterInc()» inc : «counterInc()» dec : «counterDec()» Second (named) counter: inc : «counterInc("idx")» inc : «counterInc("idx")» inc : «counterInc("idx")» reset : «counterReset("idx")» inc : «counterInc("idx")» First counter: inc : «counterInc()» «ENDFILE» «ENDDEFINE»
This example will create the following output:
First counter: get : 0 inc : 1 inc : 2 inc : 3 dec : 2 Second (named) counter: inc : 1 inc : 2 inc : 3 reset : 0 inc : 1 First counter: inc : 3
You might want to specify configuration values from properties files from your transformation code. The Properties extensions can help you there. Before being able to access the properties through an extension function the properties files must be read and its values stored. This is done through the workflow component PropertiesReader, which is described below.
Extension:
org::openarchitectureware::util::stdlib::properties
The workflow component PropertiesReader is used to load properties files. It is possible to configure multiple properties files by adding the propertiesFile tag multiple times.
Table 26. Workflow component org.openarchitectureware.util.stdlib.PropertiesReader
Property | Type | Mandatory | Description |
---|---|---|---|
propertiesFile | String | yes | The properties file to read. |
Workflow configuration:
<component class="oaw.util.stdlib.PropertiesReader"> <propertiesFile value="src/config1.properties"/> <propertiesFile value="src/config2.properties"/> </component>
config1.properties
:
shapes = box,polygon,ellipse,point
Usage in an extension:
extension org::openarchitectureware::util::stdlib::properties; cached List[String] SHAPES () : getProperty("shapes").split(",").trim();
This allows you to temporarily associate name-value pairs with any model element.
Extension:
org::openarchitectureware::util::stdlib::elementprops
Sets the property named name to the value.
Parameters:
element - The model element.
name - Property name.
value - The property value.
Returns: nothing.
In template code there is no direct access to the Issues instance of the workflow's context possible. The Issues extensions help to report warnings and errors to the Issues instance during transformation.
This should not encourage you to use constraint checking and generally raise errors directly from within the transformations. However, sometimes it is sensible and useful to be able to do that.
Extension: org::openarchitectureware::util::stdlib::issues
Reports a warning message to the workflow context.
Parameters:
message - A message.
Returns: The message.
Reports a warning message and the qualified name of a context object to the workflow context.
Parameters:
object - A context object.
message - A message.
Returns: The message.
Reports a error message to the workflow context.
Parameters:
message - A message.
Returns: The message.
The Issues extensions require that the workflow component
org.openarchitectureware.util.stdlib.ExtIssueReporter
is configured in the workflow before calling the extensions. The
purpose of this component is make the workflow's Issues instance
available for the extensions.
The ExtIssueReporter
component does not
have any properties.
Workflow configuration:
<?xml version="1.0"?> <workflow> ... <component class="oaw.util.stdlib.ExtIssueReporter"/>
Using from Xtend:
import metamodel; extension org::openarchitectureware::util::stdlib::issues; demo (Model this) : issuesExtensionsDemo() ; issuesExtensionsDemo () : reportWarning("Reporting a warn message from Xtend to the workflow");
Console output:
INFO WorkflowRunner running workflow: workflow/generator.oaw ... ... INFO CompositeComponent ExtIssueReporter: setting up issue logging from within .ext and .xpt files INFO WorkflowRunner workflow completed in 1101ms! WARN WorkflowRunner Reporting a warn message from Xtend to the workflow
The Naming extensions are only usable with EMF models.
This one helps with names, qualified names and namespaces. A qualified name is defined as the seuqence of primitive names of the containment hierarchy of an element, seperated by a dot (e.g. java.lang.String). In order for this to work, model elements are expected to have a name attribute of type EString.[9]
Extension:
org::openarchitectureware::util::stdlib::naming
Returns the namespace, i.e. the qualified name minus the name of the element itself.
Parameters:
this - A model element.
Returns: The qualified namespace name of the element.
Returns the qualified name (dot separated) of an element by evaluating its containment hierarchy.
Parameters:
this - A model element.
Returns: The qualified name of the element.
Tries to build a useful description of an element in the model; very useful for error reporting.
Parameters:
this - A model element.
Returns: Location information about the element.
Searches the candidates for an element with a specific name.
Parameters:
candidates - A collection of model elements.
name - The searched element name.
Returns: The searched element or null if no element with that name is contained in the candidates collection.
Sometimes you might want to share information within a transformation process. One alternative is the use of GLOBALVAR expressions, but this needs that the variables are configured in the workflow.
The Globalvar extensions help to store and retrieve objects within a transformation process.
Extension:
org::openarchitectureware::util::stdlib::globalvar
Stores an object.
Parameters:
s - A key.
o - The object to store. Pass null to remove the global var.
Returns: The object.
Retrieves a stored object.
Parameters:
s - The key under which the object is stored.
Returns: The stored object or null if no object was stored for the key.
Usage in Xtend:
import metamodel; extension org::openarchitectureware::util::stdlib::io; extension org::openarchitectureware::util::stdlib::globalvar; demo (Model this) : globalvarExtensionsDemo1() -> globalvarExtensionsDemo2() ; globalvarExtensionsDemo1 () : "Storing global var...".info() -> storeGlobalVar("msg", "oAW is cool stuff!"); globalvarExtensionsDemo2 () : ("Getting message from global var: "+getGlobalVar("msg")).info();
Console output:
INFO IOExtensions Storing global var... INFO IOExtensions Getting message from global var: oAW is cool stuff!
This a simple example storing a string, but of course you can store the result of any expression this way.
The cloning utilities help you to clone a model element and all
its children. The clone(Object)
function clones
a single object and its children, whereas the
clone(List)
clones a list of elements. The
semantics of cloning is as follows:
the object passed in as a parameter is duplicated
all objects referenced via containment references are also duplicated, recursively
the values of the attributes are duplicated
non-containing references to other objects are copied while the target is not cloned (a reference to the original is created in the new object)
Extension
org::openarchitectureware::util::stdlib::cloning
Clones an object.
Parameters:
original - The object that should be cloned.
Returns: The cloned object.
Sometimes there is the need to find objects that reference a specific object. This extension helps to solve this recurring task. This extension can only be used for EMF based models.
Extension:
org::openarchitectureware::util::stdlib::crossref
Usage in Xtend:
extension org::openarchitectureware::util::stdlib::crossref; crossRefDemo (Model this) : eAllContents.typeSelect(Datatype).dumpCrossReferences(); dumpCrossReferences (Datatype this) : ("Number of cross references to datatype "+name+":" + getReferencingObjects().size) .info() ;
Console output:
INFO IOExtensions Number of cross references to datatype Integer:1 INFO IOExtensions Number of cross references to datatype String:4
Often it is required to create and retrieve unique identifiers for objects through the transformation process. The UID extensions provide a simple mechanism for this task. Unique identifiers are calculated from the current system time plus an internal counter. The extensions therefore only guarantee that the identifier stays the same within one workflow execution, but will change through different runs. If you need to have unique identifiers that stay the same over every generation run (e.g. for Protected Regions Ids) then you need another mechanism.
If you are loading the model that assigns IDs to EObject (only for
EMF based models) the xmlId()
function will be
useful. Especially when using UML2 models this function will return a
unique and non-changing identifier for objects.
Extension:
org::openarchitectureware::util::stdlib::uid
Retrieves objects that reference a given object.
Parameters:
target - The target object.
Returns: A list of objects referencing the target.
Creates a unique identifier for an object.
Parameters:
target - The target object.
Returns: A list of objects referencing the target.
These utilities help with mixin models. Mixin models are typically simple models that provide additional information about model elements in a source model of a transformation. They can be seen as annotations.
These utilities expect that the mixin models have a very specific structure: A root element, and then any subtree, where the elements have a name attribute. Here's an example:
The mixin elements are
ControllingServiceRefSpec
and
BundleSpec
. They are owned by the root element,
Cbd2OsgiMixin
. The name is expected to contain
the qualified name of the element the annotation refers to. Once the
model is set up like this, and made available to a transformation using
the workflow's GLOBALVAR facilities, you can then use the extension
functions.
Extension:
org::openarchitectureware::util::stdlib::mixin
Returns the corresponding mixin element for the context object; the mixin must be of type t and its name attribute must correspond to the qualified name of the context. If none is found, a workflow ERROR is raised and a null object is returned (so you can call additional operations on it without getting a null evaluation error).
Parameters:
mixinModel - The root element of the mixin model.
ctx - The context object.
t - The type of the mixin model element.
Returns: The mixin model element corresponding to ctx.
The tracing extensions allow to create trace paths during your model transformations. This is done by creating a trace model which holds references from source to target elements. Traces must be added explicitly to the transformation code.
Extension
org::openarchitectureware::util::stdlib::tracing
Creates a trace between two elements.
Parameters:
from - Source element.
to - Target element.
kind - Name for the trace from source to target.
backkind - Name for the trace from target back to source.
Returns: Nothing.
Creates a trace between two elements.
Parameters:
from - Source element.
to - Target element.
kind - Name for the trace.
Returns: Nothing.
Finds the target of a trace. This function will report an error if no trace for the source element to the target of the specified kind can be found.
Parameters:
from - Source element.
kind - Trace kind name.
Returns: The target element of that trace.
Besides the extensions described in the previous section oAW's Stdlib provides some workflow components.
This component executes a system command.
Table 27. Workflow component org.openarchitectureware.util.stdlib.SystemCommand
Property | Type | Mandatory | Description |
---|---|---|---|
command | String | yes | The command to execute. |
directory | String | no | Execution directory. |
arg | String | no | (multiple) command arguments |
env | String | no | (multiple) additional environment entries. Format: [key],[value] |
Example:
<component class="oaw.util.stdlib.SystemCommand"> <directory value="src-gen"/> <command value="sh"/> <arg value="processdot.sh"/> </component>
Console output:
1639 INFO - Running command '[sh, processdot.sh]' in directory [absolutepath] ... 1667 INFO - processing shape_box.dot ... 2597 INFO - processing shape_polygon.dot ... ... 3564 INFO - Execution of command was successful.
Windows tip:
When executing a command on windows this is typically done with the cmd as command value. It is important that the command terminates, therefore the argument /c must be appended as arg value.[10]
This component copies an element from one slot to another. The slot content is not cloned.
Table 28. Workflow component org.openarchitectureware.util.stdlib.SlotCopier
Property | Type | Mandatory | Description |
---|---|---|---|
fromSlot | String | yes | Source slot name. |
toSlot | String | yes | Destination slot name. |
removeTopLevelList | boolean | no | If true the source slot must contain a list and the top level list is removed (i.e. the first element from the list is copied to the destination slot), otherwise it is not removed. |
Example:
<component class="oaw.util.stdlib.SlotCopier"> <fromSlot value="model"/> <toSlot value="target"/> </component>
Console output:
INFO SlotCopier copying org.eclipse.emf.ecore.impl.DynamicEObjectImpl@1fdbef (eClass: org.eclipse.emf.ecore.impl.EClassImpl@fc5b01 (name: Model) (instanceClassName: null) (abstract: false, interface: false)) [org.eclipse.emf.ecore.impl.DynamicEObjectImpl]
This component copies an element from one slot to a list contained in another slot.
Table 29. Workflow component org.openarchitectureware.util.stdlib.SlotListAdder
Property | Type | Mandatory | Description |
---|---|---|---|
modelSlot | String | yes | Source slot name. |
listSlot | String | yes | Target slot name. This slot contains a list of elements. |
uniqueNames | boolean | no | If true, names have to be unique, otherwise not. Requires that modelSlot contains an EObject. |
Example:
This example adds the content of slot 'model' to the slot 'targetList'. The slot 'targetList' does not contain anything at the time of execution.
<component class="oaw.util.stdlib.SlotListAdder"> <modelSlot value="model"/> <listSlot value="targetList"/> </component>
Console output:
INFO CompositeComponent SlotListAdder: adding contents of slot 'model' to the list of stuff in 'targetList' ... ... INFO WorkflowRunner workflow completed in 1503ms! WARN WorkflowRunner 'targetList' is empty, creating a new list. [org.openarchitectureware.util.stdlib.SlotListAdder@7536e7]
Note that the warn messages will appear after the workflow finished, since they are reported as a workflow warn issue.
This component prints a workflow context slot content to the log. This can be useful for debugging purposes.
Table 30. Workflow component org.openarchitectureware.util.stdlib.SlotPrinter
Property | Type | Mandatory | Description |
---|---|---|---|
slotName | String | yes | The name of a slot whose content should be dumped. |
message | String | no | An optional message that will be prefixed to the log output. |
level | String | no | The log level for the message. Valid values are TRACE, DEBUG, INFO, WARN. |
Example:
<component class="oaw.util.stdlib.SlotPrinter"> <slotName value="model"/> <message value="DUMP"/> <level value="INFO"/> </component>
Console output:
INFO SlotPrinter DUMP: (slot: model)org.eclipse.emf.ecore.impl.DynamicEObjectImpl@d22ddb (eClass: org.eclipse.emf.ecore.impl.EClassImpl@fe0ce9 (name: Model) (instanceClassName: null) (abstract: false, interface: false))
[9] It is intended that the uml2ecore utility can add such a name attribute to every meta class automatically.