org.jbpm.xml
Class Parser

java.lang.Object
  extended by org.jbpm.xml.Parser
All Implemented Interfaces:
org.xml.sax.EntityResolver
Direct Known Subclasses:
BindingParser, BusinessCalendarParser, DebugDomParser, EnvironmentParser, WireParser

public class Parser
extends java.lang.Object
implements org.xml.sax.EntityResolver

makes typical usage of JAXP more convenient, adds a binding framework, entity resolution and error handling.

Purpose

This is a base parser for the common pattern where first JAXP is used to parse xml into a Document Object Model (DOM), and then, this DOM is examined to build a domain model object. The main purpose of this parser is to serve as a base class for implementing such parsers and to provide a more convenient API for working with JAXP.

A Parser is a thread safe object. For each parse operation, a new Parse object is created with method createParse(). Then the parse object is used to specify the input source, execute the parse operation and extract the results.

Bindings capture parsing of a certain element type. This way, the parser becomes more modular and customizable.

Entities are schema's that specify the grammar of the XML files that are parsed by the parser.

API Usage

Parsers can be customized by inheritance (that will be covered below), but a parser can also be used as is:

 1 |   static Parser parser = new Parser();
 2 | 
 3 |   void someMethod() {
 4 |     MyDomainObject mdo = (MyDomainObject) parser
 5 |             .createParse()
 6 |             .setString(myXmlString)
 7 |             .execute()
 8 |             .checkProblems()
 9 |             .getDocumentObject();
10 |   }
 

line 1 shows that a single parser can be used for all threads as the parser is maintained in a static member field.

line 5 shows that a new parse operation is always started with the createParse() operation. The Parse object that is returned will maintain all data that is related to that single parse operation.

line 6 shows how a simple XML string can be provided as the input source for the parse operation. Alternative methods to specify the input source are Parse.setFile(java.io.File), Parse.setInputStream(java.io.InputStream), Parse.setInputSource(InputSource), Parse.setUrl(java.net.URL) and Parse.setStreamSource(StreamSource).

line 7 shows how the execution of the parse is performed. The input source will be read, the resulting Document Object Model (DOM) will be walked and potentially problems are produced in the parse.

line 8 shows how an exception can be thrown in case of an error. The parse execution itself tries to keep parsing as much as possible to provide the developer with as much feedback as possible in one parse cycle. The problems are silently captured in the parse object. If an exception is thrown by #Parse#checkProblems(String, Parse), it will contain a report of all the parsing problems. Alternatively, the problems in the parse object could be examined directly without the need for an exception.

line 9 shows how the result of the parse operation is extracted from the parse object.

Binding

Bindings are the link between a certain type of element in your XML document and the corresponding java object in your domain model.

A parser can be configured with a set of Bindings. Each Binding knows how to transform a dom element of a given tagName to the corresponding Java object. Bindings has a notion of binding categories. For example, nodes and actions can be seen as different categories in jPDL.

The purpose of bindings is to make certain elements in the parsing configurable. E.g. in jPDL, the main structure of the document is fixed. But node types can be added dynamically.

The current Bindings implementation only supports matching of an element with a Binding based on tagName. If you want to take other things into account (e.g. when you want to differentiate between elements of the same tagName with a different attribute value), you can create a specialized Bindings class.

Bindings are added by tagName, but they have to be looked up by element. That is to support more specialized bindings implementations that match an element with a binding on more information then just the tagName. In that case, a specialized subclass of Binding should be created and the method getBinding(Element, String) and constructor Bindings.Bindings(Bindings) should be provided with the more specialized matching behaviour.

Object stack

When implementing Bindings, you might want to make use of the contextual object stack that is provided on the Parse. The Binding implementations can maintain Java objects on that stack that are being created.

E.g. you could push the ProcessDefinition element onto the object stack while it is being parsed like this:

public class MyProcessBinding implements Binding {

   public Object parse(Element element, Parse parse, Parser parser) {
     // instantiate the object for this binding
     MyProcess myProcess = new MyProcess();

     // collect all the child elements of element
     List elements = XmlUtil.elements(element);

     // push my processDefinition onto the object stack
     parse.pushObject(myProcess);
     try {

       for (Element nodeElement: elements) {
         // parse the child elements with the bindings in category "node"
         parseElement(nodeElement, parse, "node");
       }
     } finally {
       // make sure my processDefinition is popped.
       parse.popObject();
     }
     return myProcess;
   }
 }
 

Then, node bindings might access the processDefinition like this:

public class MyNodeBinding implements Binding {

   public Object parse(Element element, Parse parse, Parser parser) {
     // instantiate the object for this binding
     MyNode myNode = new MyNode();

     // add the node to the processDefinition
     MyProcess myProcess = parse.findObject(MyProcess.class);
     myProcess.addNode(myNode);
     myNode.setMyProcess(myProcess);

     return myNode;
   }
 }
 

A parser implementation will typically have a static Bindings object that is leveraged in all parser objects. To customize bindings for a such a parser be sure to make a deep copy with Bindings.Bindings(Bindings) before you start adding more bindings to the specialized parser. Otherwise the base parser's bindings will be updated as well.

Building custom parsers

This parser is build for inheritance. Overriding method parseDocumentElement(Element, Parse) can be an easy way to start writing your own logic on walking the Document Object Model (DOM). Such customizations can still be combined with the usage of bindings.

Entity resolving

A parser can be configured with a set of entities with the addEntity(String, Entity) method. The UrlEntity has a convenience method to build entities from resources UrlEntity.UrlEntity(String, ClassLoader).

When a document builder is created, the default implementation of the #setEntityResolver(DocumentBuilder) will set this parser as the entity resolver. The implementation method of EntityResolver (resolveEntity(String, String) will use the added Entitys to try and find a match based on the publicId. If one is found, the Entity inputSource is returned, otherwise the systemId is used.

This class is intended to be used with aggregation as well as inheritence.

Author:
Tom Baeyens

Field Summary
protected  Bindings bindings
           
protected  java.lang.ClassLoader classLoader
           
protected  javax.xml.parsers.DocumentBuilderFactory documentBuilderFactory
           
protected  java.util.Map<java.lang.String,Entity> entities
           
 
Constructor Summary
Parser()
          the default parser
Parser(Bindings bindings)
          creates a new Parser with bindings that can be maintained statically in specialized subclasses of Parser.
Parser(Bindings bindings, java.util.Map<java.lang.String,Entity> entities)
          creates a new Parser with bindings and entities that can be maintained statically in specialized subclasses of Parser.
 
Method Summary
 void addEntity(java.lang.String publicId, Entity entity)
          adds a resolver to the schema catalog.
protected  org.w3c.dom.Document buildDom(Parse parse)
          customizable DOM building.
 void checkProblems(java.lang.String description, Parse parse)
          throws an exception with appropriate message in case the parse contains errors or fatal errors.
protected  javax.xml.parsers.DocumentBuilder createDocumentBuilder(Parse parse)
          customizable creation of a new document builder.
 Parse createParse()
          main method to start a new parse, check Parse for specifying input, executing the parse and extracting the results.
protected  void execute(Parse parse)
          customizable parse execution
 Binding getBinding(org.w3c.dom.Element element)
          the handler for the given element
 Binding getBinding(org.w3c.dom.Element element, java.lang.String category)
          the handler for the given element limited to a given category
 Bindings getBindings()
          the handlers for specific element types
 javax.xml.parsers.DocumentBuilderFactory getDocumentBuilderFactory()
          getter with lazy initialization of the document builder factory.
protected  org.xml.sax.InputSource getInputSource(Parse parse)
          customizable extraction of the inputSource from the given parse.
 void importStream(StreamSource importedStreamSource, org.w3c.dom.Element destination, Parse importingParse)
          builds a dom from the importedStreamSource and appends the child elements of the document element to the destination element.
protected  javax.xml.parsers.DocumentBuilderFactory newDocumentBuilderFactory()
          factory method for DocumentBuilderFactory during lazy initialization of the documentBuilderFactory.
 java.lang.Object parseDocument(org.w3c.dom.Document document, Parse parse)
          start of the DOM walk.
 java.lang.Object parseDocumentElement(org.w3c.dom.Element documentElement, Parse parse)
          parses the top level element in the document and produces the object that is the result from the parsing.
 java.lang.Object parseElement(org.w3c.dom.Element element, Parse parse)
          parses an arbitrary element in the document with the first matching binding found using any of the categories.
 java.lang.Object parseElement(org.w3c.dom.Element element, Parse parse, java.lang.String category)
          parses an arbitrary element in the document based on the bindings in the given category.
 org.xml.sax.InputSource resolveEntity(java.lang.String publicId, java.lang.String systemId)
          implementation of EntityResolver based on a map of Entitys.
 void setBindings(Bindings bindings)
          set the handlers for specific element types
 void setDocumentBuilderFactory(javax.xml.parsers.DocumentBuilderFactory documentBuilderFactory)
          setter for the document builder factory
 void useParseEntityResolver()
          makes sure that an EntityResolver is created based on the Entitys in this parser.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

documentBuilderFactory

protected javax.xml.parsers.DocumentBuilderFactory documentBuilderFactory

entities

protected java.util.Map<java.lang.String,Entity> entities

bindings

protected Bindings bindings

classLoader

protected java.lang.ClassLoader classLoader
Constructor Detail

Parser

public Parser()
the default parser


Parser

public Parser(Bindings bindings)
creates a new Parser with bindings that can be maintained statically in specialized subclasses of Parser.


Parser

public Parser(Bindings bindings,
              java.util.Map<java.lang.String,Entity> entities)
creates a new Parser with bindings and entities that can be maintained statically in specialized subclasses of Parser.

Method Detail

getDocumentBuilderFactory

public javax.xml.parsers.DocumentBuilderFactory getDocumentBuilderFactory()
getter with lazy initialization of the document builder factory. If no document builder factory was set previously with the setDocumentBuilderFactory(DocumentBuilderFactory) method, newDocumentBuilderFactory() will be called to create one.


setDocumentBuilderFactory

public void setDocumentBuilderFactory(javax.xml.parsers.DocumentBuilderFactory documentBuilderFactory)
setter for the document builder factory


newDocumentBuilderFactory

protected javax.xml.parsers.DocumentBuilderFactory newDocumentBuilderFactory()
factory method for DocumentBuilderFactory during lazy initialization of the documentBuilderFactory. Can be overridden by subclasses to change the DocumentBuilderFactory implementation or to apply specific configurations.


addEntity

public void addEntity(java.lang.String publicId,
                      Entity entity)
adds a resolver to the schema catalog. See also section 'Entity resolving'.


useParseEntityResolver

public void useParseEntityResolver()
makes sure that an EntityResolver is created based on the Entitys in this parser. even when none of the addEntity(String, Entity) methods are called. This enables addition of entities on a per-Parse basis when there are no parser-level entities.


resolveEntity

public org.xml.sax.InputSource resolveEntity(java.lang.String publicId,
                                             java.lang.String systemId)
implementation of EntityResolver based on a map of Entitys. See also section 'Entity resolving'.

Specified by:
resolveEntity in interface org.xml.sax.EntityResolver
See Also:
addEntity(String, Entity)

getBindings

public Bindings getBindings()
the handlers for specific element types


setBindings

public void setBindings(Bindings bindings)
set the handlers for specific element types


getBinding

public Binding getBinding(org.w3c.dom.Element element)
the handler for the given element


getBinding

public Binding getBinding(org.w3c.dom.Element element,
                          java.lang.String category)
the handler for the given element limited to a given category


createParse

public Parse createParse()
main method to start a new parse, check Parse for specifying input, executing the parse and extracting the results.


importStream

public void importStream(StreamSource importedStreamSource,
                         org.w3c.dom.Element destination,
                         Parse importingParse)
builds a dom from the importedStreamSource and appends the child elements of the document element to the destination element. Problems are reported in the importingParse.


execute

protected void execute(Parse parse)
customizable parse execution


buildDom

protected org.w3c.dom.Document buildDom(Parse parse)
customizable DOM building. Parses the input specified in the parse with a DocumentBuilder.

Returns:
a Document or null in case an exception occurs during XML parsing.

createDocumentBuilder

protected javax.xml.parsers.DocumentBuilder createDocumentBuilder(Parse parse)
customizable creation of a new document builder. Used by buildDom(Parse).


getInputSource

protected org.xml.sax.InputSource getInputSource(Parse parse)
customizable extraction of the inputSource from the given parse. Returns null in case the parse doesn't have an inputSource or a streamSource specified. If an inputStream is created in this method, it is set in the parse so that it can be closed at the end of the execute(Parse).


parseDocument

public java.lang.Object parseDocument(org.w3c.dom.Document document,
                                      Parse parse)
start of the DOM walk. This method is used as part of the parse execution. This default implementation behaviour extracts the document element and delegates to parseDocumentElement(Element, Parse). This method can be overridden for customized behaviour.

Returns:
the object that is the result from parsing this document.

parseDocumentElement

public java.lang.Object parseDocumentElement(org.w3c.dom.Element documentElement,
                                             Parse parse)
parses the top level element in the document and produces the object that is the result from the parsing.

Returns:
the object that is the result from parsing this document element.

parseElement

public java.lang.Object parseElement(org.w3c.dom.Element element,
                                     Parse parse)
parses an arbitrary element in the document with the first matching binding found using any of the categories.

Returns:
the object that is the result from parsing this element.

parseElement

public java.lang.Object parseElement(org.w3c.dom.Element element,
                                     Parse parse,
                                     java.lang.String category)
parses an arbitrary element in the document based on the bindings in the given category.

Parameters:
category - is the category in which the tagName should be resolved to a Binding. If category is null, all the categories will be scanned for an appropriate binding in random order.
Returns:
the object that is the result from parsing this element.

checkProblems

public void checkProblems(java.lang.String description,
                          Parse parse)
throws an exception with appropriate message in case the parse contains errors or fatal errors. This method also logs the problems with severity 'warning'.