Learn how to register a format in Snippetory.

Or find the code on github

Extending the platform:
Resource bundle resolution

Snippetory is an easy-to-use and extensible text generation platform. Here we focus on the extension mechanism. It is based on the service loading mechanism of the jar. Any extension available on class path, in jars, will be loaded whenever Snippetory is initialized.

This has the advantage, that the extensions are available in different environments simply by configuring the class path.

Whenever an extension is used without packaging it can be easily loaded by loading the Configurer class.

Service Provider Interface

All the classes, relevant for extending the platform are placed in the package org.jproggy.snippetory.spi. These are the interfaces that have to be implemented for creating new formats, encoding and syntaxes as well as the Configurer interface.

The example

Since information from resource bundles can be resolved statically, it would be nice to resolve them while parsing the template. On the other hand a full build in support would require a complexity, that isn't that handy. However, specialization reduces the information to be denoted. Hence, I decided to provide localization support as a how-to instead of trying to build an all-in-one solution.

Hands on

First we need an VoidFormat class. That's a specialization of the Format It stores the value resolved from the resource bundle.

package org.jproggy.example.msg; import java.util.Collections; import java.util.Set; import org.jproggy.snippetory.spi.*; public class ResourceFormat extends SimpleFormat implements VoidFormat { private final Template resolved; public ResourceFormat(Template resolved) { this.resolved = resolved; } @Override public Object formatVoid(TemplateNode node) { return resolved; } @Override public void set(String name, Object value) { resolved.set(name, value) } @Override public void append(String name, Object value) { resolved.append(name, value) } @Override public Set<String> names() { return resolved.names(); } }

However, to have a resolved value we need to resolve it. This is done within the FormatFactory.

package org.jproggy.example.msg; import java.util.ResourceBundle; import org.jproggy.snippetory.TemplateContext; import org.jproggy.snippetory.spi.*; public class ResourceFormatFab implements FormatFactory, Configurer { static { // the class is only loaded, so initialization goes to // static initializer. Format.register("msg", new ResourceFormatFab()); } @Override public FormatConfiguration create(String definition, TemplateContext ctx){ // The resource is resolved in the factory. This mean if resolving // would fail, the template could not be parsed. I.e. we have a // strong fail fast behavior. ResourceBundle msg = ResourceBundle.getBundle( "org.jproggy.example.msg.Messages", ctx.getLocale()); // parsing it to a template allows providing parameters Template resolved = Syntaxes.FLUYT.parse(msg.getString(definition)) return new ResourceFormat(resolved); } }

Now we have to configure the service loader mechanism. This is a little more complicated to describe as it needs interaction with you packaging solution. In effect, we to create a sub folder of the META-INF folder called services. This folder has to contain a file called org.jproggy.snippetory.spi.Configurer. I.e. META-INF/services/org.jproggy.snippetory.spi.Configurer
And this file has to contain a single line, that's simply the binary name of your Configurer class. The binary name differs from the qualified basically for inner classes. They're separated form their hosts name by '$' instead of a period.

com.example.tool.ResourceFormatFab

Now it's ready to use. We create a file called HalloWorldApp.html as a resource within our class path. (If you use Maven just put it in src/main/resources)

<html> <head> <title>{v:x msg="page.title"}</title> </head> <body> {v:x msg="greeting"} {v:x msg="world"} </body> </html>

Of course we need a properties file. Maybe com/example/tool/Messages_de.properties:

page.title = Hallo Welt Anwendung greeting = Hallo world = Welt

With java code like this:

. . . Repo.readResource("HalloWorldApp.html").locale(Locale.GERMANY) .parse().render(System.out); . . .
Bernd Ebertz Head, Founder and chief technology evangelist of
jproggy.org