25 July 2007

More of the same

Another Example

Continuing from yesterday, here's another example of the quick-n-dirty development technique I was describing. Before I go too much further, I need to explain a couple of things:

Velocity

I mentioned Velocity yesterday. For those that don't know, Velocity is a text templating package. You create templates containing tags (marked with $ signs), and provide a Context, which is basically a Map. Velocity can then merge the template with the context to create output which is usually written directly to a Writer. As an example, I might have this template (let's call it MyTemplate.txt):

Dear $firstname,

Here is the list of books you have ordered:

#foreach( $book in $booklist )
$book
#end

Regards...


In my code, I can create the context and merge with the template:

// Create the Context
Map<String, Object> map = new HashMap<String, Object>();
map.put("firstname", "Fred");
List<String> books = new ArrayList<String>();
books.add
("Quidditch Through the Ages");
books.add
("Fantastic Beasts & Where to Find Them");
map.put("booklist", books);
VelocityContext ctx = new VelocityContext(map);

// Merge - assumes we already have a VelocityEngine
velocityEngine.mergeTemplate("MyTemplate.txt", ctx, writer);


The result would look like this:

Dear Fred,

Here is the list of books you have ordered:

Quidditch Through the Ages
Fantastic Beasts & Where to Find Them

Regards...


iText

iText is an open-source package that (among other things) makes it easy to create PDF documents. You use it by creating a Document object then calling API methods to add paragraphs, images, and tables, set fonts, colours, text alignments and so on.

Back to the plot...

Let's say you want to be able to use Velocity templates to generate PDF documents. As things stand there's an immediate problem: Velocity needs a Writer to deliver its output to, but iText doesn't include one - and even if it did, how would it know when to insert tables, text font/colour changes, page breaks and so on?

Here is one possible solution: create an ITextWriter class, subclassing Writer, which will bridge the gap. It makes the necessary calls to the iText API according to directives it finds embedded in its input data.

The way I did this was to add the input characters from the write() calls to a StringBuffer, then look at the buffer to see if I had a complete line of text. If the line starts with a '[' and ends with a ']', it's taken to be a directive line. All other lines are treated as text to be added to the document using the current style (font, colour, alignment etc.).

So let's say I have a Velocity template that looks like this:

Here is the report for
[bold]
[color red]
$date
[normal]
[newline]
at
[font verdana 16]
[bold]
[color blue]
$time
[normal]


Velocity processes this and directs the output into the ITextWriter:

Here is the report for
[bold]
[color red]
Wednesday, July 25 2007
[normal]
[newline]
at
[font verdana 16]
[bold]
[color blue]
12:47pm
[normal]


Now, here's where the technique I described yesterday comes into play. When a directive line is found the code breaks out the first keyword (color, for example) as the directive name and places the rest into a String[] array. Then it locates a method with the directive name that accepts a String[] argument and calls it, passing the array as the argument. The method makes the necessary calls to the iText API to take care of getting the data out to the PDF document in the specified format. The result:

Here is the report for Wednesday, July 25 2007
at 12:47pm


The great thing about this is that if I need some new formatting control, all I need to do is add a new method with a meaningful name - and the job's done:

void pagebreak(String[] args)
{
...
}


In fact, the first version of the writer only had perhaps half a dozen directive methods and I added new ones as I went along, as I found I needed them for the project I was working on. The current version has about twenty methods, and most of the new methods took only minutes to create and test.

Labels:

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]



<< Home