Channels ▼
RSS

JVM Languages

Generating Code from DSLs


Switch Statement
Unlike Java, the switch statement in Xtend has

  • No fall-through: at most one case-block will be executed.
  • No required constants: The case-value can be any expression that evaluates to true/false or to a value that is compared with the switch-object. In this example, there are two boolean expressions using an instanceof operator.
  • Type Guards: Each case-block may be prefixed with a type. This guarantees that the case block is executed only if the switch-object is an instance of this type. Furthermore, it ensures that within the case-block, the switch-object is implicitly casted to the type. Explicit type casts as required in Java can be avoided this way. Keep in mind that guards and conditions are evaluated sequentially in the order of declaration. They are not sorted to check the most specific type first.

The algorithm implemented in getPackageName()first checks whether the object is of type PackageDeclaration and the object's container of type PackageDeclaration. If so, it returns the container's package-name concatenated with the local package name. Because the algorithm is recursive, this will also handle deep nesting of packages. Second, it checks whether the object is of type PackageDeclaration and its container is a Domainmodel, in which case, only the package name is returned. Finally, if the object is of type AbstractElement, it assumes there is a container with a package name. For object of all other types, an empty string is returned.


Step 3: The Complete Template

Now we need to combine the template from Step 1 and the helper functions from Step 2. Furthermore, we need to add a template for type Entity. This is actually the more interesting template, because entities have features. Given that there are two getContents() methods, Xtend decides via dynamic dispatch which to invoke. Also, the multiline string for Entities makes use of intelligent whitespace handling. Both concepts are explained in the following section.


package org.example.domainmodel.generator

import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.generator.*
import org.example.domainmodel.domainmodel.*
import static extension org.eclipse.xtext.xtend2.lib.ResourceExtensions.*

class DomainmodelGenerator implements IGenerator {
  
  override void doGenerate(Resource resource, IFileSystemAccess fsa) {
    for(type : resource.allContentsIterable().filter(typeof(Type)))
      fsa.generateFile(type.fileName, type.contents)
  }
  
  def dispatch getContents(Entity entity) '''
    package <<entity.packageName>>;
    
    public class <<entity.name>> {
      
      <<FOR f:entity.features>>
        private <<f.type.javaName>> <<f.name>>;
        
        public <<f.type.javaName>> get<<f.name.toFirstUpper>>() {
          return this.<<f.name>>;
        }
        
        public void set<<f.name.toFirstUpper>>(<<f.type.javaName>> <<f.name>>) {
          this.<<f.name>> = <<f.name>>;
        }
      <<ENDFOR>>
      
    }
  '''
  
  def dispatch getContents(DataType dataType) '''
    package <<dataType.packageName>>;
    
    public class <<dataType.name>> {
    }
  '''
  
  def getFileName(Type type) {
    type.javaName.replace(".", "/") + ".java"
  }
  
  def String getPackageName(EObject obj) {
    switch(obj) {
      PackageDeclaration case(obj.eContainer instanceof PackageDeclaration):
        obj.eContainer.packageName + "." + obj.name.toLowerCase
      PackageDeclaration case(obj.eContainer instanceof Domainmodel):
        obj.name.toLowerCase
      AbstractElement:
        obj.eContainer.packageName
      default:
        ""
    }
  }
  
  def getJavaName(Type type) {
    type.packageName + "." + type.name
  }
}

Dynamic Dispatch
Java supports only static dispatch while it's the default in Xtend. By adding the keyword dispatch to method declarations, as in the previous listing, Xtend also supports dynamic dispatch. So what's the difference? It boils down to determining which method to invoke if there are multiple methods with the same name but different parameter types. Both dispatch strategies chose the method with most specific type; that is, the closest type in the inheritance hierarchy. The difference lies in which type is taken into account:

  • For static dispatch, it is the type determined by static analysis at compile time.
  • For dynamic dispatch, it is the actual type of the object at runtime, which often is more specific than the statically known type.

To make a long story short, dynamic dispatch replaces the Visitor pattern. In contrast to a switch statement, the order is irrelevant because Xtend will always check against the most specific type first.

In the previous listing, both getContents(Entity) and getContents(DataType) can be invoked by type.contents. As explained earlier, type.contents is equivalent to getContents(type). The type of variable type is Type (as determined by type inference); therefore, it cannot be decided at compile time whether getContents(Entity) or getContents(DataType) should be called. In fact, the decision can be different for every individual object. That is exactly what Xtend will do: Determine the type at runtime and call the most appropriate getContents(). If the type is neither a DataType nor an Entity, an IllegalArgumentException will be thrown.

Run

The code generator will be run automatically by the Xtext builder for every file that the user changes. All you need to do is create a source folder with the name "src-gen" in your project. Alternatively, you can run it explicitly by running the Java class Main (in the same folder as the Xtend file) or by running DomainmodelGeneratorMWE.mwe2.


The author is a lead contributor to the Xtext project.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video