“Hello World” Generator

The hello world generator has the following structure and can be downloaded from this public bitbucket repository.

Hello World Generator

The hello world generator takes basic text files as input (e.g. names.txt in the repository). The text file include nothing else than names, separated by line breaks.

Input for hello world generator

For every non-empty line in the input files, the generator creates an output file that gets that name (with .txt file ending). The file contains a greeting, like for instance “Hello Heinz!”.

Output for hello world generator

The Model Access TextModelAccess.java

In the model access the file content is read an for each entry a string is added to the collection that is going to be returned from loadElements().

@Override
protected Collection<?> loadElements(InputStream inputStream,
    ModelAccessOptions modelAccessOptions) throws ModelAccessException {

    try {
        List<Object> result = new ArrayList<>();
        final ZipInputStream zis = new ZipInputStream(inputStream);
        ZipEntry entry = null;

        while ((entry = zis.getNextEntry()) != null) {
            if (entry.getName().toLowerCase().endsWith(".txt")) {
                // --- overwriting the close() method in order to
                // --- do nothing since the given zip input stream
                // --- will be closed by the caller
                BufferedInputStream bis = new BufferedInputStream(zis) {
                    @Override
                    public void close() throws IOException {}
                };

                String input = getStringFromInputStream(bis);
                String[] names = input.split("[\n]");

                for (String name : names) {
                    if (name != null && name.trim().length() > 0) {
                         result.add(name.trim());
                    }
                }
            } else {
                // ignore this entry since it doesn't represent
                // a file that this model access is supposed to handle
            }
            zis.closeEntry();
        }
        return result;

    } catch (IOException ex) {
        throw new ModelAccessException("Error while reading ZIP"
            + " input stream content", ex);

    } catch (Throwable th) {
        throw new ModelAccessException("Error while creating model"
            + " elements from ZIP input stream", th);
    }
}

The Model Converter TextToGreetingConverter.java

The model converter creates instances of the metamodel class ‘Person’. A collection of those instances are then the input for the generator’s last step: the generation group.

@Override
protected final Set<Object> clientConvert(Collection<?> elements,
        ModelConverterOptions options) throws ModelConverterException {

    LinkedHashSet<Object> result = new LinkedHashSet<>();

    for (Object element : elements) {
        if (element instanceof String) {
            String name = (String) element;
            Person person = new Person(name);
            result.add(person);
        }
    }

    return result;
}

The Generation Group GenerationGroupHelloWorld.java

The following three methods from the class ‘GenerationGroupHelloWorld’ are called by the Virtual Developer framework to find out, which code to call to write the generator’s output files.

MethodPurpose
getAllTargets() defines the set of all target classes that a generation group should handle
getWriterClass(Object, Class<? extends TargetI<?>>) transformation decision, returns a writer class in case a file should be generated for the given model element (Object) and target class
getWriterClass(Object, TargetI<?>) transformation delegation, returns a writer class that should be instantiated and used to write something for the given model element (Object) and target instance

General information on how to use generation groups can be found here.

/* (non-Javadoc)
 * @see org.jenerateit.generationgroup.WriterLocatorI#getWriterClass(java.lang.Object, java.lang.Class)
 */
@Override
public Class<? extends WriterI> getWriterClass(Object modelElement,
        Class<? extends TargetI<?>> targetClass) {

    if (modelElement instanceof Person && targetClass == PersonTarget.class) {
        return PersonWriter.class;
    }

    return null;
}

/* (non-Javadoc)
 * @see org.jenerateit.generationgroup.WriterLocatorI#getWriterClass(java.lang.Object, org.jenerateit.target.TargetI)
 */
@Override
public Class<? extends WriterI> getWriterClass(Object modelElement,
        TargetI<?> targetInstance) {

    @SuppressWarnings({ "unchecked" })
    Class<? extends TargetI<?>> targetClazz =
        (Class<? extends TargetI<?>>) targetInstance.getClass();

    Class<? extends WriterI> writerClass =
        getWriterClass(modelElement, targetClazz);

    return writerClass;
}

/* (non-Javadoc)
 * @see org.jenerateit.generationgroup.GenerationGroupConfigI#getAllTargets()
 */
@Override
public Set<Class<? extends TargetI<?>>> getAllTargets() {

    Set<Class<? extends TargetI<?>>> result = new LinkedHashSet<>();
    result.add(PersonTarget.class);
    return result;
}

The PersonWriter class (inner class of PersonTarget) uses the Velocity template engine to write to a file. Have a closer look at the transform() method in the writer class to see how Velocity is being used here.

@Override
public void transform(TargetSection ts) throws WriterException {
    VelocityEngine engine = new VelocityEngine();
    engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath"); 
    engine.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());

    Template template = engine.getTemplate(getTemplateName());


    VelocityContext context = new VelocityContext();
    context.put("model", getModelElement());

    StringWriter stringWriter = new StringWriter();
    template.merge(context, stringWriter);

    try {
        write(stringWriter.toString().getBytes("UTF-8"));
        wNL();
    } catch (UnsupportedEncodingException ex) {
        throw new WriterException("cannot convert result of velocity template to a byte array", ex);
    }

    if (modelElement.getHairColor() != null) {
        WriterI hairColorWriter = getTransformationTarget().getWriterInstance(modelElement.getHairColor());
        if (hairColorWriter != null) hairColorWriter.transform(ts);
    }
}