A major goal of the Stringtree software project has always been to be as compatible as possible with all the software people are using for their Java development. Naturally that also includes whatever Java version is being used.
For a long time I interpreted this goal as implying that all Stringtree code should run on all Java versions from Java 1.2 onwards. Java 1.4, however, introduced some compelling new features including built-in regular-expression handling. For a few years I still tried to ensure that most code was still 1.2-compatible (for example by using Ant to swap in a third-party regular-expression library while building a jar file), while also providing a Java 1.4 version. Eventually, use of Java versions prior to 1.4 declined enough that I felt comfortable removing the complicated pre-1.4 version.
For the last few years I have been very careful to keep all my Stringtree code compatible with all versions of Java from 1.4 upwards. Now, however, the pressure is building again to move over to Java 5. In my day-to-day coding I develop with Java 5 and make increasing use of Java 5 features such as the enhanced for loop, the Iterable interface, enums, generics, autoboxing, varargs and so on. It would be very nice to be able to update the Stringtree codebase to use these features too.
Occasionally a Java 5-specific detail has crept in to a Stringtree library, and I have soon received comments or emails pointing this out. I haven’t noticed this for a while, which might indicate either that I have been especially careful, or that I there are no longer any/many people developing with Stringtree code who are still limited to Java 1.4.
If you are reading this and you still require Java 1.4 support, please let me know. Likewise, if you have thrown off the shackles of 1.4 within the last year or so or are desperately hoping for a Java 5 Stringtree that would be good to know too.
Is it time for Java 5 yet?
Posted by Stringtree as Friki, General, HTTPClient, Inkling, JSON, Mojasef, Projects, Templater at 7:59 PM PDT
No Comments »
One of the most common “enterprise” uses for the Stringtree Templater is to generate data transfer formats from Java object models. I showed a few posts ago a little bit of how you can generate JSON or XML from an object model.
However, if you want to generate a fairly complex XML document with lots of different elements, the approach I described of using a separate template for each element name can get tedious pretty quick. A colleague asked me about this, and I told him I’d look for a smarter solution. To come up with this technique I had to dig fairly deep into the way Stringtree Templater works, but once it is out in the open, it’s easy to apply, and very re-usable. Here’s how it works:
Let’s imagine we want to generate the XML for a simple case - a FullName object with accessors getForename() and getLastname(). If we create such an object new FullName("Frank","Carver") we would expect the following XML:
<person>
<forename>Frank</forename>
<surname>Carver</surname>
</person>
First, we need a template for each element. This is pretty simple, let’s call it element.tpl:
<${name}>${value}</${name}>
Now all we need to do is store context objects “name” and “value” with the correct contents and invoke the element template. We could do this using the assignment syntax for each element (e.g. ${name=’forename’}${value=this.forename}${*element}) but this is pretty clumsy, too, and only partly addresses the problem.
A better solution would be to use the ability of Stringtree Templater to call out to Java objects. There are plenty of options for this, but the one we will use is the same method Stringtree Templater uses to provide an intuitive syntax for accessing Map entries. When Stringtree Templater encounters an expression such as ${object.field} it first tries “JavaBean” accessors ( object.getField(), object.isField() ); if they are not available it tries the field name as a regular method (object.field()); if that fails too it tries a general “get” method ( object.get("field") ). Each of these methods are tried with and without an optional parameter referring to the enclosing context.
With all that in mind, we need a context tool which, when asked to “get” a field, places the field name in the context as “name”, and the field value in the context as “value”. Unfortunately, we have no obvious way of passing in the object which supplies these fields. The solution is to work with Stringtree Templater and to realise that in the great majority of cases the object being considered will be in the context as “this”.
The final part of the context tool “get” method is its return value. In this case I choose to return a boolean indicating whether the “this” object has such a field. That way, the return value of the template expression can be used to determine whether or not to generate optional elements in the resulting XML. One version of the context tool might look like:
import org.stringtree.Fetcher;
import org.stringtree.fetcher.BeanFetcher;
import org.stringtree.finder.StringKeeper;
public class BeanTool {
public boolean get(StringKeeper context, String name) {
Fetcher fetcher = new BeanFetcher(context.getObject("this"));
Object value = fetcher.getObject(name);
if (null != value) {
context.put("name", name);
context.put("value", value);
return true;
}
return false;
}
}
Note that this is just a regular Java class (a “POJO”), with no need to extend or implement any Stringtree classes. In this case it uses some Stringtree code to manipulate the template context, but tools for other purposes would likely not need that.
Next, a template to tie it all together, let’s call it person.tpl Not that this template assumes that our new tool has been placed into the context as “tool”.
<person>
${tool.forename?*element}
${tool.surname?*element}
</person>
Note the “?” indicating the conditional execution of the next section, and the “*” indicating inclusion of a template fragment.
Finally, a bit of Java (in this case, in the form of a JUnit test case) to show how it all fits together:
import junit.framework.TestCase;
import org.stringtree.Repository;
import org.stringtree.fetcher.MapFetcher;
import org.stringtree.template.EasyTemplater;
public class EasyTemplaterTest extends TestCase {
Repository templates;
EasyTemplater templater;
public void setUp() {
templates = new MapFetcher();
templater = new EasyTemplater(templates);
}
public void testXMLElementExpansionSequence() {
templater.put("frank", new FullName("Frank", "Carver"));
templater.put("bean", new BeanTool());
templates.put("tpl1", "${frank*person}");
templates.put("person", "<root>${bean.forename?*element}${bean.surname?*element}</root>");
templates.put("element", "<${name}>${value}</${name}>");
assertEquals("<root><forename>Frank</forename><surname>Carver</surname></root>",
templater.toString("tpl1"));
}
}
Have fun!
Posted by Stringtree as Projects, Templater at 9:52 PM PDT
No Comments »
A new release of Stringtree is now available at Sourceforge
This release contains the following changes:
- added smarter string parameter parsing to DriverManagerDataSource for use from spec files
- made Spliterator constructor parameters more sensible for common usage
- alternate XML parser to support an output object tree compatible with JSON
- added DataReader interface and both XML and JSON implementations
- added AgnosticDataReader which detects and correctly parses both XML and JSON.
- added EasyTemplater to wrap up commmon usage cases
Recently, several people have been asking for more (ok, any at all) documentation on Stringtree, so here is the first of an occasional series of usage tips.
Get into Templating using EasyTemplater
One of the most popular bits of the Stringtree codebase is the Stringtree Templater. The basic aim of this is to help separate output formatting from data structures and behaviour. Stringtree Templater is a general-purpose templating system roughly equivalent to something such as Velocity, Smarty and many others. The main differences between Stringtree Templater and most others are that it allows refactoring and reuse of templates at a very fine-grained level, it has a concise syntax, and it is fast in use.
Up until now, the main issue in the way of adoption of Stringtree Templater was that its flexibility of configuration got in the way of simple use. The ability to load templates and substitution values from a wide range of different sources led to some confusion about exactly how to set it up for common cases.
With this in mind, Stringtree 2.0.8 now includes EasyTemplater, a wrapper which provides most of the power of Stringtree Templater without the head-scratching.
To use Stringtree Templater, you really only need to tell it where to find its templates. In the simplest case, this might be from a location on the file system. For example:
import org.stringtree.template.EasyTemplater;
…
EasyTemplater templater = new EasyTemplater("/some/directory/somewhere/");
…
Note the "/" at the end of the directory name. If you use this constructor, which takes a single String, it’s important that you include this. Under the covers, this constructor treats the location as a URL, defaulting to a "file:" URL if no scheme is supplied; you can just as easily load templates from a web server using an http: URL, or from the classpath using a classpath: URL, for example.
If you wish to be more precise about your template location, there are also constructors which take a File or a URL.
When you have created an EasyTemplater, using it is as simple as placing some values in its internal "context" and calling one of two methods String toString(String templateName), or byte[] toBytes(String templateName). These methods will look up the named template, expand it by substituting values from the internal context as required, and return either a String or an array of bytes.
As a more fully worked example, imagine that we have a directory in the current classpath named "templates". In this directory we have two files: person.tpl containing
Name: ${this.name}, Telephone: ${this.phone}
and list.tpl containing
Please contact the following people
${people*person/}
and tell them about ${product}!
Here’s a complete example using these templates:
// Somebody.java
// a "bean" to hold a person's contact details
// no dependencies on any Stringtree classes
public class Somebody {
private String name;
private String phone;
public Somebody(String name, String phone) {
this.name = name;
this.phone = phone;
}
public String getName() { return name; }
public String getPhone() { return phone; }
}
// Example.java
import org.stringtree.template.EasyTemplater;
public class Example {
public static void main(String[] args) {
EasyTemplater templater = new EasyTemplater("classpath:templates/");
Somebody[] folks = new Somebody[] {
new Somebody("Gordon Brown", "07112345678"),
new Somebody("George Bush", "1-555-123-4567")
};
templater.put("people", folks);
templater.put("product", "EasyTemplater");
System.out.println(templater.toString("list"));
}
}
If all goes well, you should see something like:
Please contact the following people
Name: Gordon Brown, Telephone: 07112345678
Name: George Bush, Telephone: 1-555-123-4567
and tell them about EasyTemplater!
That’s about it for today. Happy templating 
Posted by Stringtree as Projects, Templater at 10:29 AM PDT
No Comments »
Sometimes it’s the little things that make a disproportionately big difference.
Several people have been making fairly big use of the Stringtree template system recently, both as part of Mojasef and for general purpose text generation. One of the things it’s particularly good at is rendering collections and sequences. For example, if there is an ArrayList of the Integers 1, 17 and -29 stored in the template context as “stuff”, then the following template will generate a comma-separated list of the values in brackets:
[${stuff*/','}]
The ${ and } indicate the start and end of a template token. Within the token stuff is the name of the context value, the * indicates that it should be “multiplied” (i.e, each element rendered in sequence), and the /’,’ indicates that the elements should be “divided” by commas.
Expanding this template gives:
[1,17,-29]
Now, this is all fine if the values in the collection can be directly output. Now imagine that we have a collection of three Strings: ‘hello’, ‘there’ and ‘world’, and we want to generate a similar sequence, but with each String surrounded by quotes. For this we need two templates:
[${stuff*quote/','}]
and a new tamplate (named quote.tpl)
"${this}"
In this case the collection is “multiplied” by the template “quote”, i.e, each element of the collection is placed in the context in turn as “this”, and the template “quote” is evaluated. The resulting values are then “divided” by the commas.
Expanding this template gives:
["hello","there","world"]
The astute amongst you will have noticed that all of the above can actually be done by JSONWriter, but the templating sytstem is much more flexible, and works just as well generating HTML, XML and any other text format.
Now to the point of this article. I have found it increasingly common when generating XML to want to emit a sequence separated by newlines. This can easily be done using the above syntax by giving a newline as a “divide” string:
[${stuff*quote/'
'}]
But this has always looked clumsy to me.
So, in Stringtree 2.0.7 I have finally got around to making a neater syntax for this. Now, if you use a / to indicate a “divide”, but do not supply a separator, the template system will default to a newline. If you don’t want any separators at all, you can still just leave off the /. So lets try the new, neater, way to write a newline-separated chunk of XML from the same collection of Strings:
<quotes>
${stuff*quote/}
</quotes>
and the template quote.tpl
<quote>${this}</quote>
Expanding this template gives:
<quotes>
<quote>hello</quote>
<quote>there</quote>
<quote>world</quote>
</quotes>
A very small change, but one which makes a common task a lot nicer.
As a side note, in 2.0.7 I also brought out from my dusty archives an old XML parser I wrote years ago and integrated it with the current Stringtree code base. Feel free to play with it, but I don’t really consider it ready for “prime time” yet.
Posted by Stringtree as Projects, Templater at 10:12 PM PDT
No Comments »