Skip to main content

Trivial Templating

Posted by evanx on July 9, 2010 at 10:20 AM PDT

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.

[prettify]
  

Hey ${displayName}  

Your Travelstart reference: ${bookingReference}  

                             
${travelerName}
[/prettify]

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.

public String generate(String resourceName) {
    HtmlTemplate template = new 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");
    return template.compose();
}

This uses the trivial templating class below to read the HTML template, and substitute the given values into the template, etc.

public class HtmlTemplate {
    List lineList = new ArrayList();
    InputStream inputStream;
    StringBuilder templateBuilder = new StringBuilder();
    String trClass = null;
    StringBuilder rowBuilder = new StringBuilder();
    String line;
    EntryList entryList = new EntryList();

    public HtmlTemplate(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    public void setProperty(String name, Object value) {
        entryList.getList().add(new Entry(null, 0, name, value));
    }

    public void setProperty(String parent, int 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 and elements with the appropriate CSS class.

[prettify]
    public String compose() throws Exception {
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        while (true) {
            line = reader.readLine();
            if (line == null) {
                return templateBuilder.toString();
            }
            line += "\n";
            boolean trClosed = line.trim().equals("");
            boolean tableClosed = line.trim().equals("");
            if (trClass != null) {
                if (trClosed) {
                    rowBuilder.append(line);
                    templateBuilder.append(replaceRow());
                    rowBuilder.setLength(0);
                    line = null;
                } else if (tableClosed) {
                    trClass = null;
                } else if (rowBuilder.length() > 0) {
                    rowBuilder.append(line);
                    line = null;
                }
            } else {
                trClass = getRow();
                if (trClass != null) {
                    rowBuilder.setLength(0);
                    rowBuilder.append(line);
                    line = null;
                }
            }
            if (line != null) {
                templateBuilder.append(replace());
            }
        }
    }
[/prettify]

which relies on the following methods to do the donkey-work.

[prettify]
    protected String getRow() {
        for (String name : entryList.getParentList()) {
            if (line.indexOf(" 0) {
                builder.replace(index, index + pattern.length(), value.toString());
            }
        }
        return builder.toString();
    }

    protected String replace() {
        Map valueMap = entryList.getMap(0);
        for (String name : valueMap.keySet()) {
            Object value = valueMap.get(name);
            String pattern = "${" + name + "}";
            int index = line.indexOf(pattern);
            if (index > 0) {
                return line.substring(0, index) + value + line.substring(index + pattern.length());
            }
        }
        return line;
    }
[/prettify]

We use the following rough-shod classes to coddle the data to substitute into the template.

class EntryList {
    List list = new ArrayList();

    public List getList() {
        return list;
    }

    public int getListSize(String parent) {
        int maxIndex = 0;
        for (Entry entry : list) {
            if (entry.parent != null && entry.parent.equals(parent)) {
                if (entry.index > maxIndex) {
                    maxIndex = entry.index;
                }
            }
        }
        return maxIndex + 1;
    }

    public Map getMap(String parent, int index) {
        Map map = new HashMap();
        for (Entry entry : list) {
            if (entry.parent != null && entry.parent.equals(parent) && entry.index == index) {
                map.put(entry.name, entry.value);
            }
        }
        return map;
    }

    public Map getMap(int index) {
        Map map = new HashMap();
        for (Entry entry : list) {
            if (entry.parent == null && entry.index == index) {
                map.put(entry.name, entry.value);
            }
        }
        return map;
    }

    public List getParentList() {
        List parentList = new ArrayList();
        for (Entry entry : list) {
            if (entry.parent != null && !parentList.contains(entry.parent)) {
                parentList.add(entry.parent);
            }
        }
        return parentList;
    }
}

where the above handles a list of the following tuples, allowing for a parent and index for rows.

class Entry {
    String parent;
    int index;
    String name;
    Object value;

    public Entry(String parent, int index, String name, Object value) {
        this.parent = parent;
        this.index = index;
        this.name = name;
        this.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 :)

Related Topics >>