/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jbpm.pvm.internal.xml; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.jbpm.internal.log.Log; import org.jbpm.pvm.internal.stream.StreamInput; import org.jbpm.pvm.internal.util.UrlEntity; import org.jbpm.pvm.internal.util.XmlUtil; import org.jbpm.pvm.internal.wire.Descriptor; import org.jbpm.pvm.internal.wire.descriptor.ArgDescriptor; import org.jbpm.pvm.internal.wire.xml.WireParser; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; /** makes typical usage of JAXP more convenient, adds a binding framework, * entity resolution and error handling. * *
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 {@link Parser} is a thread safe object. For each parse operation, a * new {@link Parse} object is created with method {@link #createParse()}. * Then the parse object is used to specify the input source, execute the * parse operation and extract the results. *
* *{@link Binding}s capture parsing of a certain element type. This way, * the parser becomes more modular and customizable. *
* *{@link Entity Entities} are schema's that specify the grammar of the * XML files that are parsed by the parser. *
* *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 {@link #createParse()} operation. The {@link 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 {@link Parse#setFile(java.io.File)}, * {@link Parse#setInputStream(java.io.InputStream)}, * {@link Parse#setInputSource(InputSource)}, * {@link Parse#setUrl(java.net.URL)} and * {@link Parse#setStreamSource(StreamInput)}. *
* *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 {@link Parse#getProblems() problems} are silently captured in the parse * object. If an exception is thrown by * {@link Parse#checkErrors(String)}, it will contain a report of * all the parsing problems. Alternatively, the {@link Parse#getProblems() 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. *
* *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 {@link Binding}s. Each {@link Binding} * knows how to transform a dom element of a given tagName to the corresponding Java * object. {@link Bindings} has a notion of binding categories. For example, activities * 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 activity types can be * added dynamically. *
* *The current {@link Bindings} implementation only supports matching of an * element with a {@link 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 * {@link 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 * {@link Binding} should be created and the method {@link #getBinding(Element, String)} and * constructor {@link Bindings#Bindings(Bindings)} should be provided * with the more specialized matching behaviour. *
* *When implementing {@link Binding}s, you might want to make use of the * contextual object stack that is provided on the {@link Parse}. The * {@link 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 activityElement: elements) { * // parse the child elements with the bindings in category "activity" * parseElement(activityElement, parse, "activity"); * } * } finally { * // make sure my processDefinition is popped. * parse.popObject(); * } * return myProcess; * } * } *
Then, activity 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 activity 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 {@link Bindings#Bindings(Bindings)} before * you start adding more bindings to the specialized parser. Otherwise the * base parser's bindings will be updated as well. *
* *This parser is build for inheritance. * Overriding method {@link #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. *
* *A parser can be configured with a set of entities with the * {@link #addEntity(String, Entity)} method. The {@link UrlEntity} has * a convenience method to build entities from resources * {@link UrlEntity#UrlEntity(String, ClassLoader)}. *
* *When a document builder is created, the default implementation of the * {@link #setEntityResolver(DocumentBuilder)} will set this parser as the entity resolver. * The implementation method of {@link EntityResolver} ({@link #resolveEntity(String, String)} * will use the added {@link Entity}s to try and find a match based on the * publicId. If one is found, the {@link 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 */ public class Parser { private static Log log = Log.getLog(Parser.class.getName()); protected SAXParserFactory saxParserFactory; protected String[] schemaResources; protected DocumentBuilderFactory documentBuilderFactory = null; protected Bindings bindings = null; protected ClassLoader classLoader = null; /** the default parser */ public Parser() { initialize(); } /** creates a new Parser with bindings that can be maintained statically in * specialized subclasses of Parser. */ public Parser(Bindings bindings) { initialize(); this.bindings = bindings; } /** creates a new Parser with bindings and entities that can be maintained statically * in specialized subclasses of Parser. * @deprecated entities should be replaced by {@link #setSchemaResources(List)} */ public Parser(Bindings bindings, Map