Language Reference

MWE2 has a few well defined concepts which can be combined to assemble arbitrary object graphs in a compact and declarative manner.

Let’s consider the follow short example module and SampleClass to explain these concepts.

module com.mycompany.Example

import java.util.*

SampleClass {
  singleValue = 'a string'
  multiValue = ArrayList {}
  child = {}

package com.mycompany;

import java.util.List;

public class SampleClass {
  public void setSingleValue(String value) {..}
  public void addMultiValue(List<?> value) {..}
  public void addChild(SampleClass value) {..}
}

Mapping to Java Classes

The module com.mycompany.Example defines a root component of type com.mycompany.SampleClass. It is possible to use the simple class-name because MWE2 uses the very same visibility rules as the Java compiler. Classes that are in the same package as the module can be referenced by their simple name. The same rule applies for classes from the java.lang package. For convenience reasons is the package org.eclipse.emf.mwe2.runtime.workflow implicitly imported as well as it exposes some library workflow components. However, the imports are more flexible then in Java since MWE2-imports can be relative, e.g. the import java.* resolves the reference util.ArrayList to java.util.ArrayList.

The root instance of type SampleClass has to be configured after it has been created. Therefore the method setSingleValue will be called at first. The given parameter is 'a string'. The method is identified by its name which starts with set. To allow to assign multi-value properties, MWE provides access to methods called add* as well.

If the right side of the assignment in the workflow file does not define a class explicitly, its type is inferred from the method parameter. The line child = {} is equivalent to child = SampleClass {} and creates a new instance of SampleClass.

MWE2 ships with nice tool support. The editor will provide content assist for the allowed types and highlight incompatible assignments. The available properties for Java classes will be proposed as well.

Module

As MWE2 modules have a fully qualified name, it is possible to refer to them from other modules. The type of the module is derived from the type of its root component. The com.mycompany.Example can be assigned at any place where a com.mycompany.SampleClass is expected.

Let’s create a second module com.mycompany.Second like this:

module com.mycompany.sub.Second

import com.mycompany.*

SampleClass {
  child = @Example {}

The child value will be assigned to an instance of SampleClass that is configured as in the first example workflow. This enables nice composition and a very focused, reusable component design.

As the same rules apply in MWE2 like in Java, the module com.mycompany.sub.Second has to be defined in a file called Second.mwe2 in the package com.mycompany.sub. The import semantic for other modules is the same as for classes. The import statement allows to refer to com.mycompany.Example with a shortened name.

Properties

MWE2 allows to extract arbitrary information into properties to ensure that these pieces are not cluttered around the workflow and to allow for easier external customization. The exemplary component definition was only changed slightly by introducing a property value.

module com.mycompany.Example

var value = 'a string'

SampleClass {
  singleValue = value
}

The type of the property will be derived from the default value similar to the mechanism that is already known from set- and add-methods. If no default value is given, java.lang.String will be assumed. However, properties are not limited to strings. The second built in type is boolean via the familiar literals true and false. More flexibility is available via actual component literals.

module com.mycompany.Example

var childInstance = SampleClass {
                      singleValue = "child"
                    }

SampleClass {
  child = childInstance
}

If one wants to define string properties that are actual reusable parts for other properties, she may use defined variables inside other literals like this:

var aString = "part"
var anotherString = "reuse the ${part} here"

This is especially useful for file paths in workflows as one would usually want to define some common root directories only ones in the workflow and reuse this fragment across certain other file locations.

Mandatory Properties

It is not always feasible to define default values for properties. That is where mandatory properties come into play. Modules define their interface not only via their fully qualified name and the type of the root component but also by means of the defined properties.

module com.mycompany.Example

var optional = 'a string'
var mandatory

SampleClass {
  singleValue = optional
  child = {
    singleValue = mandatory
  }
}

This version of the example module exposes two externally assignable properties. The second one has no default value assigned and is thereby considered to be mandatory. The mandatory value must be assigned if we reuse org.mycompany.Example in another module like this:

module com.mycompany.Second

var newMandatory

@Example {
  mandatory = "mandatoryValue"
  optional = newMandatory

Note that it is even possible to reuse another module as the root component of a new module. In this case we set the mandatory property of Example to a specific constant value while the previously optional value is now redefined as mandatory by means of a new property without a default value.

It is not only possible to define mandatory properties for MWE2 modules but for classes as well. Therefore MWE2 ships with the @Mandatory annotation. If a set- or add-method is marked as mandatory, the module validation will fail if no value was assigned to that feature.

Named Components

Properties are not the only way to define something that can be reused. It is possible to assign a name to any instantiated component whether it’s created from a class literal or from another component. This allows to refer to previously created and configured instances. Named instances can come handy for notification and call-back mechanisms or more general in terms of defined life-cycle events.

If we wanted to assign the created instance to a property of itself, we could use the following syntax:

module com.mycompany.Example

SampleClass : self {
  child = self
}

A named component can be referenced immediately after its creation but it is not possible to define forward references in a MWE2 file.

Auto Injection

Existing modules or classes often expose a set of properties that will be assigned to features of its root component or set- and add- methods respectively. In many cases its quite hard to come up with yet another name for the very same concept which leads to the situation where the properties itself have the very same name as the component’s feature. To avoid the overall repetition of assignments, MWE2 offers the possibility to use the auto-inject modifier on the component literal:

module com.mycompany.Example

var child = SampleClass {}

SampleClass auto-inject {
}

This example will implicitly assign the value of the property child to the feature child of the root component. This is especially useful for highly configurable workflows that expose dozens of optional parameters each of which can be assigned to one or more components.

The auto-inject modifier can be used for a subset of the available features as well. It will suppressed for the explicitly set values of a component.