The first wurblet

Create a subdirectory with the name wurblets and in this directory create another pom.xml with the following contents:



  4.0.0

  
    com.example
    wurbelizer-tutorial
    1.0-SNAPSHOT
  

  org.example
  wurblets
  1.0-SNAPSHOT

  
    
      org.wurbelizer
      wurbelizer
      ${wurbelizer.version}
    
  

  
    
      
        org.wurbelizer
        wurbelizer-maven-plugin
        
          
            
              wurbile
            
          
        
      
    
  


The execution goal wurbile translates the wurblet source code to compilable Java and a resource file holding strings.

The wurbelizer can load and execute any Java class that implements the interface org.wurbelizer.wurblet.Wurblet.

The dependency above not only provides this interface but also 2 abstract classes to make the developer’s life easier:

  • org.wurbelizer.wurblet.AbstractWurblet that implements org.wurbelizer.wurblet.Wurblet
  • org.wurbelizer.wurblet.AbstractJavaWurblet that extends org.wurbelizer.wurblet.AbstractWurblet

To define the properties of the DTO we need some model which can be parsed by the wurblet. For this tutorial we’re using a very simple format with one line per property consisting of:

  • the Java type, e.g. String
  • the property’s name
  • some optional comment

In src/main/java/com/example/wurblets please create file DTOWurblet.java with the following content:

package com.example.wurblets;

import org.wurbelizer.wurbel.WurbelException;
import org.wurbelizer.wurbel.WurbelHelper;
import org.wurbelizer.wurblet.AbstractJavaWurblet;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

/**
 * Base class for the {@code @DTO}-wurblet.
 */
public class DTOWurblet extends AbstractJavaWurblet {

  /**
   * DTO property.
   */
  protected class Property {

    private final String type;                // the java type
    private final String name;                // the property name
    private final String comment;             // the comment

    public Property(String type, String name, String comment) {
      this.type = type;
      this.name = name;
      this.comment = comment == null || comment.isEmpty() ? "the " + name + " property" : comment.trim();
    }

    public String getType() {
      return type;
    }

    public String getName() {
      return name;
    }

    public String getComment() {
      return comment;
    }

    public String getGetterName() {
      return ("boolean".equals(type) ? "is" : "get") + firstToUpper(name);
    }
  }

  protected String filename;            // filename of the here-doc holding the model
  protected List<Property> properties;  // the DTO properties

  @Override
  public void run() throws WurbelException {
    super.run();

    if (getContainer().getArgs().length == 0) {
      throw new WurbelException("usage: @wurblet <guardname> DTO <filename>");
    }
    filename = getContainer().getArgs()[0];

    properties = new ArrayList<>();

    try (BufferedReader reader = new BufferedReader(WurbelHelper.openReader(filename))) {
      String line;
      while ((line = reader.readLine()) != null) {
        line = line.trim();
        if (!line.isEmpty() && !line.startsWith("#")) {
          StringTokenizer stok = new StringTokenizer(line);
          String type = null;
          String name = null;
          StringBuilder comment = new StringBuilder();
          while (stok.hasMoreTokens()) {
            String token = stok.nextToken();
            if (type == null) {
              type = token;
            }
            else if (name == null) {
              name = token;
            }
            else {
              if (comment.length() > 0) {
                comment.append(' ');
              }
              comment.append(token);
            }
          }
          if (type == null || name == null) {
            throw new WurbelException("property line too short: '" + line + "'");
          }
          properties.add(new Property(type, name, comment.toString()));
        }
      }

      // validate
      for (Property property : properties) {
        if (property.getType().isEmpty()) {
          throw new WurbelException("missing property type");
        }
        if (property.getName().isEmpty()) {
          throw new WurbelException("missing property name");
        }
      }
    }
    catch (IOException ex) {
      throw new WurbelException("reading model " + filename + " failed", ex);
    }
  }

  private String firstToUpper(String str) {
    return str.substring(0, 1).toUpperCase() + str.substring(1);
  }
}

Next, in src/main/wurblets/com/example/wurblets create the file DTO.wrbl with the following content:

@{package com.example.wurblets}@
@{import java.util.*}@
@{import java.io.*}@
@{import org.wurbelizer.wurbel.*}@
@{import org.wurbelizer.wurblet.*}@
@{import org.wurbelizer.misc.*}@
@{extends DTOWurblet}@
@{args}@
@{comment
Creates code for a data transfer object.
}@
@[
  private void wurbel() throws WurbelException {

    // create declarations
    for (Property property: properties) {
]@
  private final @(property.getType())@ @(property.getName())@;  // @(property.getComment())@
@[
    }

    // create constructor
]@

  /**
   * Creates a @(getClassName())@.
   *
@[
    StringBuilder args = new StringBuilder();
    for (Property property: properties) {
      if (args.length() > 0) {
        args.append(", ");
      }
      args.append(property.getType()).append(' ').append(property.getName());
]@
   * \@param @(property.getName())@ @(property.getComment())@
@[
    }
]@
   */
  public @(getClassName())@(@(args)@) {
@[
    for (Property property: properties) {
]@
    this.@(property.getName())@ = @(property.getName())@;
@[
    }
]@
  }
@[

    // create getter
    for (Property property: properties) {
]@

  /**
   * Gets @(property.getComment())@.
   *
   * \@return @(property.getComment())@
   */
  public @(property.getType())@ @(property.getGetterName())@() {
    return @(property.getName())@;
  }
@[
    }
  }
]@

As already mentioned in the introduction the wurbelizer’s template language is Java with a few syntax extensions. The extensions are necessary to separate the template level from the generated code level:

  • @[ to switch to template level
  • ]@ to switch to code level
  • @(...)@ to extract a value from the template level (within code level)

The other lines at the beginning of the wurblet source are pretty self-explaining. Notice that the wurblet extends DTOWurblet.

Execute mvn clean install in the parent directory. This will invoke the wurbiler (wurblet compiler), compile the Java code and create a jar file.

In the next chapter we will use the wurblet to generate code for a DTO.