• Home
  • RSS Feed
  • Log in

Pecia: Towards a Fluent Interface for Building Documents
Posted by Wilfred Springer in the early morning: June 25th, 2009

1. Introduction

There is a chance that - after having read this article - you conclude that nobody in a sane state of mind would ever use what this article is going to suggest. Let me therefore start with disclaimer: I have never made any public claims regarding my state of mind.

Apart from that, I figure an article about a technology almost nobody is using, is still way more interesting than an article everyone is using. In fact, I guess the more senior you are, the more things you have already seen before, the less likely it is you will be you will be interested in something people already did many times before. Based on that, you might as well say that the most experienced people around will probably be interested in stuff that nobody is using. This article is for those people.

With that out of the way: Pecia is a new way of generating documentation from your Java applications. You will probably wonder why we need yet another way of generating documents from Java, and I have to admit that the Java world is not in a bad shape if it comes down to the number of frameworks allowing you to generate documents. However, Pecia takes another stab at it, and I just had to see if it would work. You be the judge whether it makes sense.

2. Background

I clearly remember the day on which I spend more time than I am willing to admit on finding out why my Maven report was not producing well-formed HTML. Definitely not one of my finest moments.

Maven reports are built using an API called Doxia. It's not a general purpose template-based text generating mechanism, like Velocity, FreeMarker or StringTemplate. In fact, there is no template at all. Instead, Doxia provides an API for building a document, abstracting the final representation of that document.

In a way, the most important interface in Doxia is the Sink interface. The Sink interface is basically the builder interface. It has operations to start the document body, to start a section, to generate text, to start a table, a table row, a table cell, and many other document chunks, and then a slew of operations to finish all of those. Example 1, “Doxia Sink usage” shows an excerpt of some of my code using Doxia.

Example 1. Doxia Sink usage

sink.body();
sink.sectionTitle1();
sink.text("Message Catalog");
sink.sectionTitle1_();
sink.table();
sink.tableRow();
sink.tableHeaderCell();
sink.text("Type");
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text("Identifier");
sink.tableHeaderCell_();
sink.tableHeaderCell();
sink.text("Message");
sink.tableHeaderCell_();
sink.tableRow_();
...


Unmistakeably, there is a correspondence between the Sink interface and a subset of the HTML content model. However, HTML is not the only type of document that can be generated from Doxia. In fact, by abstracting the interface from the implementation, Doxia is capable of generating any type of output document. Because of that, it generates PDF just as easy as HTML.

Now, there is one limitation in the Doxia approach: you are never really sure if your code is building a valid document. The API does not prevent you from adding an image to a table row, or inserting text in a table row outside of a table cell. And since Doxia aims to abstract you from the target representation, it's quite hard to make any assumptions on what is or is not considered to be valid.

This was in fact the reason why I spend so much time in completing my Maven report. It turned out I was 'building' the wrong type of document elements at the wrong time. The API did not prevent me from doing it, and the framework did not warn me at runtime. Which made me wonder....

3. Pecia

It made me wonder if it would not be possible to enforce validation at compile time. Would it not be much nicer to have the API prevent me from adding images to table rows, or - for instance - from adding a fourth table cell to a three-column table? Would it not be much nicer if the API would prevent me from making any mistakes like these? And would it not be great if the API would be a fluent API[]?

From my perspective, the answer to all of these questions was: yes, that would be much nicer. It would allow me to spot errors quickly, and moreover, it would make my IDE's code completion assistance actually become valuable. With Doxia's Sink interface, your IDE will offer you the choice of adding images to a table row. In a framework that enforces proper document structure through its API, your IDE would never consider that to be a viable option.

4. Pecia API Principles

4.1. Context-based

The API offered by Pecia depends on on your context. This is probably best explained with an example.

Example 2. Pecia API Usage

Article article = ...;
ItemizedList list = article.itemizedList();
list.item("first item");
list.item("second item");


So what happens in the example above? Well, first you create the target document. More on that later. From that point on, you can add content to the Article. Once you add an itemized list, the API returns an ItemizedList instance, an object representation of that new context. If you want to add content to the itemized list, you need to invoke operations on the ItemizedList instance. In this case, the example adds two items to the itemized list.

Just like Doxia, Pecia is also backed by a number of implementations of the API. At this stage, it supports both DocBook and HTML output. Given the sample code given above, a simple HTML implementation would generate HTML output like this:

<html>
  <body>
    <ul>
      <li>first item</li>
      <li>second item</li>
    </ul>
  </body>
</html>

4.2. Method Chaining

Now, the example given above is not really illustrative for the way you would write code with Pecia. With method chaining, you can create the document without declaring variables to hold every intermediate content model element, and create a interface that bears a greater similarity with the way you would normally create documents in markup languages in, say, HTML or DocBook. Let us just say, a more fluent interface. So instead of writing the code given above, in the previous chapter, you can write code like this:

Example 3. Method chaining

Article article = ...;
article.itemizedList()
  .item("first item")
  .item("second item");


4.3. Shorthand Notations

This is another sample document:

Example 4. Mixed 'Expanded' and Shorthand Notations

article
  .author("Wilfred Springer")
  .copyright("agilejava.com", 2008)
  .para()
    .text("This is the ")
    .emphasis("first")
    .text(" paragraph.")
  .end()
  .para("And this is the second.")
.end()


Which will generate something similar to this:

<html>
  <body>
    <p>This is the <em>first</em> paragraph.</p>
    <p>And this is the second.</p>
  </body>
</html>

The important principle illustrated here is that Pecia both has shorthand notations as well as more verbose notations for specifying content. The simple para(String) operation (illustrated by the second paragraph in the example) starts the paragraph, adds text to it and closes the paragraph. So, it basically expands to this:

.para()
  .text("And this is the second.")
.end()

The principle does not only apply to paragraphs. It also applies to other document elements, such as list items, table cells and footnotes. In all of these cases, you can add that document element using a simple operation accepting a String with the text to be embedded within that document element, or by calling an operation without any arguments, which will change the context into the context of that document element.

Let's take an API snippet as an example. Example 5, “Pecia API Snippet” shows the signature of some operations on Para, the interface implemented by paragraphs. As you can see, there are two different footnote operations. They are different in a number of ways.

First of all, the first one takes a String argument, and the second does not. The first operation will create footnote, add a paragraph, and add text to the paragraph in a single call. Once it is done, the entire footnote is considered to be done. The context is no longer the footnote that was just added the paragraph. The context is - again - the paragraph itself.

Example 5. Pecia API Snippet

interface Para<T> {
  ...
  Para<T> footnote(String text);
  Footnote<? extends Para<T>> footnote();
  ...
}


The other footnote operation does not take a String argument. The API assumes you are not interested in adding an empty footnote (why would you?), and changes the current context into the context of the footnote. From that point on, you can only invoke operations defined by the Footnote interface, until you finally consider yourself to be done with the footnote and call its end() operation, which will restore the original context.

4.4. Tables

Tables deserve some special attention. In order to preserve a valid document structure, you not only want to restrict table cells to table rows; you also need to make sure that every row contains exactly the same number of cells.

Enforcing this property of tables proved to be challenging. Before going into details, let us first look at an example:

Example 6. A table in Pecia

article.table2Cols()
  .header()
    .entry().para("col1")
    .entry().para("col2")
  .end()
  .row()
    .entry().para("foo")
    .entry().para("bar")
  .end()
  .row()
    .entry().para("foo")
    .entry().para("bar")
  .end()
.end();


Example 6, “A table in Pecia” illustrates how to build a table that more or less corresponds to this HTML table:

<table>
  <tr>
    <th><p>col1</p></th>
    <th><p>col2</p></th>
  </tr>
  <tr>
    <td><p>foo</p></td>
    <td><p>bar</p></td>
  </tr>
  <tr>
    <td><p>foo</p></td>
    <td><p>bar</p></td>
  </tr>
</table>

So what exactly is happening in Example 6, “A table in Pecia”? Well, first the table2Cols() operation constructs a table of two columns. The object getting created will allow only operations on tables of two columns.

The first thing we do after that, is adding a table header, by calling header() on the table. Since it is a two column table, the header accepts only two cells. Any attempt to add more or less then those two cells will give compilation errors.

Every table cell is getting constructed by calling entry(). The resulting context is a table cell. There are a number of things you can add to a table cell, such as paragraphs. Once you are done with the cell, you either call entry() or end(). Calling entry() will create the next table cell. Calling end() will mark the end of the current table header. And because of the way Pecia has been constructed, you can only call end() after the last table cell, and only call entry() before the last table cell.

Table header are added in exactly the same way as table rows; only in this case you call row() instead of header().

4.5. Metadata

Some document elements can have metadata associated to it; it often involves data that is not necessarily part of the main document flow. In cases like those, Pecia allows you to specify metadata at the start of the document element to which it is pertaining.

Let us take an article as an example. An article can have an author. At the beginning of an article, before adding any content to the article, you can add metadata like the author's name. Once you have started adding content to the article, it is impossible to add any more metadata. Example 7, “Article metadata” shows you a valid way of using it. Example 8, “Illegal article metadata” illustrates an invalid way of specifying metadata; the compiler will not accept any more metadata after content has been added to it.

Example 7. Article metadata

article
  .author()
    .firstname("Wilfred")
    .surname("Springer")
  .end()
  .para("This is the first paragraph.");



Example 8. Illegal article metadata

article
  .para("This is the first paragraph.")
  .author()
    .firstname("Wilfred")
    .surname("Springer")
 .end()


5. Using Pecia

In the previous section, you have seen most of the basic principles behind Pecia. However, you have not really seen how you actually make sure that some output document is generated as a result. That was done deliberately. The important thing here is the API outlined above. How you actually get your hands on an actual implementation, and how that implementation will treat handle the documents you are building is implementation specific.

Fortunately, Pecia does come with an implementation. So this is how you use the implementation:

Example 9. Producing HTML

// The standard implementation uses a wrapper around STaX to
// produce XML documents.
XmlWriter writer = new StreamingXmlWriter(...);

// The DocumentBuilder will actually produce the output.
DocumentBuilder builder = new HtmlDocumentBuilder(writer);

// But if we are building documents, we need to have an
// implementation of the interfaces mentioned above. Let's wrap
// the DocumentBuilder in an Article implementation. (The
// second argument is the Article's title.)
ArticleDocument document =
  new DefaultArticleDocument(builder, "Example");

// ... and now we can build the document.
document
  .section("First section")
    .section("First subsection")
    .end()
  .end()
.end();


The standard Pecia implementation will generate the output on the fly. Technically, there is nothing preventing you from creating the entire document in memory first, and generating output afterwards. So all of this is all just implementation. In fact, I suspect some significant changes in the implementation, somewhere in the future; use this implementation at your own risk.

6. Pecia State

After having read the previous section, you probably already guessed that Pecia is not done yet. It is usable, and it is actually in use in one of my projects, but there is still work left to be done. So this article is in a way covers an alpha version of the API.

7. Pecia Document Object Model

The document object model supported by Pecia today is fairly simple. In fact, it is probably way too simple. Which is another reason why Pecia there is no 1.0 version of Pecia yet.

Figure 1. Pecia Document Object Model

Pecia DOM


Figure 1, “Pecia Document Object Model” provides a schematic overview of the document object model supported. The arrows denote potential containment relationships: a list item can contain paragraphs, sections can contain tables, lists, verbatim content and other sections, etc. The document elements supported are pretty self-explanatory. The only exception may be xref, which represents an internal reference to another part of the document.

8. Summary & Conclusions

In this article, I have tried to justify the creation of yet another framework for generating documentation. It grew out of unease with the existing solutions, and then turned to have a couple of interesting side effects. Pecia not only prevents you from breaking the document structure in the documents you generate at compile time, but also supports the IDE in preventing you to make these errors altogether, at coding time.

In fact, there are some benefits that I haven't even covered yet. They might be less tangible, but nevertheless, very real. I started to see that benefit when applying Pecia in a project where I had 40-something small objects floating around, each of which needed to be represented differently in my document, depending on the context.

In situations like those, many of the existing frameworks commonly in use for generating documents will force you have all of these 40-something objects expose their state to the outside world, in order to be able to bind to it from an external template.

However, this violates one of the most important principles of object orientation: the encapsulation principle. By having the objects expose their entire state to the outer world, you have actually increased the dependencies between the outer world and the object's implementation, and instead of defining behaviour as part of the object, the behaviour (how to represent itself) is externalized entirely. Consequently, maintaining the templates becomes a nightmare; your templates need to have a deep understanding of the internals of all objects.

Doxia would already allow you to take a different approach: you could potentially define a common interface on all objects allowing each object to render itself using the Sink interface. However, how would you convey the context in which the content needs to be written? How would you make sure that your object understands that it needs to display itself as part of a paragraph. Or as a table? How do you make sure that the object does not write outside of the context expected by the calling program?

void render(Sink sink) {
  // doh, how would I know if I am in a paragraph or table context
}

In Doxia, there is no way of solving this. In Pecia, this is trivial. The common interface would simply define a single operation for each context in which the object needs to render itself:

void render(Para<?> para) {
  // ah, I need to render it as part of a paragraph
  // end there is no way of writing outside of that context.
}

Which is only to say: there does seem to be a case for frameworks like Pecia. True, the current incarnation of Pecia is still quite young, and there are definitely things that will change in due time, but I have come to believe that it has potential, and I hope to have convinced you about that too.

Pecia is currently hosted at SourceForge (http://pecia.sourceforge.net/).

[] See http://www.martinfowler.com/bliki/FluentInterface.html.

  • Share/Bookmark

Filed under Java | 2 Comments »



2 Responses to “Pecia: Towards a Fluent Interface for Building Documents”



    Anthony Gatlin Says:
    Posted at: August 29, 2009 at 8:44 am

    I think this is a very interesting idea but there are a few wrinkles that must be considered. As you understand, the primary idea behind method chaining is that every method returns a type from which any available methods on that type can be called. If the output of every method was a string, this would cause incredible performance problems since strings are immutable and have to be recreated with each modification. However, if the methods which were called returned some implementation of something like StringBuilder, then the final document would not have to be rendered until it was complete. All of the method chaining could occur on the StringBuilder object or its derivatives. Your idea has great promise. I would be interested in discussing this idea further. Thanks for sharing it!



    Wilfred springer Says:
    Posted at: August 30, 2009 at 4:32 pm

    Hi Anthony,

    Pecia does not return Strings; if you call .para() on an object, you cat a Para implementation upon which you can call other methods, for creating paragraph content. Those objects are basically shortly-living objects. In the current implementation of Pecia (found here: http://pecia.flotsam.nl/), data will be written to an StAX type of document writer as quickly as possible.



Leave a Reply

Click here to cancel reply.

Deployment automation for Java application running on Websphere, WebLogic and JBoss

Archives

  • January 2010
  • December 2009
  • November 2009
  • October 2009
  • September 2009
  • August 2009
  • July 2009
  • June 2009
  • May 2009
  • April 2009
  • March 2009
  • February 2009

Xebia Sites

  • Xebia Corporate
  • Xebia France
  • Xebia India

Categories

  • Java (279)
  • Agile (109)
  • General (50)
  • Testing (42)
  • Performance (42)
  • Hibernate (36)
  • Scrum (33)
  • Podcast (31)
  • Architecture (31)
  • Spring (28)
  • SOA (24)
  • Maven (22)
  • Project Management (22)
  • Flex (17)
  • JPA (17)
    • JPA implementation patterns (13)
  • Eclipse (15)
  • Quality Assurance (14)
  • Middleware (19)
  • Frameworks (13)

Tag Cloud

    Introduction to Agile Lean Closures Seam fitnesse Hibernate Scrum Poppendieck SOA product owner Ajax XML Grails Xebia IntelliJ Groovy Agile Awareness Workshop Agile Java Spring Performance Architecture Semantic Web Testing qcon Maven Functional Programming esb Scala JavaOne
medicin depression buy phentermine without a perscription aricept generic hair loss help how do you prevent bone loss urinary tract infection symptoms viagra sex domination cialis viagra cures for throat infection buy sumycin acne care new medication for cancer treatment help for sleeping problems on-line pharmacies cure snoring medications to help clot blood what is aspirin buy zestoretic bronchitis vs pneumonia back pain muscle acne face medication muscle women pain behind knee fat blocker man health arthritis natural cure woman health women insomnia cheap phentermine online cats and irritable bowel syndrome buy cialis generic online nutritional diet for osteoporosis abnormal blood clots treatments for hair loss what is zyprexa dental whitening products impotence herbs drugs for diabetes allergy prevention buy canada levitra Mentax adhd in children hair loss in woman medicines for blood clot online imitrex viagra buy free dog products clindamycin drug how to stop hair loss chloramphenicol discount drug viagra what valium does permanent hair loss heart failure medicine avapro 150mg ordering viagra online food allergies order viagra online online viagra prescription carisoprodol mg improve your skin discount erectile dysfunction medication buy xanax online buy order viagra scabies teatments information allegra vitamine b1 diazepam breast cancer support free stop smoking cipro side effects ultram cheapest treatment attention deficit disorder discount vitamins supplements how to get viagra online synthroid buy cheapest cialis zyrtec online how to clear acne preventive osteoporosis immune stimulants what is hoodia On Line Viagra getting over the pain diflucan dosage health asthma online stores hair loss products blood clot drugs colon parasites hair loss products discount medicine pravastatin buy griseofulvin tablets order indomethacin dog health products how to take a beta-blocker diazapan is valium treating cold sores chronic pain drug what is osteoporosis stress drug tooth whitening lowering cholesterol naturally legality of buying cialis online order levitra treatment for insomnia cheapest cialis index depakote overdose alprazolam condom sales treatment of yeast infection xanax sales taking viagra after cialis how to control pain new birth control chest pain health prozac prescription blood clots viagra in mexico chlamydia pill cancer drugs cold flu drugs how do i order viagra online super viagra acyclovir medicine benadryl dosage erythromycin pregnancy buy contoured condom chronic muscle pain pet health dogs treatment attention deficit disorder dental teeth whitening asthma medicine free prescription drugs herpes drug diabetes treatment buy tooth whitening gel cheap fast valium generic levitra buy cheapest viagra online lopressor drug pharmacy drug prices ultram dosing treatments for bipolar disorder neurontin withdrawal parasite medication chlamydia tips for increasing breast size ways to enhance breast what is valium used for metformin tablet order birth control hair loss for men how does xanax work treatment hepatitis c rythmol cheap acai antioxidants nexium generic blood pressure pills levitra online no prescription Levitra Online medications on line motion sickness drugs bactrim online order roxithromycin nicotine where can i order viagra immune supplements buy erexin v bph prostate allopurinol xanax for depression drug new smoking stop cheap impotence drug generic cialis delivery new treatment for depression antibiotics for cat viagra china alternative medicine cholesterol viagra dose anxiety disorder treatment severe muscle pain treatment of cancer calcium carbonate penis enlargement without pill valium maximum dosage reasons for high blood pressure energy product breast enlargement info cheap effexor building your body wrinkle cream aricept dosage alpha blocker increasing female sex drive valium depression new pain meds no rx xanax drug trileptal mg imitrex avapro 150mg medicine drugs contraception female claritin pill medication for acne med orders buy viagra internet levitra effect treatment for blood clots order sominex buy creatine buy precose cheap viagra overnight lopressor drug body building info health drugs general health and medical what is diazepam eye infections in dogs online prescription pills diclofenac tablet new medication anxiety buy citalopram medication male enhancement enhancement fat blocker medicine for throat infection order cardizem about soma health remedies for dogs generic xanax cheap zyrtec for depression medicine viagra sex domination buy acne skin care product hypnosis help study cure vaginal yeast infection weight loss supplement program muscle pain in leg how to increase erection buy viagra what is cla augmentin doses gaining muscle mass health med online heart rate treatments lopressor drug dog ear canal phentermine without prescription viagra order online weight loss glipizide diabetes astelin generic fat blocker buy gel tooth whitening cheap wellbutrin online weight loss program buy antiox anti-biotics acne skin treatmen tramadole vpxl pill drugs affecting levitra immune system support augmentin hypothyroidism medication buy erexin v uy prescription medication without a prescription buy discount order osteo arthritis online buy pilocarpine cheapest place to buy phentermine parasite treatment impotence help body fat loss viagra herb alternative constipation supplements treatment dementia adhd and medications muscle spasm relief viagra online cheap relieve upper back pain stop hair loss discount viagra online menstrual cycle problems antifungal shampoo side effects ativan gabapentin medication where can i buy viagra diazepam buy soma online clonidine dosage viagra gel top hair loss fast antibiotics cure chlamydia skin fungal infections drug zofran give up smoking alternative medicine cholesterol sleeping help best online viagra scams prednisone 10mg viagra sex domination lotensin easy weight loss pain meds without prescription over the counter drugs new high blood pressure medic generic compazine cetirizine drug order phentermine best fat blockers woman enhancement supplement drug zofran buy precose new drug treatment for cancer how to increase fertility viagra in australia benadryl dosing buy alcoholism medications order l arginine buy diazepam generic for ativan ativan prescription drugs weight loss treatment for chest pain woman health where can i buy phentermine online skin fungal infection give up smoking viagra on line hoodia information how does osteoporosis occur buy viagra online buy alcoholism medications depakote overdose klonopin pill tetracycline capsules what is high blood pressure bladder control for dogs generic for lipitor glucophage online pharmacy gabapentin dosage treating yeast infections dog health info cymbalta anxiety cheap tramadol without prescription hydrea drugs used for cancer cure for high blood pressure alcohol and valium relief from constipation liver infection treatment cialis soft zantac medication help sleep problems all natural antibiotics order medication without prescription sleep problems free hypnotherapy gaining muscle mass cheap viagra order online natural help for pain how to buy viagra drug price celebrex information otc diuretic levitra 10 mg buy medicine online pets products relief foot pain cialis without prescription med care cheapest generic cialis rapid hair loss pain medications generic side effects meds without prescriptions cat anxiety buy simplicef natural cure arthritis effects of high blood pressure lowest price generic viagra how to get birth control new breast cancer drug buy topamax blood pressure meds when are beta blockers prescribed how to get pain meds order fosamax online viagra name order viagra viagra cialis cat's eye health how to relieve lower back pain treating ear infections diazapan is valium online pain doctors high blood pressure in elderly medication to stop smoking wellbutrin dosages diabetes blood sugar levels weight loss diet pill side effects of prescribed pain pills drug list high blood pressure buy cialis online in usa ultram cost how to help osteoporosis how to use clomid discount brand viagra wellbutrin cymbalta buy pills without a prescription buy pain medicine online tab tramadol depression symptoms treatment how levitra work hypertension medications beta blockers prevent premature ejaculation xanax interactions with other medicines purchase medicine on line does alli work xenical mexico prescriptions buy sumycin uy prescription medication without a prescription ambien cost methocarbamol effects cheap beta blockers cats bladder reduce cholesterol naturally metformin tablet scabies medicine breast enhancer pills body building over 50 order viagra cheap zestril medication how to buy prescription medications online pharma kamagra drugs depression ear infection symptoms big muscle controlling blood pressure pain meds and pregnancy buy diazepam without prescription skin allergies antibiotic zoloft buy weight loss nutrition program Buy Cialis breast increase meds without prescriptions blood clots medical edema treatment for flu best hangover remedy diabetes drugs