- /* $Id: Digester.java,v 1.104.2.1 2004/07/30 20:11:00 rdonkin Exp $
- *
- * Copyright 2001-2004 The Apache Software Foundation.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.apache.commons.digester;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.Reader;
- import java.lang.reflect.InvocationTargetException;
- import java.util.EmptyStackException;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Properties;
- import javax.xml.parsers.ParserConfigurationException;
- import javax.xml.parsers.SAXParser;
- import javax.xml.parsers.SAXParserFactory;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.apache.commons.collections.ArrayStack;
- import org.xml.sax.Attributes;
- import org.xml.sax.EntityResolver;
- import org.xml.sax.ErrorHandler;
- import org.xml.sax.InputSource;
- import org.xml.sax.Locator;
- import org.xml.sax.SAXException;
- import org.xml.sax.SAXNotRecognizedException;
- import org.xml.sax.SAXNotSupportedException;
- import org.xml.sax.SAXParseException;
- import org.xml.sax.XMLReader;
- import org.xml.sax.helpers.DefaultHandler;
- /**
- * <p>A <strong>Digester</strong> processes an XML input stream by matching a
- * series of element nesting patterns to execute Rules that have been added
- * prior to the start of parsing. This package was inspired by the
- * <code>XmlMapper</code> class that was part of Tomcat 3.0 and 3.1,
- * but is organized somewhat differently.</p>
- *
- * <p>See the <a href="package-summary.html#package_description">Digester
- * Developer Guide</a> for more information.</p>
- *
- * <p><strong>IMPLEMENTATION NOTE</strong> - A single Digester instance may
- * only be used within the context of a single thread at a time, and a call
- * to <code>parse()</code> must be completed before another can be initiated
- * even from the same thread.</p>
- *
- * <p><strong>IMPLEMENTATION NOTE</strong> - A bug in Xerces 2.0.2 prevents
- * the support of XML schema. You need Xerces 2.1/2.3 and up to make
- * this class working with XML schema</p>
- */
- public class Digester extends DefaultHandler {
- // --------------------------------------------------------- Constructors
- /**
- * Construct a new Digester with default properties.
- */
- public Digester() {
- super();
- }
- /**
- * Construct a new Digester, allowing a SAXParser to be passed in. This
- * allows Digester to be used in environments which are unfriendly to
- * JAXP1.1 (such as WebLogic 6.0). Thanks for the request to change go to
- * James House (james@interobjective.com). This may help in places where
- * you are able to load JAXP 1.1 classes yourself.
- */
- public Digester(SAXParser parser) {
- super();
- this.parser = parser;
- }
- /**
- * Construct a new Digester, allowing an XMLReader to be passed in. This
- * allows Digester to be used in environments which are unfriendly to
- * JAXP1.1 (such as WebLogic 6.0). Note that if you use this option you
- * have to configure namespace and validation support yourself, as these
- * properties only affect the SAXParser and emtpy constructor.
- */
- public Digester(XMLReader reader) {
- super();
- this.reader = reader;
- }
- // --------------------------------------------------- Instance Variables
- /**
- * The body text of the current element.
- */
- protected StringBuffer bodyText = new StringBuffer();
- /**
- * The stack of body text string buffers for surrounding elements.
- */
- protected ArrayStack bodyTexts = new ArrayStack();
- /**
- * Stack whose elements are List objects, each containing a list of
- * Rule objects as returned from Rules.getMatch(). As each xml element
- * in the input is entered, the matching rules are pushed onto this
- * stack. After the end tag is reached, the matches are popped again.
- * The depth of is stack is therefore exactly the same as the current
- * "nesting" level of the input xml.
- *
- * @since 1.6
- */
- protected ArrayStack matches = new ArrayStack(10);
- /**
- * The class loader to use for instantiating application objects.
- * If not specified, the context class loader, or the class loader
- * used to load Digester itself, is used, based on the value of the
- * <code>useContextClassLoader</code> variable.
- */
- protected ClassLoader classLoader = null;
- /**
- * Has this Digester been configured yet.
- */
- protected boolean configured = false;
- /**
- * The EntityResolver used by the SAX parser. By default it use this class
- */
- protected EntityResolver entityResolver;
- /**
- * The URLs of entityValidator that have been registered, keyed by the public
- * identifier that corresponds.
- */
- protected HashMap entityValidator = new HashMap();
- /**
- * The application-supplied error handler that is notified when parsing
- * warnings, errors, or fatal errors occur.
- */
- protected ErrorHandler errorHandler = null;
- /**
- * The SAXParserFactory that is created the first time we need it.
- */
- protected SAXParserFactory factory = null;
- /**
- * @deprecated This is now managed by {@link ParserFeatureSetterFactory}
- */
- protected String JAXP_SCHEMA_LANGUAGE =
- "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
- /**
- * The Locator associated with our parser.
- */
- protected Locator locator = null;
- /**
- * The current match pattern for nested element processing.
- */
- protected String match = "";
- /**
- * Do we want a "namespace aware" parser.
- */
- protected boolean namespaceAware = false;
- /**
- * Registered namespaces we are currently processing. The key is the
- * namespace prefix that was declared in the document. The value is an
- * ArrayStack of the namespace URIs this prefix has been mapped to --
- * the top Stack element is the most current one. (This architecture
- * is required because documents can declare nested uses of the same
- * prefix for different Namespace URIs).
- */
- protected HashMap namespaces = new HashMap();
- /**
- * The parameters stack being utilized by CallMethodRule and
- * CallParamRule rules.
- */
- protected ArrayStack params = new ArrayStack();
- /**
- * The SAXParser we will use to parse the input stream.
- */
- protected SAXParser parser = null;
- /**
- * The public identifier of the DTD we are currently parsing under
- * (if any).
- */
- protected String publicId = null;
- /**
- * The XMLReader used to parse digester rules.
- */
- protected XMLReader reader = null;
- /**
- * The "root" element of the stack (in other words, the last object
- * that was popped.
- */
- protected Object root = null;
- /**
- * The <code>Rules</code> implementation containing our collection of
- * <code>Rule</code> instances and associated matching policy. If not
- * established before the first rule is added, a default implementation
- * will be provided.
- */
- protected Rules rules = null;
- /**
- * The XML schema language to use for validating an XML instance. By
- * default this value is set to <code>W3C_XML_SCHEMA</code>
- */
- protected String schemaLanguage = W3C_XML_SCHEMA;
- /**
- * The XML schema to use for validating an XML instance.
- */
- protected String schemaLocation = null;
- /**
- * The object stack being constructed.
- */
- protected ArrayStack stack = new ArrayStack();
- /**
- * Do we want to use the Context ClassLoader when loading classes
- * for instantiating new objects. Default is <code>false</code>.
- */
- protected boolean useContextClassLoader = false;
- /**
- * Do we want to use a validating parser.
- */
- protected boolean validating = false;
- /**
- * The Log to which most logging calls will be made.
- */
- protected Log log =
- LogFactory.getLog("org.apache.commons.digester.Digester");
- /**
- * The Log to which all SAX event related logging calls will be made.
- */
- protected Log saxLog =
- LogFactory.getLog("org.apache.commons.digester.Digester.sax");
- /**
- * The schema language supported. By default, we use this one.
- */
- protected static final String W3C_XML_SCHEMA =
- "http://www.w3.org/2001/XMLSchema";
- /**
- * An optional class that substitutes values in attributes and body text.
- * This may be null and so a null check is always required before use.
- */
- protected Substitutor substitutor;
- /** Stacks used for interrule communication, indexed by name String */
- private HashMap stacksByName = new HashMap();
- // ------------------------------------------------------------- Properties
- /**
- * Return the currently mapped namespace URI for the specified prefix,
- * if any; otherwise return <code>null</code>. These mappings come and
- * go dynamically as the document is parsed.
- *
- * @param prefix Prefix to look up
- */
- public String findNamespaceURI(String prefix) {
- ArrayStack stack = (ArrayStack) namespaces.get(prefix);
- if (stack == null) {
- return (null);
- }
- try {
- return ((String) stack.peek());
- } catch (EmptyStackException e) {
- return (null);
- }
- }
- /**
- * Return the class loader to be used for instantiating application objects
- * when required. This is determined based upon the following rules:
- * <ul>
- * <li>The class loader set by <code>setClassLoader()</code>, if any</li>
- * <li>The thread context class loader, if it exists and the
- * <code>useContextClassLoader</code> property is set to true</li>
- * <li>The class loader used to load the Digester class itself.
- * </ul>
- */
- public ClassLoader getClassLoader() {
- if (this.classLoader != null) {
- return (this.classLoader);
- }
- if (this.useContextClassLoader) {
- ClassLoader classLoader =
- Thread.currentThread().getContextClassLoader();
- if (classLoader != null) {
- return (classLoader);
- }
- }
- return (this.getClass().getClassLoader());
- }
- /**
- * Set the class loader to be used for instantiating application objects
- * when required.
- *
- * @param classLoader The new class loader to use, or <code>null</code>
- * to revert to the standard rules
- */
- public void setClassLoader(ClassLoader classLoader) {
- this.classLoader = classLoader;
- }
- /**
- * Return the current depth of the element stack.
- */
- public int getCount() {
- return (stack.size());
- }
- /**
- * Return the name of the XML element that is currently being processed.
- */
- public String getCurrentElementName() {
- String elementName = match;
- int lastSlash = elementName.lastIndexOf('/');
- if (lastSlash >= 0) {
- elementName = elementName.substring(lastSlash + 1);
- }
- return (elementName);
- }
- /**
- * Return the debugging detail level of our currently enabled logger.
- *
- * @deprecated This method now always returns 0. Digester uses the apache
- * jakarta commons-logging library; see the documentation for that library
- * for more information.
- */
- public int getDebug() {
- return (0);
- }
- /**
- * Set the debugging detail level of our currently enabled logger.
- *
- * @param debug New debugging detail level (0=off, increasing integers
- * for more detail)
- *
- * @deprecated This method now has no effect at all. Digester uses
- * the apache jakarta comons-logging library; see the documentation
- * for that library for more information.
- */
- public void setDebug(int debug) {
- ; // No action is taken
- }
- /**
- * Return the error handler for this Digester.
- */
- public ErrorHandler getErrorHandler() {
- return (this.errorHandler);
- }
- /**
- * Set the error handler for this Digester.
- *
- * @param errorHandler The new error handler
- */
- public void setErrorHandler(ErrorHandler errorHandler) {
- this.errorHandler = errorHandler;
- }
- /**
- * Return the SAXParserFactory we will use, creating one if necessary.
- */
- public SAXParserFactory getFactory() {
- if (factory == null) {
- factory = SAXParserFactory.newInstance();
- factory.setNamespaceAware(namespaceAware);
- factory.setValidating(validating);
- }
- return (factory);
- }
- /**
- * Returns a flag indicating whether the requested feature is supported
- * by the underlying implementation of <code>org.xml.sax.XMLReader</code>.
- * See <a href="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description"
- * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a>
- * for information about the standard SAX2 feature flags.
- *
- * @param feature Name of the feature to inquire about
- *
- * @exception ParserConfigurationException if a parser configuration error
- * occurs
- * @exception SAXNotRecognizedException if the property name is
- * not recognized
- * @exception SAXNotSupportedException if the property name is
- * recognized but not supported
- */
- public boolean getFeature(String feature)
- throws ParserConfigurationException, SAXNotRecognizedException,
- SAXNotSupportedException {
- return (getFactory().getFeature(feature));
- }
- /**
- * Sets a flag indicating whether the requested feature is supported
- * by the underlying implementation of <code>org.xml.sax.XMLReader</code>.
- * See <a href="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description"
- * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a>
- * for information about the standard SAX2 feature flags. In order to be
- * effective, this method must be called <strong>before</strong> the
- * <code>getParser()</code> method is called for the first time, either
- * directly or indirectly.
- *
- * @param feature Name of the feature to set the status for
- * @param value The new value for this feature
- *
- * @exception ParserConfigurationException if a parser configuration error
- * occurs
- * @exception SAXNotRecognizedException if the property name is
- * not recognized
- * @exception SAXNotSupportedException if the property name is
- * recognized but not supported
- */
- public void setFeature(String feature, boolean value)
- throws ParserConfigurationException, SAXNotRecognizedException,
- SAXNotSupportedException {
- getFactory().setFeature(feature, value);
- }
- /**
- * Return the current Logger associated with this instance of the Digester
- */
- public Log getLogger() {
- return log;
- }
- /**
- * Set the current logger for this Digester.
- */
- public void setLogger(Log log) {
- this.log = log;
- }
- /**
- * Gets the logger used for logging SAX-related information.
- * <strong>Note</strong> the output is finely grained.
- *
- * @since 1.6
- */
- public Log getSAXLogger() {
- return saxLog;
- }
- /**
- * Sets the logger used for logging SAX-related information.
- * <strong>Note</strong> the output is finely grained.
- * @param saxLog Log, not null
- *
- * @since 1.6
- */
- public void setSAXLogger(Log saxLog) {
- this.saxLog = saxLog;
- }
- /**
- * Return the current rule match path
- */
- public String getMatch() {
- return match;
- }
- /**
- * Return the "namespace aware" flag for parsers we create.
- */
- public boolean getNamespaceAware() {
- return (this.namespaceAware);
- }
- /**
- * Set the "namespace aware" flag for parsers we create.
- *
- * @param namespaceAware The new "namespace aware" flag
- */
- public void setNamespaceAware(boolean namespaceAware) {
- this.namespaceAware = namespaceAware;
- }
- /**
- * Set the publid id of the current file being parse.
- * @param publicId the DTD/Schema public's id.
- */
- public void setPublicId(String publicId){
- this.publicId = publicId;
- }
- /**
- * Return the public identifier of the DTD we are currently
- * parsing under, if any.
- */
- public String getPublicId() {
- return (this.publicId);
- }
- /**
- * Return the namespace URI that will be applied to all subsequently
- * added <code>Rule</code> objects.
- */
- public String getRuleNamespaceURI() {
- return (getRules().getNamespaceURI());
- }
- /**
- * Set the namespace URI that will be applied to all subsequently
- * added <code>Rule</code> objects.
- *
- * @param ruleNamespaceURI Namespace URI that must match on all
- * subsequently added rules, or <code>null</code> for matching
- * regardless of the current namespace URI
- */
- public void setRuleNamespaceURI(String ruleNamespaceURI) {
- getRules().setNamespaceURI(ruleNamespaceURI);
- }
- /**
- * Return the SAXParser we will use to parse the input stream. If there
- * is a problem creating the parser, return <code>null</code>.
- */
- public SAXParser getParser() {
- // Return the parser we already created (if any)
- if (parser != null) {
- return (parser);
- }
- // Create a new parser
- try {
- if (validating) {
- Properties properties = new Properties();
- properties.put("SAXParserFactory", getFactory());
- if (schemaLocation != null) {
- properties.put("schemaLocation", schemaLocation);
- properties.put("schemaLanguage", schemaLanguage);
- }
- parser = ParserFeatureSetterFactory.newSAXParser(properties); } else {
- parser = getFactory().newSAXParser();
- }
- } catch (Exception e) {
- log.error("Digester.getParser: ", e);
- return (null);
- }
- return (parser);
- }
- /**
- * Return the current value of the specified property for the underlying
- * <code>XMLReader</code> implementation.
- * See <a href="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description"
- * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a>
- * for information about the standard SAX2 properties.
- *
- * @param property Property name to be retrieved
- *
- * @exception SAXNotRecognizedException if the property name is
- * not recognized
- * @exception SAXNotSupportedException if the property name is
- * recognized but not supported
- */
- public Object getProperty(String property)
- throws SAXNotRecognizedException, SAXNotSupportedException {
- return (getParser().getProperty(property));
- }
- /**
- * Set the current value of the specified property for the underlying
- * <code>XMLReader</code> implementation.
- * See <a href="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description"
- * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a>
- * for information about the standard SAX2 properties.
- *
- * @param property Property name to be set
- * @param value Property value to be set
- *
- * @exception SAXNotRecognizedException if the property name is
- * not recognized
- * @exception SAXNotSupportedException if the property name is
- * recognized but not supported
- */
- public void setProperty(String property, Object value)
- throws SAXNotRecognizedException, SAXNotSupportedException {
- getParser().setProperty(property, value);
- }
- /**
- * By setting the reader in the constructor, you can bypass JAXP and
- * be able to use digester in Weblogic 6.0.
- *
- * @deprecated Use getXMLReader() instead, which can throw a
- * SAXException if the reader cannot be instantiated
- */
- public XMLReader getReader() {
- try {
- return (getXMLReader());
- } catch (SAXException e) {
- log.error("Cannot get XMLReader", e);
- return (null);
- }
- }
- /**
- * Return the <code>Rules</code> implementation object containing our
- * rules collection and associated matching policy. If none has been
- * established, a default implementation will be created and returned.
- */
- public Rules getRules() {
- if (this.rules == null) {
- this.rules = new RulesBase();
- this.rules.setDigester(this);
- }
- return (this.rules);
- }
- /**
- * Set the <code>Rules</code> implementation object containing our
- * rules collection and associated matching policy.
- *
- * @param rules New Rules implementation
- */
- public void setRules(Rules rules) {
- this.rules = rules;
- this.rules.setDigester(this);
- }
- /**
- * Return the XML Schema URI used for validating an XML instance.
- */
- public String getSchema() {
- return (this.schemaLocation);
- }
- /**
- * Set the XML Schema URI used for validating a XML Instance.
- *
- * @param schemaLocation a URI to the schema.
- */
- public void setSchema(String schemaLocation){
- this.schemaLocation = schemaLocation;
- }
- /**
- * Return the XML Schema language used when parsing.
- */
- public String getSchemaLanguage() {
- return (this.schemaLanguage);
- }
- /**
- * Set the XML Schema language used when parsing. By default, we use W3C.
- *
- * @param schemaLanguage a URI to the schema language.
- */
- public void setSchemaLanguage(String schemaLanguage){
- this.schemaLanguage = schemaLanguage;
- }
- /**
- * Return the boolean as to whether the context classloader should be used.
- */
- public boolean getUseContextClassLoader() {
- return useContextClassLoader;
- }
- /**
- * Determine whether to use the Context ClassLoader (the one found by
- * calling <code>Thread.currentThread().getContextClassLoader()</code>)
- * to resolve/load classes that are defined in various rules. If not
- * using Context ClassLoader, then the class-loading defaults to
- * using the calling-class' ClassLoader.
- *
- * @param use determines whether to use Context ClassLoader.
- */
- public void setUseContextClassLoader(boolean use) {
- useContextClassLoader = use;
- }
- /**
- * Return the validating parser flag.
- */
- public boolean getValidating() {
- return (this.validating);
- }
- /**
- * Set the validating parser flag. This must be called before
- * <code>parse()</code> is called the first time.
- *
- * @param validating The new validating parser flag.
- */
- public void setValidating(boolean validating) {
- this.validating = validating;
- }
- /**
- * Return the XMLReader to be used for parsing the input document.
- *
- * FIX ME: there is a bug in JAXP/XERCES that prevent the use of a
- * parser that contains a schema with a DTD.
- * @exception SAXException if no XMLReader can be instantiated
- */
- public XMLReader getXMLReader() throws SAXException {
- if (reader == null){
- reader = getParser().getXMLReader();
- }
- reader.setDTDHandler(this);
- reader.setContentHandler(this);
- if (entityResolver == null){
- reader.setEntityResolver(this);
- } else {
- reader.setEntityResolver(entityResolver);
- }
- reader.setErrorHandler(this);
- return reader;
- }
- /**
- * Gets the <code>Substitutor</code> used to convert attributes and body text.
- * @return Substitutor, null if not substitutions are to be performed.
- */
- public Substitutor getSubstitutor() {
- return substitutor;
- }
- /**
- * Sets the <code>Substitutor</code> to be used to convert attributes and body text.
- * @param substitutor the Substitutor to be used to convert attributes and body text
- * or null if not substitution of these values is to be performed.
- */
- public void setSubstitutor(Substitutor substitutor) {
- this.substitutor = substitutor;
- }
- // ------------------------------------------------- ContentHandler Methods
- /**
- * Process notification of character data received from the body of
- * an XML element.
- *
- * @param buffer The characters from the XML document
- * @param start Starting offset into the buffer
- * @param length Number of characters from the buffer
- *
- * @exception SAXException if a parsing error is to be reported
- */
- public void characters(char buffer[], int start, int length)
- throws SAXException {
- if (saxLog.isDebugEnabled()) {
- saxLog.debug("characters(" + new String(buffer, start, length) + ")");
- }
- bodyText.append(buffer, start, length);
- }
- /**
- * Process notification of the end of the document being reached.
- *
- * @exception SAXException if a parsing error is to be reported
- */
- public void endDocument() throws SAXException {
- if (saxLog.isDebugEnabled()) {
- if (getCount() > 1) {
- saxLog.debug("endDocument(): " + getCount() +
- " elements left");
- } else {
- saxLog.debug("endDocument()");
- }
- }
- while (getCount() > 1) {
- pop();
- }
- // Fire "finish" events for all defined rules
- Iterator rules = getRules().rules().iterator();
- while (rules.hasNext()) {
- Rule rule = (Rule) rules.next();
- try {
- rule.finish();
- } catch (Exception e) {
- log.error("Finish event threw exception", e);
- throw createSAXException(e);
- } catch (Error e) {
- log.error("Finish event threw error", e);
- throw e;
- }
- }
- // Perform final cleanup
- clear();
- }
- /**
- * Process notification of the end of an XML element being reached.
- *
- * @param namespaceURI - The Namespace URI, or the empty string if the
- * element has no Namespace URI or if Namespace processing is not
- * being performed.
- * @param localName - The local name (without prefix), or the empty
- * string if Namespace processing is not being performed.
- * @param qName - The qualified XML 1.0 name (with prefix), or the
- * empty string if qualified names are not available.
- * @exception SAXException if a parsing error is to be reported
- */
- public void endElement(String namespaceURI, String localName,
- String qName) throws SAXException {
- boolean debug = log.isDebugEnabled();
- if (debug) {
- if (saxLog.isDebugEnabled()) {
- saxLog.debug("endElement(" + namespaceURI + "," + localName +
- "," + qName + ")");
- }
- log.debug(" match='" + match + "'");
- log.debug(" bodyText='" + bodyText + "'");
- }
- // the actual element name is either in localName or qName, depending
- // on whether the parser is namespace aware
- String name = localName;
- if ((name == null) || (name.length() < 1)) {
- name = qName;
- }
- // Fire "body" events for all relevant rules
- List rules = (List) matches.pop();
- if ((rules != null) && (rules.size() > 0)) {
- String bodyText = this.bodyText.toString();
- Substitutor substitutor = getSubstitutor();
- if (substitutor!= null) {
- bodyText = substitutor.substitute(bodyText);
- }
- for (int i = 0; i < rules.size(); i++) {
- try {
- Rule rule = (Rule) rules.get(i);
- if (debug) {
- log.debug(" Fire body() for " + rule);
- }
- rule.body(namespaceURI, name, bodyText);
- } catch (Exception e) {
- log.error("Body event threw exception", e);
- throw createSAXException(e);
- } catch (Error e) {
- log.error("Body event threw error", e);
- throw e;
- }
- }
- } else {
- if (debug) {
- log.debug(" No rules found matching '" + match + "'.");
- }
- }
- // Recover the body text from the surrounding element
- bodyText = (StringBuffer) bodyTexts.pop();
- if (debug) {
- log.debug(" Popping body text '" + bodyText.toString() + "'");
- }
- // Fire "end" events for all relevant rules in reverse order
- if (rules != null) {
- for (int i = 0; i < rules.size(); i++) {
- int j = (rules.size() - i) - 1;
- try {
- Rule rule = (Rule) rules.get(j);
- if (debug) {
- log.debug(" Fire end() for " + rule);
- }
- rule.end(namespaceURI, name);
- } catch (Exception e) {
- log.error("End event threw exception", e);
- throw createSAXException(e);
- } catch (Error e) {
- log.error("End event threw error", e);
- throw e;
- }
- }
- }
- // Recover the previous match expression
- int slash = match.lastIndexOf('/');
- if (slash >= 0) {
- match = match.substring(0, slash);
- } else {
- match = "";
- }
- }
- /**
- * Process notification that a namespace prefix is going out of scope.
- *
- * @param prefix Prefix that is going out of scope
- *
- * @exception SAXException if a parsing error is to be reported
- */
- public void endPrefixMapping(String prefix) throws SAXException {
- if (saxLog.isDebugEnabled()) {
- saxLog.debug("endPrefixMapping(" + prefix + ")");
- }
- // Deregister this prefix mapping
- ArrayStack stack = (ArrayStack) namespaces.get(prefix);
- if (stack == null) {
- return;
- }
- try {
- stack.pop();
- if (stack.empty())
- namespaces.remove(prefix);
- } catch (EmptyStackException e) {
- throw createSAXException("endPrefixMapping popped too many times");
- }
- }
- /**
- * Process notification of ignorable whitespace received from the body of
- * an XML element.
- *
- * @param buffer The characters from the XML document
- * @param start Starting offset into the buffer
- * @param len Number of characters from the buffer
- *
- * @exception SAXException if a parsing error is to be reported
- */
- public void ignorableWhitespace(char buffer[], int start, int len)
- throws SAXException {
- if (saxLog.isDebugEnabled()) {
- saxLog.debug("ignorableWhitespace(" +
- new String(buffer, start, len) + ")");
- }
- ; // No processing required
- }
- /**
- * Process notification of a processing instruction that was encountered.
- *
- * @param target The processing instruction target
- * @param data The processing instruction data (if any)
- *
- * @exception SAXException if a parsing error is to be reported
- */
- public void processingInstruction(String target, String data)
- throws SAXException {
- if (saxLog.isDebugEnabled()) {
- saxLog.debug("processingInstruction('" + target + "','" + data + "')");
- }
- ; // No processing is required
- }
- /**
- * Gets the document locator associated with our parser.
- *
- * @return the Locator supplied by the document parser
- */
- public Locator getDocumentLocator() {
- return locator;
- }
- /**
- * Sets the document locator associated with our parser.
- *
- * @param locator The new locator
- */
- public void setDocumentLocator(Locator locator) {
- if (saxLog.isDebugEnabled()) {
- saxLog.debug("setDocumentLocator(" + locator + ")");
- }
- this.locator = locator;
- }
- /**
- * Process notification of a skipped entity.
- *
- * @param name Name of the skipped entity
- *
- * @exception SAXException if a parsing error is to be reported
- */
- public void skippedEntity(String name) throws SAXException {
- if (saxLog.isDebugEnabled()) {
- saxLog.debug("skippedEntity(" + name + ")");
- }
- ; // No processing required
- }
- /**
- * Process notification of the beginning of the document being reached.
- *
- * @exception SAXException if a parsing error is to be reported
- */
- public void startDocument() throws SAXException {
- if (saxLog.isDebugEnabled()) {
- saxLog.debug("startDocument()");
- }
- // ensure that the digester is properly configured, as
- // the digester could be used as a SAX ContentHandler
- // rather than via the parse() methods.
- configure();
- }
- /**
- * Process notification of the start of an XML element being reached.
- *
- * @param namespaceURI The Namespace URI, or the empty string if the element
- * has no Namespace URI or if Namespace processing is not being performed.
- * @param localName The local name (without prefix), or the empty
- * string if Namespace processing is not being performed.
- * @param qName The qualified name (with prefix), or the empty
- * string if qualified names are not available.\
- * @param list The attributes attached to the element. If there are
- * no attributes, it shall be an empty Attributes object.
- * @exception SAXException if a parsing error is to be reported
- */
- public void startElement(String namespaceURI, String localName,
- String qName, Attributes list)
- throws SAXException {
- boolean debug = log.isDebugEnabled();
- if (saxLog.isDebugEnabled()) {
- saxLog.debug("startElement(" + namespaceURI + "," + localName + "," +
- qName + ")");
- }
- // Save the body text accumulated for our surrounding element
- bodyTexts.push(bodyText);
- if (debug) {
- log.debug(" Pushing body text '" + bodyText.toString() + "'");
- }
- bodyText = new StringBuffer();
- // the actual element name is either in localName or qName, depending
- // on whether the parser is namespace aware
- String name = localName;
- if ((name == null) || (name.length() < 1)) {
- name = qName;
- }
- // Compute the current matching rule
- StringBuffer sb = new StringBuffer(match);
- if (match.length() > 0) {
- sb.append('/');
- }
- sb.append(name);
- match = sb.toString();
- if (debug) {
- log.debug(" New match='" + match + "'");
- }
- // Fire "begin" events for all relevant rules
- List rules = getRules().match(namespaceURI, match);
- matches.push(rules);
- if ((rules != null) && (rules.size() > 0)) {
- Substitutor substitutor = getSubstitutor();
- if (substitutor!= null) {
- list = substitutor.substitute(list);
- }
- for (int i = 0; i < rules.size(); i++) {
- try {
- Rule rule = (Rule) rules.get(i);
- if (debug) {
- log.debug(" Fire begin() for " + rule);
- }
- rule.begin(namespaceURI, name, list);
- } catch (Exception e) {
- log.error("Begin event threw exception", e);
- throw createSAXException(e);
- } catch (Error e) {
- log.error("Begin event threw error", e);
- throw e;
- }
- }
- } else {
- if (debug) {
- log.debug(" No rules found matching '" + match + "'.");
- }
- }
- }
- /**
- * Process notification that a namespace prefix is coming in to scope.
- *
- * @param prefix Prefix that is being declared
- * @param namespaceURI Corresponding namespace URI being mapped to
- *
- * @exception SAXException if a parsing error is to be reported
- */
- public void startPrefixMapping(String prefix, String namespaceURI)
- throws SAXException {
- if (saxLog.isDebugEnabled()) {
- saxLog.debug("startPrefixMapping(" + prefix + "," + namespaceURI + ")");
- }
- // Register this prefix mapping
- ArrayStack stack = (ArrayStack) namespaces.get(prefix);
- if (stack == null) {
- stack = new ArrayStack();
- namespaces.put(prefix, stack);
- }
- stack.push(namespaceURI);
- }
- // ----------------------------------------------------- DTDHandler Methods
- /**
- * Receive notification of a notation declaration event.
- *
- * @param name The notation name
- * @param publicId The public identifier (if any)
- * @param systemId The system identifier (if any)
- */
- public void notationDecl(String name, String publicId, String systemId) {
- if (saxLog.isDebugEnabled()) {
- saxLog.debug("notationDecl(" + name + "," + publicId + "," +
- systemId + ")");
- }
- }
- /**
- * Receive notification of an unparsed entity declaration event.
- *
- * @param name The unparsed entity name
- * @param publicId The public identifier (if any)
- * @param systemId The system identifier (if any)
- * @param notation The name of the associated notation
- */
- public void unparsedEntityDecl(String name, String publicId,
- String systemId, String notation) {
- if (saxLog.isDebugEnabled()) {
- saxLog.debug("unparsedEntityDecl(" + name + "," + publicId + "," +
- systemId + "," + notation + ")");
- }
- }
- // ----------------------------------------------- EntityResolver Methods
- /**
- * Set the <code>EntityResolver</code> used by SAX when resolving
- * public id and system id.
- * This must be called before the first call to <code>parse()</code>.
- * @param entityResolver a class that implement the <code>EntityResolver</code> interface.
- */
- public void setEntityResolver(EntityResolver entityResolver){
- this.entityResolver = entityResolver;
- }
- /**
- * Return the Entity Resolver used by the SAX parser.
- * @return Return the Entity Resolver used by the SAX parser.
- */
- public EntityResolver getEntityResolver(){
- return entityResolver;
- }
- /**
- * Resolve the requested external entity.
- *
- * @param publicId The public identifier of the entity being referenced
- * @param systemId The system identifier of the entity being referenced
- *
- * @exception SAXException if a parsing exception occurs
- *
- */
- public InputSource resolveEntity(String publicId, String systemId)
- throws SAXException {
- if (saxLog.isDebugEnabled()) {
- saxLog.debug("resolveEntity('" + publicId + "', '" + systemId + "')");
- }
- if (publicId != null)
- this.publicId = publicId;
- // Has this system identifier been registered?
- String entityURL = null;
- if (publicId != null) {
- entityURL = (String) entityValidator.get(publicId);
- }
- // Redirect the schema location to a local destination
- if (schemaLocation != null && entityURL == null && systemId != null){
- entityURL = (String)entityValidator.get(systemId);
- }
- if (entityURL == null) {
- if (systemId == null) {
- // cannot resolve
- if (log.isDebugEnabled()) {
- log.debug(" Cannot resolve entity: '" + entityURL + "'");
- }
- return (null);
- } else {
- // try to resolve using system ID
- if (log.isDebugEnabled()) {
- log.debug(" Trying to resolve using system ID '" + systemId + "'");
- }
- entityURL = systemId;
- }
- }
- // Return an input source to our alternative URL
- if (log.isDebugEnabled()) {
- log.debug(" Resolving to alternate DTD '" + entityURL + "'");
- }
- try {
- return (new InputSource(entityURL));
- } catch (Exception e) {
- throw createSAXException(e);
- }
- }
- // ------------------------------------------------- ErrorHandler Methods
- /**
- * Forward notification of a parsing error to the application supplied
- * error handler (if any).
- *
- * @param exception The error information
- *
- * @exception SAXException if a parsing exception occurs
- */
- public void error(SAXParseException exception) throws SAXException {
- log.error("Parse Error at line " + exception.getLineNumber() +
- " column " + exception.getColumnNumber() + ": " +
- exception.getMessage(), exception);
- if (errorHandler != null) {
- errorHandler.error(exception);
- }
- }
- /**
- * Forward notification of a fatal parsing error to the application
- * supplied error handler (if any).
- *
- * @param exception The fatal error information
- *
- * @exception SAXException if a parsing exception occurs
- */
- public void fatalError(SAXParseException exception) throws SAXException {
- log.error("Parse Fatal Error at line " + exception.getLineNumber() +
- " column " + exception.getColumnNumber() + ": " +
- exception.getMessage(), exception);
- if (errorHandler != null) {
- errorHandler.fatalError(exception);
- }
- }
- /**
- * Forward notification of a parse warning to the application supplied
- * error handler (if any).
- *
- * @param exception The warning information
- *
- * @exception SAXException if a parsing exception occurs
- */
- public void warning(SAXParseException exception) throws SAXException {
- if (errorHandler != null) {
- log.warn("Parse Warning Error at line " + exception.getLineNumber() +
- " column " + exception.getColumnNumber() + ": " +
- exception.getMessage(), exception);
- errorHandler.warning(exception);
- }
- }
- // ------------------------------------------------------- Public Methods
- /**
- * Log a message to our associated logger.
- *
- * @param message The message to be logged
- * @deprecated Call getLogger() and use it's logging methods
- */
- public void log(String message) {
- log.info(message);
- }
- /**
- * Log a message and exception to our associated logger.
- *
- * @param message The message to be logged
- * @deprecated Call getLogger() and use it's logging methods
- */
- public void log(String message, Throwable exception) {
- log.error(message, exception);
- }
- /**
- * Parse the content of the specified file using this Digester. Returns
- * the root element from the object stack (if any).
- *
- * @param file File containing the XML data to be parsed
- *
- * @exception IOException if an input/output error occurs
- * @exception SAXException if a parsing exception occurs
- */
- public Object parse(File file) throws IOException, SAXException {
- configure();
- InputSource input = new InputSource(new FileInputStream(file));
- input.setSystemId("file://" + file.getAbsolutePath());
- getXMLReader().parse(input);
- return (root);
- }
- /**
- * Parse the content of the specified input source using this Digester.
- * Returns the root element from the object stack (if any).
- *
- * @param input Input source containing the XML data to be parsed
- *
- * @exception IOException if an input/output error occurs
- * @exception SAXException if a parsing exception occurs
- */
- public Object parse(InputSource input) throws IOException, SAXException {
- configure();
- getXMLReader().parse(input);
- return (root);
- }
- /**
- * Parse the content of the specified input stream using this Digester.
- * Returns the root element from the object stack (if any).
- *
- * @param input Input stream containing the XML data to be parsed
- *
- * @exception IOException if an input/output error occurs
- * @exception SAXException if a parsing exception occurs
- */
- public Object parse(InputStream input) throws IOException, SAXException {
- configure();
- InputSource is = new InputSource(input);
- getXMLReader().parse(is);
- return (root);
- }
- /**
- * Parse the content of the specified reader using this Digester.
- * Returns the root element from the object stack (if any).
- *
- * @param reader Reader containing the XML data to be parsed
- *
- * @exception IOException if an input/output error occurs
- * @exception SAXException if a parsing exception occurs
- */
- public Object parse(Reader reader) throws IOException, SAXException {
- configure();
- InputSource is = new InputSource(reader);
- getXMLReader().parse(is);
- return (root);
- }
- /**
- * Parse the content of the specified URI using this Digester.
- * Returns the root element from the object stack (if any).
- *
- * @param uri URI containing the XML data to be parsed
- *
- * @exception IOException if an input/output error occurs
- * @exception SAXException if a parsing exception occurs
- */
- public Object parse(String uri) throws IOException, SAXException {
- configure();
- InputSource is = new InputSource(uri);
- getXMLReader().parse(is);
- return (root);
- }
- /**
- * <p>Register the specified DTD URL for the specified public identifier.
- * This must be called before the first call to <code>parse()</code>.
- * </p><p>
- * <code>Digester</code> contains an internal <code>EntityResolver</code>
- * implementation. This maps <code>PUBLICID</code>'s to URLs
- * (from which the resource will be loaded). A common use case for this
- * method is to register local URLs (possibly computed at runtime by a
- * classloader) for DTDs. This allows the performance advantage of using
- * a local version without having to ensure every <code>SYSTEM</code>
- * URI on every processed xml document is local. This implementation provides
- * only basic functionality. If more sophisticated features are required,
- * using {@link #setEntityResolver} to set a custom resolver is recommended.
- * </p><p>
- * <strong>Note:</strong> This method will have no effect when a custom
- * <code>EntityResolver</code> has been set. (Setting a custom
- * <code>EntityResolver</code> overrides the internal implementation.)
- * </p>
- * @param publicId Public identifier of the DTD to be resolved
- * @param entityURL The URL to use for reading this DTD
- */
- public void register(String publicId, String entityURL) {
- if (log.isDebugEnabled()) {
- log.debug("register('" + publicId + "', '" + entityURL + "'");
- }
- entityValidator.put(publicId, entityURL);
- }
- // --------------------------------------------------------- Rule Methods
- /**
- * <p>Register a new Rule matching the specified pattern.
- * This method sets the <code>Digester</code> property on the rule.</p>
- *
- * @param pattern Element matching pattern
- * @param rule Rule to be registered
- */
- public void addRule(String pattern, Rule rule) {
- rule.setDigester(this);
- getRules().add(pattern, rule);
- }
- /**
- * R