Wednesday, June 17, 2015

Another view on generated code

This is a follow-up on my last article regarding CodeGen, the object-oriented code generator for java. One of the advantages of the modular approach demonstrated there is that multiple views can be attached to the same generator. By using different "factories", the rendering can go through the same pipeline and still be divided into different contexts.

In this example, we will render the same "concat-method" model using two different installers. One is the standard java-installer and one is an example XML-installer.

The XMLInstaller will have two additional views, MethodXMLView and FieldXMLView. Those views will use some of the Java-views (like TypeView) since a type looks the same in both contexts.

Main.java

public class Main {
    private final static TransformFactory 
        XML = new DefaultTransformFactory("XMLTransformFactory")
            .install(Method.class, MethodXMLView.class)
            .install(Field.class, FieldXMLView.class),

        JAVA = new JavaTransformFactory();

    public static void main(String... args) {
        final Generator gen = new DefaultGenerator(JAVA, XML);
        Formatting.tab("    ");

        gen.on(
            Method.of("concat", DefaultType.STRING).public_()
                .add(Field.of("str1", DefaultType.STRING))
                .add(Field.of("str2", DefaultType.STRING))
                .add("return str1 + str2;")
        ).forEach(code -> {
            System.out.println("-------------------------------------");
            System.out.println("  " + code.getInstaller().getName() + ":");
            System.out.println("-------------------------------------");
            System.out.println(code.getText());
        });
    }
}

MethodXMLView.java

public class MethodXMLView implements Transform<Method, String> {
    @Override
    public Optional<String> render(Generator gen, Method model) {
        return Optional.of(
            "<method name=\"" + model.getName() + "\" type=\"" + 
            gen.on(model.getType()).get() + "\">" + nl() + indent(
                "<params>" + nl() + indent(
                    gen.metaOn(model.getFields())
                        .filter(c -> XML.equals(c.getFactory()))
                        .map(c -> c.getText())
                        .collect(Collectors.joining(nl()))
                ) + nl() + "</params>" + nl() +
                "<code>" + nl() + indent(
                    model.getCode().stream().collect(Collectors.joining(nl()))
                ) + nl() + "</code>"
            ) + nl() + "</methods>"
        );
    }
}

FieldXMLView.java

public class FieldXMLView implements Transform<Field, String> {
    @Override
    public Optional<String> render(Generator gen, Field model) {
        return Optional.of("<field name=\"" + model.getName() + "\" />");
    }
}

Results

When the application above is executed, the following will be outputed:

-------------------------------------
  JavaTransformFactory:
-------------------------------------
public String concat(String str1, String str2) {
    return str1 + str2;
}
-------------------------------------
  XMLTransformFactory:
-------------------------------------
<method name="concat" type="java.lang.String">
    <params>
        <field name="str1" />
        <field name="str2" />
    </params>
    <code>
        return str1 + str2;
    </code>
</methods>

The same model is rendered in two different languages using the same rendering pipeline.

Tuesday, June 2, 2015

Object-Oriented approach to Code Generation

Code Generation is  a common way to reduce the unhealthy load of boring tasks often put on us eager code grunts. Many code generation frameworks I have seen use a template-replace-repeat approach where you write a template for how the generated code file should look and then replace certain keywords and repeat other sections to produce the specific file you want.

A problem with this approach that annoys me is that it is really difficult to know if the generated code will work or not until you compile it. You might have changed the name of one class and suddenly the generated code won't build. To handle this issue I started a project called CodeGen that aim to be completely object-oriented so that you can benefit from type-safety all the way from template to executable code. The main user case for the generator is the Speedment software, but it can be used in a variety of projects.

Consider the following code:
final Generator generator = new JavaGenerator();

final File file = File.of("org/example/Foo.java")
    .add(Class.of("Foo").public_()
        .add(Field.of("x", DOUBLE_PRIMITIVE).final_())
        .add(Field.of("y", DOUBLE_PRIMITIVE).final_())
        .add(Field.of("z", DOUBLE_PRIMITIVE).final_())
        .call(new AutoConstructor())
        .call(new AutoSetGetAdd())
        .call(new AutoEquals())
    )
    .call(new AutoJavadoc())
    .call(new AutoImports(generator))
;

The model tree of the application is built using beans. New methods and member variables can be added to the tree to create variants of the same class.
When the code is to be rendered it can easily be passed to a generator class.
String code = generator.on(file).get();

The generated code will look like the following:
/**
 * Write some documentation here.
 */
package org.example;

import java.util.Optional;

/**
 * @author You name here
 */
public class Foo {

    private final double x;
    private final double y;
    private final double z;

    /**
     * Initializes the Foo component.
     *
     * @param x  the x
     * @param y  the y
     * @param z  the z
     */
    public Foo(double x, double y, double z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    /**
     * Returns the value of x.
     *
     * @return  the value of x
     */
    public double getX() {
        return x;
    }

    /**
     * Sets a new value for x.
     *
     * @param x  the new value of x
     */
    public void setX(double x) {
        this.x = x;
    }

    /**
     * Returns the value of y.
     *
     * @return  the value of y
     */
    public double getY() {
        return y;
    }

    /**
     * Sets a new value for y.
     *
     * @param y  the new value of y
     */
    public void setY(double y) {
        this.y = y;
    }

    /**
     * Returns the value of z.
     *
     * @return  the value of z
     */
    public double getZ() {
        return z;
    }

    /**
     * Sets a new value for z.
     *
     * @param z  the new value of z
     */
    public void setZ(double z) {
        this.z = z;
    }

    /**
     * Generates a hashCode for this object. If any field is
     * changed to another value, the hashCode may be different.
     * Two objects with the same values are guaranteed to have
     * the same hashCode. Two objects with the same hashCode are
     * not guaranteed to have the same hashCode."
     *
     * @return  the hash code
     */
    @Override
    public int hashCode() {
        int hash = 7;
        hash = 31 * hash + (Double.hashCode(this.x));
        hash = 31 * hash + (Double.hashCode(this.y));
        hash = 31 * hash + (Double.hashCode(this.z));
        return hash;
    }

    /**
     * Compares this object with the specified one for equality.
     * The other object must be of the same type and not null for
     * the method to return true.
     *
     * @param other  the object to compare with
     * @return  {@code true} if the objects are equal
     */
    @Override
    public boolean equals(Object other) {
        return Optional.ofNullable(other)
            .filter(o -> getClass().equals(o.getClass()))
            .map(o -> (Foo) o)
            .filter(o -> this.x == o.x)
            .filter(o -> this.y == o.y)
            .filter(o -> this.z == o.z)
            .isPresent();
    }
} 

Every component is implemented as a Interface-Class pair so that you can change the implementation dynamically without rewriting other parts of the system.

Hopefully this will be helpful for other people!