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 javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.apache.commons.collections.ArrayStack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; 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; public class Digester extends DefaultHandler { public Digester() {} public Digester(SAXParser parser) { this.parser = parser; } public Digester(XMLReader reader) { this.reader = reader; } protected StringBuffer bodyText = new StringBuffer(); protected ArrayStack bodyTexts = new ArrayStack(); protected ClassLoader classLoader = null; protected boolean configured = false; protected EntityResolver entityResolver; protected HashMap entityValidator = new HashMap(); protected ErrorHandler errorHandler = null; protected SAXParserFactory factory = null; private static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource"; protected String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; protected Locator locator = null; protected String match = ""; protected boolean namespaceAware = false; protected HashMap namespaces = new HashMap(); protected ArrayStack params = new ArrayStack(); protected SAXParser parser = null; protected String publicId = null; protected XMLReader reader = null; protected Object root = null; protected Rules rules = null; protected String schemaLanguage = "http://www.w3.org/2001/XMLSchema"; protected String schemaLocation = null; protected ArrayStack stack = new ArrayStack(); protected boolean useContextClassLoader = false; protected boolean validating = false; protected Log log = LogFactory.getLog("org.apache.commons.digester.Digester"); protected Log saxLog = LogFactory.getLog("org.apache.commons.digester.Digester.sax"); protected static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; public String findNamespaceURI(String prefix) { ArrayStack stack = (ArrayStack)this.namespaces.get(prefix); if (stack == null) return null; try { return (String)stack.peek(); } catch (EmptyStackException e) { return null; } } public ClassLoader getClassLoader() { if (this.classLoader != null) return this.classLoader; if (this.useContextClassLoader) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if (classLoader != null) return classLoader; } return getClass().getClassLoader(); } public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public int getCount() { return this.stack.size(); } public String getCurrentElementName() { String elementName = this.match; int lastSlash = elementName.lastIndexOf('/'); if (lastSlash >= 0) elementName = elementName.substring(lastSlash + 1); return elementName; } public int getDebug() { return 0; } public void setDebug(int debug) {} public ErrorHandler getErrorHandler() { return this.errorHandler; } public void setErrorHandler(ErrorHandler errorHandler) { this.errorHandler = errorHandler; } public SAXParserFactory getFactory() { if (this.factory == null) { this.factory = SAXParserFactory.newInstance(); this.factory.setNamespaceAware(this.namespaceAware); this.factory.setValidating(this.validating); } return this.factory; } public boolean getFeature(String feature) throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException { return getFactory().getFeature(feature); } public void setFeature(String feature, boolean value) throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException { getFactory().setFeature(feature, value); } public Log getLogger() { return this.log; } public void setLogger(Log log) { this.log = log; } public String getMatch() { return this.match; } public boolean getNamespaceAware() { return this.namespaceAware; } public void setNamespaceAware(boolean namespaceAware) { this.namespaceAware = namespaceAware; } public void setPublicId(String publicId) { this.publicId = publicId; } public String getPublicId() { return this.publicId; } public String getRuleNamespaceURI() { return getRules().getNamespaceURI(); } public void setRuleNamespaceURI(String ruleNamespaceURI) { getRules().setNamespaceURI(ruleNamespaceURI); } public SAXParser getParser() { if (this.parser != null) return this.parser; try { this.parser = getFactory().newSAXParser(); } catch (Exception e) { this.log.error("Digester.getParser: ", e); return null; } try { if (this.schemaLocation != null) { setProperty(this.JAXP_SCHEMA_LANGUAGE, this.schemaLanguage); setProperty("http://java.sun.com/xml/jaxp/properties/schemaSource", this.schemaLocation); } } catch (Exception e) { this.log.warn("" + e); } return this.parser; } public Object getProperty(String property) throws SAXNotRecognizedException, SAXNotSupportedException { return getParser().getProperty(property); } public void setProperty(String property, Object value) throws SAXNotRecognizedException, SAXNotSupportedException { getParser().setProperty(property, value); } public XMLReader getReader() { try { return getXMLReader(); } catch (SAXException e) { this.log.error("Cannot get XMLReader", e); return null; } } public Rules getRules() { if (this.rules == null) { this.rules = new RulesBase(); this.rules.setDigester(this); } return this.rules; } public void setRules(Rules rules) { this.rules = rules; this.rules.setDigester(this); } public String getSchema() { return this.schemaLocation; } public void setSchema(String schemaLocation) { this.schemaLocation = schemaLocation; } public String getSchemaLanguage() { return this.schemaLanguage; } public void setSchemaLanguage(String schemaLanguage) { this.schemaLanguage = schemaLanguage; } public boolean getUseContextClassLoader() { return this.useContextClassLoader; } public void setUseContextClassLoader(boolean use) { this.useContextClassLoader = use; } public boolean getValidating() { return this.validating; } public void setValidating(boolean validating) { this.validating = validating; } public XMLReader getXMLReader() throws SAXException { if (this.reader == null) this.reader = getParser().getXMLReader(); this.reader.setDTDHandler(this); this.reader.setContentHandler(this); if (this.entityResolver == null) { this.reader.setEntityResolver(this); } else { this.reader.setEntityResolver(this.entityResolver); } this.reader.setErrorHandler(this); return this.reader; } public void characters(char[] buffer, int start, int length) throws SAXException { if (this.saxLog.isDebugEnabled()) this.saxLog.debug("characters(" + new String(buffer, start, length) + ")"); this.bodyText.append(buffer, start, length); } public void endDocument() throws SAXException { if (this.saxLog.isDebugEnabled()) if (getCount() > 1) { this.saxLog.debug("endDocument(): " + getCount() + " elements left"); } else { this.saxLog.debug("endDocument()"); } while (getCount() > 1) pop(); Iterator rules = getRules().rules().iterator(); while (rules.hasNext()) { Rule rule = rules.next(); try { rule.finish(); } catch (Exception e) { this.log.error("Finish event threw exception", e); throw createSAXException(e); } catch (Error e) { this.log.error("Finish event threw error", e); throw e; } } clear(); } public void endElement(String namespaceURI, String localName, String qName) throws SAXException { boolean debug = this.log.isDebugEnabled(); if (debug) { if (this.saxLog.isDebugEnabled()) this.saxLog.debug("endElement(" + namespaceURI + "," + localName + "," + qName + ")"); this.log.debug(" match='" + this.match + "'"); this.log.debug(" bodyText='" + this.bodyText + "'"); } String name = localName; if (name == null || name.length() < 1) name = qName; List rules = getRules().match(namespaceURI, this.match); if (rules != null && rules.size() > 0) { String bodyText = this.bodyText.toString(); for (int i = 0; i < rules.size(); i++) { try { Rule rule = rules.get(i); if (debug) this.log.debug(" Fire body() for " + rule); rule.body(namespaceURI, name, bodyText); } catch (Exception e) { this.log.error("Body event threw exception", e); throw createSAXException(e); } catch (Error e) { this.log.error("Body event threw error", e); throw e; } } } else if (debug) { this.log.debug(" No rules found matching '" + this.match + "'."); } this.bodyText = (StringBuffer)this.bodyTexts.pop(); if (debug) this.log.debug(" Popping body text '" + this.bodyText.toString() + "'"); if (rules != null) for (int i = 0; i < rules.size(); i++) { int j = rules.size() - i - 1; try { Rule rule = rules.get(j); if (debug) this.log.debug(" Fire end() for " + rule); rule.end(namespaceURI, name); } catch (Exception e) { this.log.error("End event threw exception", e); throw createSAXException(e); } catch (Error e) { this.log.error("End event threw error", e); throw e; } } int slash = this.match.lastIndexOf('/'); if (slash >= 0) { this.match = this.match.substring(0, slash); } else { this.match = ""; } } public void endPrefixMapping(String prefix) throws SAXException { if (this.saxLog.isDebugEnabled()) this.saxLog.debug("endPrefixMapping(" + prefix + ")"); ArrayStack stack = (ArrayStack)this.namespaces.get(prefix); if (stack == null) return; try { stack.pop(); if (stack.empty()) this.namespaces.remove(prefix); } catch (EmptyStackException e) { throw createSAXException("endPrefixMapping popped too many times"); } } public void ignorableWhitespace(char[] buffer, int start, int len) throws SAXException { if (this.saxLog.isDebugEnabled()) this.saxLog.debug("ignorableWhitespace(" + new String(buffer, start, len) + ")"); } public void processingInstruction(String target, String data) throws SAXException { if (this.saxLog.isDebugEnabled()) this.saxLog.debug("processingInstruction('" + target + "','" + data + "')"); } public Locator getDocumentLocator() { return this.locator; } public void setDocumentLocator(Locator locator) { if (this.saxLog.isDebugEnabled()) this.saxLog.debug("setDocumentLocator(" + locator + ")"); this.locator = locator; } public void skippedEntity(String name) throws SAXException { if (this.saxLog.isDebugEnabled()) this.saxLog.debug("skippedEntity(" + name + ")"); } public void startDocument() throws SAXException { if (this.saxLog.isDebugEnabled()) this.saxLog.debug("startDocument()"); configure(); } public void startElement(String namespaceURI, String localName, String qName, Attributes list) throws SAXException { boolean debug = this.log.isDebugEnabled(); if (this.saxLog.isDebugEnabled()) this.saxLog.debug("startElement(" + namespaceURI + "," + localName + "," + qName + ")"); this.bodyTexts.push(this.bodyText); if (debug) this.log.debug(" Pushing body text '" + this.bodyText.toString() + "'"); this.bodyText = new StringBuffer(); String name = localName; if (name == null || name.length() < 1) name = qName; StringBuffer sb = new StringBuffer(this.match); if (this.match.length() > 0) sb.append('/'); sb.append(name); this.match = sb.toString(); if (debug) this.log.debug(" New match='" + this.match + "'"); List rules = getRules().match(namespaceURI, this.match); if (rules != null && rules.size() > 0) { String bodyText = this.bodyText.toString(); for (int i = 0; i < rules.size(); i++) { try { Rule rule = rules.get(i); if (debug) this.log.debug(" Fire begin() for " + rule); rule.begin(namespaceURI, name, list); } catch (Exception e) { this.log.error("Begin event threw exception", e); throw createSAXException(e); } catch (Error e) { this.log.error("Begin event threw error", e); throw e; } } } else if (debug) { this.log.debug(" No rules found matching '" + this.match + "'."); } } public void startPrefixMapping(String prefix, String namespaceURI) throws SAXException { if (this.saxLog.isDebugEnabled()) this.saxLog.debug("startPrefixMapping(" + prefix + "," + namespaceURI + ")"); ArrayStack stack = (ArrayStack)this.namespaces.get(prefix); if (stack == null) { stack = new ArrayStack(); this.namespaces.put(prefix, stack); } stack.push(namespaceURI); } public void notationDecl(String name, String publicId, String systemId) { if (this.saxLog.isDebugEnabled()) this.saxLog.debug("notationDecl(" + name + "," + publicId + "," + systemId + ")"); } public void unparsedEntityDecl(String name, String publicId, String systemId, String notation) { if (this.saxLog.isDebugEnabled()) this.saxLog.debug("unparsedEntityDecl(" + name + "," + publicId + "," + systemId + "," + notation + ")"); } public void setEntityResolver(EntityResolver entityResolver) { this.entityResolver = entityResolver; } public EntityResolver getEntityResolver() { return this.entityResolver; } public InputSource resolveEntity(String publicId, String systemId) throws SAXException { boolean debug = this.log.isDebugEnabled(); if (this.saxLog.isDebugEnabled()) this.saxLog.debug("resolveEntity('" + publicId + "', '" + systemId + "')"); if (publicId != null) this.publicId = publicId; String entityURL = null; if (publicId != null) entityURL = (String)this.entityValidator.get(publicId); if (this.schemaLocation != null && entityURL == null && systemId != null) entityURL = (String)this.entityValidator.get(systemId); if (entityURL == null) return null; if (debug) this.log.debug(" Resolving to alternate DTD '" + entityURL + "'"); try { return new InputSource(entityURL); } catch (Exception e) { throw createSAXException(e); } } public void error(SAXParseException exception) throws SAXException { this.log.error("Parse Error at line " + exception.getLineNumber() + " column " + exception.getColumnNumber() + ": " + exception.getMessage(), exception); if (this.errorHandler != null) this.errorHandler.error(exception); } public void fatalError(SAXParseException exception) throws SAXException { this.log.error("Parse Fatal Error at line " + exception.getLineNumber() + " column " + exception.getColumnNumber() + ": " + exception.getMessage(), exception); if (this.errorHandler != null) this.errorHandler.fatalError(exception); } public void warning(SAXParseException exception) throws SAXException { if (this.errorHandler != null) { this.log.warn("Parse Warning Error at line " + exception.getLineNumber() + " column " + exception.getColumnNumber() + ": " + exception.getMessage(), exception); this.errorHandler.warning(exception); } } public void log(String message) { this.log.info(message); } public void log(String message, Throwable exception) { this.log.error(message, exception); } 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 this.root; } public Object parse(InputSource input) throws IOException, SAXException { configure(); getXMLReader().parse(input); return this.root; } public Object parse(InputStream input) throws IOException, SAXException { configure(); InputSource is = new InputSource(input); getXMLReader().parse(is); return this.root; } public Object parse(Reader reader) throws IOException, SAXException { configure(); InputSource is = new InputSource(reader); getXMLReader().parse(is); return this.root; } public Object parse(String uri) throws IOException, SAXException { configure(); InputSource is = new InputSource(uri); getXMLReader().parse(is); return this.root; } public void register(String publicId, String entityURL) { if (this.log.isDebugEnabled()) this.log.debug("register('" + publicId + "', '" + entityURL + "'"); this.entityValidator.put(publicId, entityURL); } public void addRule(String pattern, Rule rule) { rule.setDigester(this); getRules().add(pattern, rule); } public void addRuleSet(RuleSet ruleSet) { String oldNamespaceURI = getRuleNamespaceURI(); String newNamespaceURI = ruleSet.getNamespaceURI(); if (this.log.isDebugEnabled()) if (newNamespaceURI == null) { this.log.debug("addRuleSet() with no namespace URI"); } else { this.log.debug("addRuleSet() with namespace URI " + newNamespaceURI); } setRuleNamespaceURI(newNamespaceURI); ruleSet.addRuleInstances(this); setRuleNamespaceURI(oldNamespaceURI); } public void addBeanPropertySetter(String pattern) { addRule(pattern, new BeanPropertySetterRule()); } public void addBeanPropertySetter(String pattern, String propertyName) { addRule(pattern, new BeanPropertySetterRule(propertyName)); } public void addCallMethod(String pattern, String methodName) { addRule(pattern, new CallMethodRule(methodName)); } public void addCallMethod(String pattern, String methodName, int paramCount) { addRule(pattern, new CallMethodRule(methodName, paramCount)); } public void addCallMethod(String pattern, String methodName, int paramCount, String[] paramTypes) { addRule(pattern, new CallMethodRule(methodName, paramCount, paramTypes)); } public void addCallMethod(String pattern, String methodName, int paramCount, Class[] paramTypes) { addRule(pattern, new CallMethodRule(methodName, paramCount, paramTypes)); } public void addCallParam(String pattern, int paramIndex) { addRule(pattern, new CallParamRule(paramIndex)); } public void addCallParam(String pattern, int paramIndex, String attributeName) { addRule(pattern, new CallParamRule(paramIndex, attributeName)); } public void addCallParam(String pattern, int paramIndex, boolean fromStack) { addRule(pattern, new CallParamRule(paramIndex, fromStack)); } public void addCallParam(String pattern, int paramIndex, int stackIndex) { addRule(pattern, new CallParamRule(paramIndex, stackIndex)); } public void addFactoryCreate(String pattern, String className) { addFactoryCreate(pattern, className, false); } public void addFactoryCreate(String pattern, Class clazz) { addFactoryCreate(pattern, clazz, false); } public void addFactoryCreate(String pattern, String className, String attributeName) { addFactoryCreate(pattern, className, attributeName, false); } public void addFactoryCreate(String pattern, Class clazz, String attributeName) { addFactoryCreate(pattern, clazz, attributeName, false); } public void addFactoryCreate(String pattern, ObjectCreationFactory creationFactory) { addFactoryCreate(pattern, creationFactory, false); } public void addFactoryCreate(String pattern, String className, boolean ignoreCreateExceptions) { addRule(pattern, new FactoryCreateRule(className, ignoreCreateExceptions)); } public void addFactoryCreate(String pattern, Class clazz, boolean ignoreCreateExceptions) { addRule(pattern, new FactoryCreateRule(clazz, ignoreCreateExceptions)); } public void addFactoryCreate(String pattern, String className, String attributeName, boolean ignoreCreateExceptions) { addRule(pattern, new FactoryCreateRule(className, attributeName, ignoreCreateExceptions)); } public void addFactoryCreate(String pattern, Class clazz, String attributeName, boolean ignoreCreateExceptions) { addRule(pattern, new FactoryCreateRule(clazz, attributeName, ignoreCreateExceptions)); } public void addFactoryCreate(String pattern, ObjectCreationFactory creationFactory, boolean ignoreCreateExceptions) { creationFactory.setDigester(this); addRule(pattern, new FactoryCreateRule(creationFactory, ignoreCreateExceptions)); } public void addObjectCreate(String pattern, String className) { addRule(pattern, new ObjectCreateRule(className)); } public void addObjectCreate(String pattern, Class clazz) { addRule(pattern, new ObjectCreateRule(clazz)); } public void addObjectCreate(String pattern, String className, String attributeName) { addRule(pattern, new ObjectCreateRule(className, attributeName)); } public void addObjectCreate(String pattern, String attributeName, Class clazz) { addRule(pattern, new ObjectCreateRule(attributeName, clazz)); } public void addSetNext(String pattern, String methodName) { addRule(pattern, new SetNextRule(methodName)); } public void addSetNext(String pattern, String methodName, String paramType) { addRule(pattern, new SetNextRule(methodName, paramType)); } public void addSetRoot(String pattern, String methodName) { addRule(pattern, new SetRootRule(methodName)); } public void addSetRoot(String pattern, String methodName, String paramType) { addRule(pattern, new SetRootRule(methodName, paramType)); } public void addSetProperties(String pattern) { addRule(pattern, new SetPropertiesRule()); } public void addSetProperties(String pattern, String attributeName, String propertyName) { addRule(pattern, new SetPropertiesRule(attributeName, propertyName)); } public void addSetProperties(String pattern, String[] attributeNames, String[] propertyNames) { addRule(pattern, new SetPropertiesRule(attributeNames, propertyNames)); } public void addSetProperty(String pattern, String name, String value) { addRule(pattern, new SetPropertyRule(name, value)); } public void addSetTop(String pattern, String methodName) { addRule(pattern, new SetTopRule(methodName)); } public void addSetTop(String pattern, String methodName, String paramType) { addRule(pattern, new SetTopRule(methodName, paramType)); } public void clear() { this.match = ""; this.bodyTexts.clear(); this.params.clear(); this.publicId = null; this.stack.clear(); } public Object peek() { try { return this.stack.peek(); } catch (EmptyStackException e) { this.log.warn("Empty stack (returning null)"); return null; } } public Object peek(int n) { try { return this.stack.peek(n); } catch (EmptyStackException e) { this.log.warn("Empty stack (returning null)"); return null; } } public Object pop() { try { return this.stack.pop(); } catch (EmptyStackException e) { this.log.warn("Empty stack (returning null)"); return null; } } public void push(Object object) { if (this.stack.size() == 0) this.root = object; this.stack.push(object); } public Object getRoot() { return this.root; } protected void configure() { if (this.configured) return; this.configured = true; } Map getRegistrations() { return this.entityValidator; } List getRules(String match) { return getRules().match(match); } Object peekParams() { try { return this.params.peek(); } catch (EmptyStackException e) { this.log.warn("Empty stack (returning null)"); return null; } } Object peekParams(int n) { try { return this.params.peek(n); } catch (EmptyStackException e) { this.log.warn("Empty stack (returning null)"); return null; } } Object popParams() { try { if (this.log.isTraceEnabled()) this.log.trace("Popping params"); return this.params.pop(); } catch (EmptyStackException e) { this.log.warn("Empty stack (returning null)"); return null; } } void pushParams(Object object) { if (this.log.isTraceEnabled()) this.log.trace("Pushing params"); this.params.push(object); } protected SAXException createSAXException(String message, Exception e) { if (e != null && e instanceof InvocationTargetException) { Throwable t = ((InvocationTargetException)e).getTargetException(); if (t != null && t instanceof Exception) e = (Exception)t; } if (this.locator != null) { String error = "Error at (" + this.locator.getLineNumber() + ", " + this.locator.getColumnNumber() + ": " + message; if (e != null) return new SAXParseException(error, this.locator, e); return new SAXParseException(error, this.locator); } this.log.error("No Locator!"); if (e != null) return new SAXException(message, e); return new SAXException(message); } protected SAXException createSAXException(Exception e) { if (e instanceof InvocationTargetException) { Throwable t = ((InvocationTargetException)e).getTargetException(); if (t != null && t instanceof Exception) e = (Exception)t; } return createSAXException(e.getMessage(), e); } protected SAXException createSAXException(String message) { return createSAXException(message, null); } }