A blog by Devendra Tewari
The Apache Velocity Runner merges an XML document such as
<!DOCTYPE velocity SYSTEM "velocity.dtd">
<velocity>
<template root="c:\java\velocity" file="HelloVelocity.txt"/>
<output root="c:\java\velocity" file="HelloVelocity.out.txt"/>
<context>
<property name="Name" value="Devendra Tewari"/>
<list name="lastaccesses">
<object>
<property name="time" value="12/12/2001 13:30"/>
<property name="computer" value="abc"/>
</object>
<object>
<property name="time" value="31/12/2001 18:30"/>
<property name="computer" value="abc"/>
</object>
<object>
<property name="time" value="12/12/2001 13:30"/>
<property name="computer" value="abc"/>
</object>
<object>
<property name="time" value="12/12/2001 13:30"/>
<property name="computer" value="abc"/>
</object>
<object>
<property name="time" value="16/12/2001 13:30"/>
<property name="computer" value="abc"/>
</object>
</list>
</context>
</velocity>
With a template such as
Hello $Name!
Last $!{lastaccesses.size()} accesses:
#foreach($access in $lastaccesses)
${velocityCount}. $access.time from $access.computer
#end
$!silent
To produce the following output
Hello Devendra Tewari!
Last 5 accesses:
1. 12/12/2001 13:30 from abc
2. 31/12/2001 18:30 from abc
3. 12/12/2001 13:30 from abc
4. 12/12/2001 13:30 from abc
5. 16/12/2001 13:30 from abc
The XML document schema in DTD form is reproduced below
<?xml encoding="UTF-8" ?>
<!ELEMENT velocity (template,output,context)>
<!ELEMENT template EMPTY>
<!ATTLIST template
root CDATA #REQUIRED
file CDATA #REQUIRED
>
<!ELEMENT output EMPTY>
<!ATTLIST output
root CDATA #REQUIRED
file CDATA #REQUIRED
>
<!-- velocity context -->
<!ELEMENT context (property | list)+>
<!-- name/value pairs are placed directly into the context -->
<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED
>
<!-- maps contain one or more name/value pairs -->
<!ELEMENT object (property)+>
<!-- lists contain one or more maps -->
<!ELEMENT list (object)+>
<!ATTLIST list
name CDATA #REQUIRED
>
package velocity;
import java.util.Properties;
import java.util.ArrayList;
import java.util.HashMap;
import java.io.File;
import java.io.Writer;
import java.io.FileWriter;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.Template;
import org.apache.velocity.app.VelocityEngine;
public class RunVelocity {
public RunVelocity() {
}
/**
* @param args One or more velocity run file(s)
*/
public static void main(String[] args) throws Exception {
RunVelocity rv = new RunVelocity();
rv.process(args);
System.out.println("done.");
}
/**
* Process run files.
*
* @param runFiles One or more velocity run file(s).
*/
public void process(String[] runFiles) throws Exception {
if (runFiles.length == 0) {
System.out.println("Specify atleast one velocity run file.");
System.out.println("Usage:");
System.out.println("\tjava RunVelocity <run file 1> [<run file 2> <run file 3> ...]");
}
for(int i = 0; i < runFiles.length; i++) {
processRunFile(runFiles[i]);
}
}
/**
* Process a single run file.
*
* @param file Name of the run file to process.
*/
private void processRunFile(String file) throws Exception {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setValidating(true);
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document doc = builder.parse(file);
// Locate velocity element
NodeList nodeList = doc.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
if (nodeList.item(i).getNodeType() == Node.ELEMENT_NODE
&& nodeList.item(i).getNodeName().equals("velocity")) {
processVelocityElement((Element)nodeList.item(i));
}
}
}
/**
* Process the velocity element and process the velocity
* template to generate the output file.
* @param e Element node of the velocity element.
* @return none.
*/
private void processVelocityElement(Element e) throws Exception {
VelocityEngine ve = new VelocityEngine();
Template template = null;
VelocityContext context = null;
Writer writer = null;
NodeList nodeList = e.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
if (nodeList.item(i).getNodeName().equals("template")) {
template = getTemplateFromElement(ve, (Element)nodeList.item(i));
}
if (nodeList.item(i).getNodeName().equals("output")) {
writer = getWriterFromElement((Element)nodeList.item(i));
}
if (nodeList.item(i).getNodeName().equals("context")) {
context = getContextFromElement((Element)nodeList.item(i));
}
}
// Merge
template.merge(context, writer);
writer.flush();
writer.close();
}
/**
* Obtain a velocity template object based on the
* configuration specified in the run file template
* element.
*
* @param ve Reference to a velocity engine.
* @param e Element node of the template.
* @return A new velocity Template.
*/
private Template getTemplateFromElement(VelocityEngine ve, Element e) throws Exception {
Template t = new Template();
if (e.hasAttributes()) {
Properties p = new Properties();
p.setProperty("file.resource.loader.path", e.getAttribute("root"));
ve.init(p);
t = ve.getTemplate(e.getAttribute("file"));
}
return t;
}
/**
* Obtain a writer object based on the
* configuration specified in the run file output
* element.
*
* @param e Element node of the output element.
* @return A new writer pointing to an output file.
*/
private Writer getWriterFromElement(Element e) throws Exception {
Writer w = null;
if (e.hasAttributes()) {
File f = new File(e.getAttribute("root"), e.getAttribute("file"));
w = new FileWriter(f);
}
return w;
}
/**
* Obtain a velocity context object based on the
* data specified in the run file context
* element.
*
* @param e Element node of the context element.
* @return A new velocity context.
*/
private VelocityContext getContextFromElement(Element e) throws Exception {
VelocityContext vc = new VelocityContext();
// read all values
NodeList nodeList = e.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++) {
if (nodeList.item(i).getNodeName().equals("property")) {
Element value = (Element)nodeList.item(i);
vc.put(value.getAttribute("name"), value.getAttribute("value"));
}
}
// read all lists
nodeList = e.getElementsByTagName("list");
for (int i = 0; i < nodeList.getLength(); i++) {
addListToContext(vc, (Element)nodeList.item(i));
}
return vc;
}
/**
* Reads a single list element, constructs
* an ArrayList and adds it to the context.
*
* @param vc Reference to a velocity context.
* @param e Element node of the list.
*/
private void addListToContext(VelocityContext vc, Element e) {
ArrayList arrayList = new ArrayList();
// read all maps and add to list
NodeList nodeList = e.getElementsByTagName("object");
for (int i = 0; i < nodeList.getLength(); i++) {
arrayList.add(getMapFromElement((Element)nodeList.item(i)));
}
vc.put(e.getAttribute("name"), arrayList);
}
/**
* Creates a HashMap given an object element.
*
* @param e Element node of the object element.
* @return HasMap of the object element.
*/
private HashMap getMapFromElement(Element e) {
HashMap hashMap = new HashMap();
// read all values and add to HashMap
NodeList nodeList = e.getElementsByTagName("property");
for (int i = 0; i < nodeList.getLength(); i++) {
Element value = (Element)nodeList.item(i);
hashMap.put(value.getAttribute("name"), value.getAttribute("value"));
}
return hashMap;
}
}