Top

Inferring a JVM Model

In many cases, you will want your DSLs concepts to be usable as Java elements. E.g. an Entity will become a Java class and should be usable as such. In the domain model example, you can write

entity Employee extends Person {
    boss: Person
...

entity Person {
    friends: List<Person>
...

i.e. use entities instead of Java types or even mix Java types as List with entities such as Person. One way to achieve this is to let your concepts inherit from a corresponding JVM type, e.g. let Entity inherit from JvmGenericType (src). But this would result in a lot of accidentally inherited properties in your domain model. In Xbase there is an alternative: You can simply define how to derive a JVM model from your model. This inferred JVM model is the representation of your concepts in the typesystem of Xbase.

The main component for the inferred JVM model is the IJvmModelInferrer (src). It has a single method that takes the root model element as an argument and produces a number of JvmGenericTypes (src). As Xbase cannot guess how you would like to map your concepts to JVM elements, you have to implement this component yourself. This usually boils down to using an injected JvmTypesBuilder (src) to create a hierarchy of JVM elements. The builder (src) helps to initialize the produced types with sensible default and encapsulates the logic that associates the source elements with the derived JVM concepts. As this kind of transformation can be elegantly implemented using polymorphic dispatch functions and extension methods, it is a good choice to write the IJvmModelInferrer (src) in Xtend. It becomes even simpler if you inherit from the AbstractModelInferrer (src) which traverses the input model and dispatches to its contents until you decide which elements to handle.

For our domain model example, we implement a polymorphic dispatch function infer for Entities to transform them into a JvmGenericType (src). The produced type holds a JvmOperation (src) for each Operation and a JvmField (src) plus accessors for each Property. The resulting DomainmodelJvmModelInferrer looks like this:

class DomainModelJvmModelInferrer extends AbstractModelInferrer {

  /**
   * a builder API to programmatically create Jvm elements 
   * in readable way.
   */

  @Inject extension JvmTypesBuilder
  
  @Inject extension IQualifiedNameProvider
  
  def dispatch void infer(Entity element, 
                IAcceptor<JvmDeclaredType> acceptor, 
                boolean isPrelinkingPhase) {
    
    acceptor.accept(element.toClass(element.fullyQualifiedName) [
      documentation = element.documentation
      for (feature : element.features) {
        members += feature.toField(feature.name, feature.type)
        members += feature.toSetter(feature.name, feature.type)
        members += feature.toGetter(feature.name, feature.type)
      }
    ])
  }
}

Linking and Indexing

As Java elements and your concepts are now represented as JVM model elements, other models can now transparently link to Java or your DSL. In other words, you can use a mapped element of your DSL in the same places as the corresponding Java type.

The Xbase framework will automatically switch between the JVM element or the DSL element when needed, e.g. when following hyperlinks. The component allowing to navigate between the source model and the JVM model is called IJvmModelAssociations (src), the read-only antagonist of the IJvmModelAssociator (src) that is used by the JvmTypesBuilder (src).

By default, the inferred model is indexed, so it can be cross referenced from other models.