Does Apache Velocity include a mechanism for adding metadata to a template?
I'm trying to add some extra information to my templates (e.g., type and descriptive name), and then read those to programmatically group templates by type, and list the templates on the UI using their descriptive name.
I've tried to use literal #[[...]]#
blocks (and parse them), and #set
directives, but both a have issues. They are hacky (require some parsing of the template) and far from elegant.
Hmmm, I'm not aware of anything built-in to do this. To avoid processing a whole template on a first pass though, one trick is to conditionally throw an exception (MetadataFinished
below) during that pass, but not normal execution.
Clearly this would still need to compile the whole template up front, though this should come in useful at execution time.
E.g.
import org.apache.commons.io.output.NullWriter;
public class Metadata {
private Map<String, Template> byKey = new LinkedHashMap<>();
private Template currentTemplate;
/** Callback from .vm */
public void set(String key) throws MetadataFinished {
// Only do this in addTemplate()
if (currentTemplate != null) {
byKey.put(key, currentTemplate);
throw new MetadataFinished();
}
}
public void addTemplate(Template template) {
currentTemplate = template;
try {
Context context = new VelocityContext();
context.put("metadata", this);
template.merge(context, new NullWriter());
} catch (MetadataFinished ex) {
// Ignored
} finally {
currentTemplate = null;
}
}
public void execute(String key) {
Template template = byKey.get(key);
Context context = new VelocityContext();
PrintWriter pw = new PrintWriter(System.out);
template.merge(context, pw);
pw.flush();
}
// Extends Error to avoid Velocity adding a wrapping MethodInvocationException
private static class MetadataFinished extends Error {
}
public static void main(String[] args) {
Metadata metadata = new Metadata();
VelocityEngine engine = new VelocityEngine();
engine.setProperty("file.resource.loader.path", "/temp");
engine.init();
String[] fileNames = { "one.vm", "two.vm" };
for (String fileName : fileNames) {
Template template = engine.getTemplate(fileName);
metadata.addTemplate(template);
}
metadata.execute("vm1");
metadata.execute("vm2");
}
}
Then in one.vm
:
$!metadata.set("vm1")##
-----------
This is VM1
-----------
The ##
there is a bit ugly - it's just to stop a blank line being output. If readability is important, this can be made a bit neater with a macro though:
#metadata("vm2")
-----------
This is VM2
-----------
That macro could be defined in the global VM_global_library.vm
:
#macro( metadata $key )
$!metadata.set($key)#end
Just for reference, the output is as expected:
-----------
This is VM1
-----------
-----------
This is VM2
-----------