I don’t tend to use it myself (preferring the “spec” mechanism built-in to Stringtree), but today someone asked me if a Mojasef application could work with Spring.
After a bit of thought, some downloading, and a little playing, here is the result. My intention was to allow beans configured in a Spring application context to act as first-class citizens within a Stringtree context, so that they can be used seamlessly inside Mojasef application code and templates.
The implementation was actually pretty simple, essentially consisting of creating a class which implements org.stringtree.Fetcher and provides access to any Spring beans within a Spring context.
package org.stringtree.spring;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.stringtree.Fetcher;
public class SpringFetcher implements Fetcher {
private BeanFactory factory;
public SpringFetcher(BeanFactory factory) {
this.factory = factory;
}
public Object getObject(String name) {
Object ret = null;
try {
ret = factory.getBean(name);
} catch(BeansException e) {
ret = null;
}
return ret;
}
}
To use this class, you will need to create an instance of it, passing in a Spring BeanFactory or ApplicationContext object to the constructor. Then the SpringFetcher object can be used just as any other Fetcher.
It seems that when using Spring in a Mojasef application, what will likely be needed is a single Mojasef context which provides access to both Mojasef and Spring objects. To achieve this, simply wrap the two Fetchers in a FallbackFetcher:
Fetcher spring = new SpringFetcher(new XmlBeanFactory(new FileSystemResource("application.xml")));
Fetcher map = new MapFetcher();
Fetcher ff = new FallbackFetcher(spring, map);
An example Eclipse project containing the SpringFetcher class, some unit tests, and the jar files to compile and run them is available in sourceforge subversion at https://svn.sourceforge.net/svnroot/stringtree/projects/spring_example/trunk.
Posted by Stringtree as Mojasef, Projects at 9:56 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 »
Hot on the heels of the long-awaited and bountiful Stringtree 2.0.5 release comes a somewhat less momentous 2.0.6. I had tentatively planned to make the next release at the end of April 2007, but, as Jörn Zaefferer pointed out, I had forgotten something I had promised to do.
As an overall policy, all code in the Stringtree project and its subprojects should not depend on anything later than Java 1.4. Inadvertently, however, a dependency on a new interface (Iterable) introduced in Java 5 had crept into org.stringtree.json.JSONWriter.
It’s only a one-line change if you are using the source code, but this problem is is most significant for people who just wish to use Stringtree JSON “out of the box” with Java 1.4. So I have brought forward the 2.0.6 release to include it.
Also in this release is Jim Yingst’s improvement to the “bean” processing in JSONWriter which makes the code more usable with “partially public” objects (such as public inner classes).
Feel free to download the full Stringtree jar, the Stringtree source code, or the lean and mean Stringtree JSON jar.
Posted by Stringtree as JSON, Projects at 3:43 PM PDT
3 Comments »
It’s been a while since I put out an updated version of Stringtree. There have been a steady stream of changes and additions, so I reckon it’s time for a new one. Say hello to Stringtree 2.0.5.
Changes in this version:
- added templater test for calling a pipe on an object before multiplying
- added initial mock implementation of JDBC classes
- reintroduced some Tract Fetcher tests. Not all working yet, though.
- added quot and apos to XMLEscaper
- migrate juicer & tests
- migrate workflow and tests
- reformat Juicer code to new standard
- add juicer tests into main test sequence
- add workflow tests into main test sequence
- go through all code and check formatting
- write JSON validator
- add MapResultRowListener to collect name/value pairs from database results
- (spec) support use of @ and ! with no names as resource and filesystem includes
- (JSON) write documentation and examples
- (JSON) notify JSON.org
- modify build script to generate separate stringtree-json jar
One of the most significant changes is that JSON now gets its own sourceforge binary release and even its own web page. Stringtree JSON has been a surprising success, so I thought I’d make it easier for people to understand and use it without the rest of the Stringtree library.
Posted by Stringtree as JSON, Projects at 11:11 AM PDT
7 Comments »
As promised, I have eventually got around to writing the final part of the JSON trilogy, although maybe I should have waited until Friday the 13th
Along with the new JSONValidator class, I have also added validating versions of the existing JSONReader and JSONWriter classes so that those who wish to always validate can do so in a simple manner. By default the validator generates a simple error summary to standard output, although this behaviour is easily overridden by supplying a custom implementation of JSONErrorListener. As usual there are plenty of test cases to take a look at to see examples of how to use the various classes, and there are no external dependencies to you can plug these classes into your apps or frameworks without disturbing or requiring anything else.
All the files in the Stringtree JSON suite are currently available from subversion at
the tests are available from subversion at
or if you just want the meat, you can download the files individually:
Please check these out if you are interested in a fast, lean Java JSON implementation. I’d love to hear any comments and suggestions, ideally before I include these files in the next release of Stringtree.
Posted by Stringtree as JSON, Projects at 4:41 PM PST
15 Comments »
It seems that my lean Java JSON implementation has caught some people’s attention. Not only have helpful folks submitted patches on this blog, but it’s actually getting use in real applications. With any luck I’ll get a bit of slack to work on the missing validator compopnent soon.
In the meanwhile, I have fixed the problem in JSONReader reported by “Sonny Gill” and “jlist”, and added a test case for that behaviour. I’d really appreciate it if a few folks could check out the latest version from https://svn.sourceforge.net/svnroot/stringtree/trunk (or direct from JSONReader.java) and try it out.
There will probably be a new release of the full Stringtree package sometime in the next week or two.
Posted by Stringtree as JSON, Projects at 2:27 PM PST
No Comments »
A few days ago I was reading Slava Pestov’s blog about his continuing development of his language “factor”. It’s always interesting, but the post that got me coding was his mention of JSON, the “java script object notation”. In particular, the pointer to a large and rambling Java implementation.
I reckoned I could do a smaller/simpler one, so I had a go. I used a strict test-first methodology and came up with a JSON reader in about 190 lines of Java and a JSON writer in about 150 lines of Java. Both assume correct syntax, so the third part is a validator, which I haven’t done yet.
My examples are not as concise as the Factor implementations, but they are a whole lot smaller than the jroller one, and they have absolutely no external dependecies.
Unfortunately I’m having trouble with access to sourceforge at the moment, but when I get it sorted out this code (and all the tests which drove it) will be added to the Stringtree project.
[Update] The validator is now written. See this more recent post for details
Posted by Stringtree as JSON, Projects at 11:36 AM PDT
17 Comments »
There’s a new wiki comparison web site wikimatrix.org. I got an email from the organizers asking if I would like to add Friki to their list, and I happily did so. The list is filling up nicely (you can find the detals for Friki here), and has already prompted me to think about if/how I can add some of the other popular features to Friki.
While you’re at their site, don’t forget to check out their discussion forum, too.
Posted by Stringtree as Friki at 7:59 PM PST
1 Comment »