Trivial Templating
Notwithstanding the fact that anyone in their right mind (which rules me out) would use Apache Velocity or FreeMarker for templating, we present a trivial templating helper class, where for instance we have an HTML template as follows to send a confirmation email to a customer.
<p>Hey ${displayName}</span>
<p>Your Travelstart reference: <span class="bookingReference">${bookingReference}</span>
<table class="travelerTable">
<tbody>
<tr class="travelerRow">
<td>${travelerName}</td>
</tr>
</tbody>
</table>
where the above is used to compose an HTML email, and/or render a PDF document using FlyingSaucer.
So we invoke the "templating engine" as follows.
<b>public</b> String generate(String resourceName) {
HtmlTemplate template = <b>new</b> HtmlTemplate(getClass().getResourceAsStream(resourceName));
template.setProperty("displayName", "Evan");
template.setProperty("bookingReference", "555555");
template.setProperty("bookingDate", "16 Jul 2010");
template.setProperty("amount", "R 1,200.00");
template.setProperty("travelerRow", 0, "travelerName", "Evan Summers");
template.setProperty("travelerRow", 1, "travelerName", "Tootie Milburn");
<b>return</b> template.compose();
}
This uses the trivial templating class below to read the HTML template, and substitute the given values into the template, etc.
<b>public</b> <b>class</b> HtmlTemplate {
List<String> lineList = <b>new</b> ArrayList();
InputStream inputStream;
StringBuilder templateBuilder = <b>new</b> StringBuilder();
String trClass = <b>null</b>;
StringBuilder rowBuilder = <b>new</b> StringBuilder();
String line;
EntryList entryList = <b>new</b> EntryList();
<b>public</b> HtmlTemplate(InputStream inputStream) {
<b>this</b>.inputStream = inputStream;
}
<b>public</b> <b>void</b> setProperty(String name, Object value) {
entryList.getList().add(new Entry(<b>null</b>, 0, name, value));
}
<b>public</b> <b>void</b> setProperty(String parent, <b>int</b> index, String name, Object value) {
entryList.getList().add(new Entry(parent, index, name, value));
}
...
}
Once the properties to substitute have been given using setProperty() above, we invoke compose() below, which handles multiple rows by looking for <tr> and </tr> elements with the appropriate CSS class.
<b>public</b> String compose() <b>throws</b> Exception {
BufferedReader reader = <b>new</b> BufferedReader(new InputStreamReader(inputStream));
<b>while</b> (true) {
line = reader.readLine();
<b>if</b> (line == <b>null</b>) {
<b>return</b> templateBuilder.toString();
}
line += "\n";
boolean trClosed = line.trim().equals("</tr>");
boolean tableClosed = line.trim().equals("</table>");
<b>if</b> (trClass != <b>null</b>) {
<b>if</b> (trClosed) {
rowBuilder.append(line);
templateBuilder.append(replaceRow());
rowBuilder.setLength(0);
line = <b>null</b>;
} <b>else</b> <b>if</b> (tableClosed) {
trClass = <b>null</b>;
} <b>else</b> <b>if</b> (rowBuilder.length() > 0) {
rowBuilder.append(line);
line = <b>null</b>;
}
} <b>else</b> {
trClass = getRow();
<b>if</b> (trClass != <b>null</b>) {
rowBuilder.setLength(0);
rowBuilder.append(line);
line = <b>null</b>;
}
}
<b>if</b> (line != <b>null</b>) {
templateBuilder.append(replace());
}
}
}
which relies on the following methods to do the donkey-work.
<b>protected</b> String getRow() {
<b>for</b> (String name : entryList.getParentList()) {
<b>if</b> (line.indexOf("<tr class=\"" + name) >= 0) {
<b>return</b> name;
}
}
<b>return</b> <b>null</b>;
}
<b>protected</b> String replaceRow() {
<b>int</b> size = entryList.getListSize(trClass);
StringBuilder builder = <b>new</b> StringBuilder();
<b>for</b> (<b>int</b> rowIndex = 0; rowIndex < size; rowIndex++) {
builder.append(replaceRow(rowIndex));
}
<b>return</b> builder.toString();
}
<b>protected</b> String replaceRow(<b>int</b> rowIndex) {
StringBuilder builder = <b>new</b> StringBuilder(rowBuilder);
Map<String, Object> rowMap = entryList.getMap(trClass, rowIndex);
<b>for</b> (String name : rowMap.keySet()) {
Object value = rowMap.get(name);
String pattern = "${" + name + "}";
<b>int</b> index = builder.indexOf(pattern);
<b>if</b> (index > 0) {
builder.replace(index, index + pattern.length(), value.toString());
}
}
<b>return</b> builder.toString();
}
<b>protected</b> String replace() {
Map<String, Object> valueMap = entryList.getMap(0);
<b>for</b> (String name : valueMap.keySet()) {
Object value = valueMap.get(name);
String pattern = "${" + name + "}";
<b>int</b> index = line.indexOf(pattern);
<b>if</b> (index > 0) {
<b>return</b> line.substring(0, index) + value + line.substring(index + pattern.length());
}
}
<b>return</b> line;
}
We use the following rough-shod classes to coddle the data to substitute into the template.
<b>class</b> EntryList {
List<Entry> list = <b>new</b> ArrayList();
<b>public</b> List<Entry> getList() {
<b>return</b> list;
}
<b>public</b> <b>int</b> getListSize(String parent) {
<b>int</b> maxIndex = 0;
<b>for</b> (Entry entry : list) {
<b>if</b> (entry.parent != <b>null</b> && entry.parent.equals(parent)) {
<b>if</b> (entry.index > maxIndex) {
maxIndex = entry.index;
}
}
}
<b>return</b> maxIndex + 1;
}
<b>public</b> Map<String, Object> getMap(String parent, <b>int</b> index) {
Map<String, Object> map = <b>new</b> HashMap();
<b>for</b> (Entry entry : list) {
<b>if</b> (entry.parent != <b>null</b> && entry.parent.equals(parent) && entry.index == index) {
map.put(entry.name, entry.value);
}
}
<b>return</b> map;
}
<b>public</b> Map<String, Object> getMap(<b>int</b> index) {
Map<String, Object> map = <b>new</b> HashMap();
<b>for</b> (Entry entry : list) {
<b>if</b> (entry.parent == <b>null</b> && entry.index == index) {
map.put(entry.name, entry.value);
}
}
<b>return</b> map;
}
<b>public</b> List<String> getParentList() {
List<String> parentList = <b>new</b> ArrayList();
<b>for</b> (Entry entry : list) {
<b>if</b> (entry.parent != <b>null</b> && !parentList.contains(entry.parent)) {
parentList.add(entry.parent);
}
}
<b>return</b> parentList;
}
}
where the above handles a list of the following tuples, allowing for a parent and index for rows.
<b>class</b> Entry {
String parent;
<b>int</b> index;
String name;
Object value;
<b>public</b> Entry(String parent, <b>int</b> index, String name, Object value) {
<b>this</b>.parent = parent;
<b>this</b>.index = index;
<b>this</b>.name = name;
<b>this</b>.value = value;
}
}
But of course one should keep it real with Apache Velocity or FreeMarker. I was just decided to have some trivial coding fun :)
- Login or register to post comments
- Printer-friendly version
- evanx's blog
- 1318 reads





