ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/tomcat/util/digester/Digester.java

Path
ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/tomcat/util/digester/Digester.java
Status
scanned
Type
file
Name
Digester.java
Extension
.java
Programming language
Java
Mime type
text/html
File type
HTML document, ASCII text, with CRLF line terminators
Tag

      
    
Rootfs path

      
    
Size
68776 (67.2 KB)
MD5
c817c829eb5bd414623f325f09fbcda2
SHA1
c7232a7695c348113d73ef4b839ade8598d982b5
SHA256
085e4901e7c9a9745955e6fd681a18283c4db4bb4377c3f5753940f97efb7c69
SHA512

      
    
SHA1_git
db634fedf465e61f884c9d05dd320091f3d0871e
Is binary

      
    
Is text
True
Is archive

      
    
Is media

      
    
Is legal

      
    
Is manifest

      
    
Is readme

      
    
Is top level

      
    
Is key file

      
    
Digester.java | 67.2 KB |

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.tomcat.util.digester; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.EmptyStackException; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.ExceptionUtils; import org.apache.tomcat.util.IntrospectionUtils; import org.apache.tomcat.util.IntrospectionUtils.PropertySource; import org.apache.tomcat.util.buf.B2CConverter; import org.apache.tomcat.util.buf.ToStringUtil; import org.apache.tomcat.util.res.StringManager; 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.ext.DefaultHandler2; import org.xml.sax.ext.EntityResolver2; import org.xml.sax.ext.Locator2; import org.xml.sax.helpers.AttributesImpl; /** * <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 DefaultHandler2 { // ---------------------------------------------------------- Static Fields protected static IntrospectionUtils.PropertySource[] propertySources; private static boolean propertySourcesSet = false; protected static final StringManager sm = StringManager.getManager(Digester.class); static { String classNames = System.getProperty("org.apache.tomcat.util.digester.PROPERTY_SOURCE"); ArrayList<IntrospectionUtils.PropertySource> sourcesList = new ArrayList<>(); IntrospectionUtils.PropertySource[] sources = null; if (classNames != null) { StringTokenizer classNamesTokenizer = new StringTokenizer(classNames, ","); while (classNamesTokenizer.hasMoreTokens()) { String className = classNamesTokenizer.nextToken().trim(); ClassLoader[] cls = new ClassLoader[] { Digester.class.getClassLoader(), Thread.currentThread().getContextClassLoader() }; for (ClassLoader cl : cls) { try { Class<?> clazz = Class.forName(className, true, cl); sourcesList.add((PropertySource) clazz.getConstructor().newInstance()); break; } catch (Throwable t) { ExceptionUtils.handleThrowable(t); LogFactory.getLog(Digester.class) .error(sm.getString("digester.propertySourceLoadError", className), t); } } } sources = sourcesList.toArray(new IntrospectionUtils.PropertySource[0]); } if (sources != null) { propertySources = sources; propertySourcesSet = true; } if (Boolean.getBoolean("org.apache.tomcat.util.digester.REPLACE_SYSTEM_PROPERTIES")) { replaceSystemProperties(); } } public static void setPropertySource(IntrospectionUtils.PropertySource propertySource) { if (!propertySourcesSet) { propertySources = new IntrospectionUtils.PropertySource[1]; propertySources[0] = propertySource; propertySourcesSet = true; } } public static void setPropertySource(IntrospectionUtils.PropertySource[] propertySources) { if (!propertySourcesSet) { Digester.propertySources = propertySources; propertySourcesSet = true; } } private static final HashSet<String> generatedClasses = new HashSet<>(); public static void addGeneratedClass(String className) { generatedClasses.add(className); } public static String[] getGeneratedClasses() { return generatedClasses.toArray(new String[0]); } public interface GeneratedCodeLoader { Object loadGeneratedCode(String className); } private static GeneratedCodeLoader generatedCodeLoader; public static boolean isGeneratedCodeLoaderSet() { return generatedCodeLoader != null; } public static void setGeneratedCodeLoader(GeneratedCodeLoader generatedCodeLoader) { if (Digester.generatedCodeLoader == null) { Digester.generatedCodeLoader = generatedCodeLoader; } } public static Object loadGeneratedClass(String className) { if (generatedCodeLoader != null) { return generatedCodeLoader.loadGeneratedCode(className); } return null; } // --------------------------------------------------- Instance Variables protected IntrospectionUtils.PropertySource[] source; /** * The body text of the current element. */ protected StringBuilder bodyText = new StringBuilder(); /** * The stack of body text string buffers for surrounding elements. */ protected ArrayStack<StringBuilder> 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<List<Rule>> 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 uses this class */ protected EntityResolver entityResolver; /** * The URLs of entityValidator that have been registered, keyed by the public identifier that corresponds. */ protected HashMap<String,String> 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; /** * 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<String,ArrayStack<String>> namespaces = new HashMap<>(); /** * The parameters stack being utilized by CallMethodRule and CallParamRule rules. */ protected ArrayStack<Object> 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 object stack being constructed. */ protected ArrayStack<Object> 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; /** * Warn on missing attributes and elements. */ protected boolean rulesValidation = false; /** * Fake attributes map (attributes are often used for object creation). */ protected Map<Class<?>,List<String>> fakeAttributes = null; /** * The Log to which most logging calls will be made. */ protected Log log = LogFactory.getLog(Digester.class); /** * The Log to which all SAX event related logging calls will be made. */ protected Log saxLog = LogFactory.getLog("org.apache.tomcat.util.digester.Digester.sax"); /** * Generated code. */ protected StringBuilder code = null; public Digester() { propertySourcesSet = true; ArrayList<IntrospectionUtils.PropertySource> sourcesList = new ArrayList<>(); boolean systemPropertySourceFound = false; if (propertySources != null) { for (IntrospectionUtils.PropertySource source : propertySources) { if (source instanceof SystemPropertySource) { systemPropertySourceFound = true; } sourcesList.add(source); } } if (!systemPropertySourceFound) { sourcesList.add(new SystemPropertySource()); } source = sourcesList.toArray(new IntrospectionUtils.PropertySource[0]); } public static void replaceSystemProperties() { Log log = LogFactory.getLog(Digester.class); if (propertySources != null) { Properties properties = System.getProperties(); Set<String> names = properties.stringPropertyNames(); for (String name : names) { String value = System.getProperty(name); if (value != null) { try { String newValue = IntrospectionUtils.replaceProperties(value, null, propertySources, null); if (!value.equals(newValue)) { System.setProperty(name, newValue); } } catch (Exception e) { log.warn(sm.getString("digester.failedToUpdateSystemProperty", name, value), e); } } } } } public void startGeneratingCode() { code = new StringBuilder(); } public void endGeneratingCode() { code = null; known.clear(); } public StringBuilder getGeneratedCode() { return code; } protected ArrayList<Object> known = new ArrayList<>(); public void setKnown(Object object) { known.add(object); } public String toVariableName(Object object) { boolean found = false; int pos = 0; if (!known.isEmpty()) { for (int i = known.size() - 1; i >= 0; i--) { if (known.get(i) == object) { pos = i; found = true; break; } } } if (!found) { pos = known.size(); known.add(object); } return "tc_" + object.getClass().getSimpleName() + "_" + String.valueOf(pos); } // ------------------------------------------------------------- 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 * * @return the namespace URI */ public String findNamespaceURI(String prefix) { ArrayStack<String> stack = namespaces.get(prefix); if (stack == null) { return null; } try { return 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> * * @return the classloader */ 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 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; } /** * SAX parser factory method. * * @return the SAXParserFactory we will use, creating one if necessary. * * @throws ParserConfigurationException Error creating parser * @throws SAXNotSupportedException Error creating parser * @throws SAXNotRecognizedException Error creating parser */ public SAXParserFactory getFactory() throws SAXNotRecognizedException, SAXNotSupportedException, ParserConfigurationException { if (factory == null) { factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(namespaceAware); // Preserve xmlns attributes if (namespaceAware) { factory.setFeature("http://xml.org/sax/features/namespace-prefixes", true); } factory.setValidating(validating); if (validating) { // Enable DTD validation factory.setFeature("http://xml.org/sax/features/validation", true); // Enable schema validation factory.setFeature("http://apache.org/xml/features/validation/schema", true); } } return factory; } /** * 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. * * @param log The logger that will be used */ 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 * * @return the SAX logger */ 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 public id of the current file being parsed. * * @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 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 { parser = getFactory().newSAXParser(); } catch (Exception e) { log.error(sm.getString("digester.createParserError"), 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 * * @return the property value * * @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); } /** * 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. * * @return the rules */ 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 a boolean to indicate if 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 rules validation flag. */ public boolean getRulesValidation() { return this.rulesValidation; } /** * Set the rules validation flag. This must be called before <code>parse()</code> is called the first time. * * @param rulesValidation The new rules validation flag. */ public void setRulesValidation(boolean rulesValidation) { this.rulesValidation = rulesValidation; } /** * @return the fake attributes list. */ public Map<Class<?>,List<String>> getFakeAttributes() { return this.fakeAttributes; } /** * Determine if an attribute is a fake attribute. * * @param object The object * @param name The attribute name * * @return <code>true</code> if this is a fake attribute */ public boolean isFakeAttribute(Object object, String name) { if (fakeAttributes == null) { return false; } List<String> result = fakeAttributes.get(object.getClass()); if (result == null) { result = fakeAttributes.get(Object.class); } if (result == null) { return false; } else { return result.contains(name); } } /** * Set the fake attributes. * * @param fakeAttributes The new fake attributes. */ public void setFakeAttributes(Map<Class<?>,List<String>> fakeAttributes) { this.fakeAttributes = fakeAttributes; } /** * 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. * * @return the XML reader * * @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); EntityResolver entityResolver = getEntityResolver(); if (entityResolver == null) { entityResolver = this; } // Wrap the resolver so we can perform ${...} property replacement if (entityResolver instanceof EntityResolver2) { entityResolver = new EntityResolver2Wrapper((EntityResolver2) entityResolver, source, classLoader); } else { entityResolver = new EntityResolverWrapper(entityResolver, source, classLoader); } reader.setEntityResolver(entityResolver); reader.setProperty("http://xml.org/sax/properties/lexical-handler", this); reader.setErrorHandler(this); return reader; } // ------------------------------------------------- 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 */ @Override public void characters(char[] buffer, int start, int length) throws SAXException { if (saxLog.isTraceEnabled()) { saxLog.trace("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 */ @Override public void endDocument() throws SAXException { if (saxLog.isTraceEnabled()) { if (getCount() > 1) { saxLog.trace("endDocument(): " + getCount() + " elements left"); } else { saxLog.trace("endDocument()"); } } while (getCount() > 1) { pop(); } // Fire "finish" events for all defined rules for (Rule rule : getRules().rules()) { try { rule.finish(); } catch (Exception e) { log.error(sm.getString("digester.error.finish"), e); throw createSAXException(e); } catch (Error e) { log.error(sm.getString("digester.error.finish"), 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 */ @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { boolean debug = log.isTraceEnabled(); if (debug) { if (saxLog.isDebugEnabled()) { saxLog.trace("endElement(" + namespaceURI + "," + localName + "," + qName + ")"); } log.trace(" match='" + match + "'"); log.trace(" bodyText='" + bodyText + "'"); } // Parse system properties bodyText = updateBodyText(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.isEmpty())) { name = qName; } // Fire "body" events for all relevant rules List<Rule> rules = matches.pop(); if ((rules != null) && (!rules.isEmpty())) { String bodyText = this.bodyText.toString().intern(); for (Rule rule : rules) { try { if (debug) { log.trace(" Fire body() for " + rule); } rule.body(namespaceURI, name, bodyText); } catch (Exception e) { log.error(sm.getString("digester.error.body"), e); throw createSAXException(e); } catch (Error e) { log.error(sm.getString("digester.error.body"), e); throw e; } } } else { if (debug) { log.trace(sm.getString("digester.noRulesFound", match)); } if (rulesValidation) { log.warn(sm.getString("digester.noRulesFound", match)); } } // Recover the body text from the surrounding element bodyText = bodyTexts.pop(); // 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 = rules.get(j); if (debug) { log.trace(" Fire end() for " + rule); } rule.end(namespaceURI, name); } catch (Exception e) { log.error(sm.getString("digester.error.end"), e); throw createSAXException(e); } catch (Error e) { log.error(sm.getString("digester.error.end"), 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 */ @Override public void endPrefixMapping(String prefix) throws SAXException { if (saxLog.isTraceEnabled()) { saxLog.trace("endPrefixMapping(" + prefix + ")"); } // Deregister this prefix mapping ArrayStack<String> stack = namespaces.get(prefix); if (stack == null) { return; } try { stack.pop(); if (stack.empty()) { namespaces.remove(prefix); } } catch (EmptyStackException e) { throw createSAXException(sm.getString("digester.emptyStackError")); } } /** * 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 */ @Override public void ignorableWhitespace(char[] buffer, int start, int len) throws SAXException { if (saxLog.isTraceEnabled()) { saxLog.trace("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 */ @Override public void processingInstruction(String target, String data) throws SAXException { if (saxLog.isTraceEnabled()) { saxLog.trace("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 */ @Override public void setDocumentLocator(Locator locator) { if (saxLog.isTraceEnabled()) { saxLog.trace("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 */ @Override public void skippedEntity(String name) throws SAXException { if (saxLog.isTraceEnabled()) { saxLog.trace("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 */ @Override public void startDocument() throws SAXException { if (saxLog.isTraceEnabled()) { saxLog.trace("startDocument()"); } if (locator instanceof Locator2) { if (root instanceof DocumentProperties.Charset) { String enc = ((Locator2) locator).getEncoding(); if (enc != null) { try { ((DocumentProperties.Charset) root).setCharset(B2CConverter.getCharset(enc)); } catch (UnsupportedEncodingException e) { log.warn(sm.getString("digester.encodingInvalid", enc), e); } } } } // 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 */ @Override public void startElement(String namespaceURI, String localName, String qName, Attributes list) throws SAXException { boolean debug = log.isTraceEnabled(); if (saxLog.isTraceEnabled()) { saxLog.trace("startElement(" + namespaceURI + "," + localName + "," + qName + ")"); } // Parse system properties list = updateAttributes(list); // Save the body text accumulated for our surrounding element bodyTexts.push(bodyText); bodyText = new StringBuilder(); // 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.isEmpty())) { name = qName; } // Compute the current matching rule StringBuilder sb = new StringBuilder(match); if (!match.isEmpty()) { sb.append('/'); } sb.append(name); match = sb.toString(); if (debug) { log.trace(" New match='" + match + "'"); } // Fire "begin" events for all relevant rules List<Rule> rules = getRules().match(namespaceURI, match); matches.push(rules); if ((rules != null) && (!rules.isEmpty())) { for (Rule rule : rules) { try { if (debug) { log.trace(" Fire begin() for " + rule); } rule.begin(namespaceURI, name, list); } catch (ClassNotFoundException cnfe) { log.error(sm.getString("digester.error.begin"), cnfe); if (log.isDebugEnabled()) { log.debug(ToStringUtil.classPathForCNFE(getClassLoader())); } throw createSAXException(cnfe); } catch (Exception e) { log.error(sm.getString("digester.error.begin"), e); throw createSAXException(e); } catch (Error e) { log.error(sm.getString("digester.error.begin"), e); throw e; } } } else { if (debug) { log.trace(sm.getString("digester.noRulesFound", 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 */ @Override public void startPrefixMapping(String prefix, String namespaceURI) throws SAXException { if (saxLog.isTraceEnabled()) { saxLog.trace("startPrefixMapping(" + prefix + "," + namespaceURI + ")"); } // Register this prefix mapping ArrayStack<String> stack = 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) */ @Override public void notationDecl(String name, String publicId, String systemId) { if (saxLog.isTraceEnabled()) { saxLog.trace("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 */ @Override public void unparsedEntityDecl(String name, String publicId, String systemId, String notation) { if (saxLog.isTraceEnabled()) { saxLog.trace("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; } @Override public InputSource resolveEntity(String name, String publicId, String baseURI, String systemId) throws SAXException, IOException { if (saxLog.isTraceEnabled()) { saxLog.trace("resolveEntity('" + publicId + "', '" + systemId + "', '" + baseURI + "')"); } // Has this system identifier been registered? String entityURL = null; if (publicId != null) { entityURL = entityValidator.get(publicId); } if (entityURL == null) { if (systemId == null) { // cannot resolve if (log.isTraceEnabled()) { log.trace(" Cannot resolve entity: '" + publicId + "'"); } return null; } else { // try to resolve using system ID if (log.isTraceEnabled()) { log.trace(" Trying to resolve using system ID '" + systemId + "'"); } entityURL = systemId; // resolve systemId against baseURI if it is not absolute if (baseURI != null) { try { URI uri = new URI(systemId); if (!uri.isAbsolute()) { entityURL = new URI(baseURI).resolve(uri).toString(); } } catch (URISyntaxException e) { if (log.isDebugEnabled()) { log.debug(sm.getString("digester.invalidURI", baseURI, systemId), e); } } } } } // Return an input source to our alternative URL if (log.isTraceEnabled()) { log.trace(" Resolving to alternate DTD '" + entityURL + "'"); } try { return new InputSource(entityURL); } catch (Exception e) { throw createSAXException(e); } } // ----------------------------------------------- LexicalHandler Methods @Override public void startDTD(String name, String publicId, String systemId) throws SAXException { setPublicId(publicId); } // ------------------------------------------------- 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 */ @Override public void error(SAXParseException exception) throws SAXException { log.error(sm.getString("digester.parseError", Integer.valueOf(exception.getLineNumber()), Integer.valueOf(exception.getColumnNumber())), 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 */ @Override public void fatalError(SAXParseException exception) throws SAXException { log.error(sm.getString("digester.parseErrorFatal", Integer.valueOf(exception.getLineNumber()), Integer.valueOf(exception.getColumnNumber())), 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 */ @Override public void warning(SAXParseException exception) throws SAXException { log.error(sm.getString("digester.parseWarning", Integer.valueOf(exception.getLineNumber()), Integer.valueOf(exception.getColumnNumber()), exception)); if (errorHandler != null) { errorHandler.warning(exception); } } // ------------------------------------------------------- Public Methods /** * 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 * * @return the root object * * @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 * * @return the root object * * @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 * * @return the root object * * @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; } /** * <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.isTraceEnabled()) { log.trace("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); } /** * Register a set of Rule instances defined in a RuleSet. * * @param ruleSet The RuleSet instance to configure from */ public void addRuleSet(RuleSet ruleSet) { ruleSet.addRuleInstances(this); } /** * Add a "call method" rule for a method which accepts no arguments. * * @param pattern Element matching pattern * @param methodName Method name to be called * * @see CallMethodRule */ public void addCallMethod(String pattern, String methodName) { addRule(pattern, new CallMethodRule(methodName)); } /** * Add a "call method" rule for the specified parameters. * * @param pattern Element matching pattern * @param methodName Method name to be called * @param paramCount Number of expected parameters (or zero for a single parameter from the body of this element) * * @see CallMethodRule */ public void addCallMethod(String pattern, String methodName, int paramCount) { addRule(pattern, new CallMethodRule(methodName, paramCount)); } /** * Add a "call parameter" rule for the specified parameters. * * @param pattern Element matching pattern * @param paramIndex Zero-relative parameter index to set (from the body of this element) * * @see CallParamRule */ public void addCallParam(String pattern, int paramIndex) { addRule(pattern, new CallParamRule(paramIndex)); } /** * Add a "factory create" rule for the specified parameters. * * @param pattern Element matching pattern * @param creationFactory Previously instantiated ObjectCreationFactory to be utilized * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during object creation will be * ignored. * * @see FactoryCreateRule */ public void addFactoryCreate(String pattern, ObjectCreationFactory creationFactory, boolean ignoreCreateExceptions) { creationFactory.setDigester(this); addRule(pattern, new FactoryCreateRule(creationFactory, ignoreCreateExceptions)); } /** * Add an "object create" rule for the specified parameters. * * @param pattern Element matching pattern * @param className Java class name to be created * * @see ObjectCreateRule */ public void addObjectCreate(String pattern, String className) { addRule(pattern, new ObjectCreateRule(className)); } /** * Add an "object create" rule for the specified parameters. * * @param pattern Element matching pattern * @param className Default Java class name to be created * @param attributeName Attribute name that optionally overrides the default Java class name to be created * * @see ObjectCreateRule */ public void addObjectCreate(String pattern, String className, String attributeName) { addRule(pattern, new ObjectCreateRule(className, attributeName)); } /** * Add a "set next" rule for the specified parameters. * * @param pattern Element matching pattern * @param methodName Method name to call on the parent element * @param paramType Java class name of the expected parameter type (if you wish to use a primitive type, specify * the corresponding Java wrapper class instead, such as <code>java.lang.Boolean</code> for a * <code>boolean</code> parameter) * * @see SetNextRule */ public void addSetNext(String pattern, String methodName, String paramType) { addRule(pattern, new SetNextRule(methodName, paramType)); } /** * Add a "set properties" rule for the specified parameters. * * @param pattern Element matching pattern * * @see SetPropertiesRule */ public void addSetProperties(String pattern) { addRule(pattern, new SetPropertiesRule()); } public void addSetProperties(String pattern, String[] excludes) { addRule(pattern, new SetPropertiesRule(excludes)); } // --------------------------------------------------- Object Stack Methods /** * Clear the current contents of the object stack. * <p> * Calling this method <i>might</i> allow another document of the same type to be correctly parsed. However, this * method was not intended for this purpose. In general, a separate Digester object should be created for each * document to be parsed. */ public void clear() { match = ""; bodyTexts.clear(); params.clear(); publicId = null; stack.clear(); log = null; saxLog = null; configured = false; } public void reset() { root = null; setErrorHandler(null); clear(); } /** * Return the top object on the stack without removing it. If there are no objects on the stack, return * <code>null</code>. * * @return the top object */ public Object peek() { try { return stack.peek(); } catch (EmptyStackException e) { log.warn(sm.getString("digester.emptyStack")); return null; } } /** * Return the n'th object down the stack, where 0 is the top element and [getCount()-1] is the bottom element. If * the specified index is out of range, return <code>null</code>. * * @param n Index of the desired element, where 0 is the top of the stack, 1 is the next element down, and so on. * * @return the specified object */ public Object peek(int n) { try { return stack.peek(n); } catch (EmptyStackException e) { log.warn(sm.getString("digester.emptyStack")); return null; } } /** * Pop the top object off of the stack, and return it. If there are no objects on the stack, return * <code>null</code>. * * @return the top object */ public Object pop() { try { return stack.pop(); } catch (EmptyStackException e) { log.warn(sm.getString("digester.emptyStack")); return null; } } /** * Push a new object onto the top of the object stack. * * @param object The new object */ public void push(Object object) { if (stack.isEmpty()) { root = object; } stack.push(object); } /** * When the Digester is being used as a SAXContentHandler, this method allows you to access the root object that has * been created after parsing. * * @return the root object that has been created after parsing or null if the digester has not parsed any XML yet. */ public Object getRoot() { return root; } // ------------------------------------------------ Parameter Stack Methods // ------------------------------------------------------ Protected Methods /** * <p> * Provide a hook for lazy configuration of this <code>Digester</code> instance. The default implementation does * nothing, but subclasses can override as needed. * </p> * <p> * <strong>Note</strong> This method may be called more than once. * </p> */ protected void configure() { // Do not configure more than once if (configured) { return; } log = LogFactory.getLog("org.apache.tomcat.util.digester.Digester"); saxLog = LogFactory.getLog("org.apache.tomcat.util.digester.Digester.sax"); // Set the configuration flag to avoid repeating configured = true; } /** * <p> * Return the top object on the parameters stack without removing it. If there are no objects on the stack, return * <code>null</code>. * </p> * <p> * The parameters stack is used to store <code>CallMethodRule</code> parameters. See {@link #params}. * </p> * * @return the top object on the parameters stack */ public Object peekParams() { try { return params.peek(); } catch (EmptyStackException e) { log.warn(sm.getString("digester.emptyStack")); return null; } } /** * <p> * Pop the top object off of the parameters stack, and return it. If there are no objects on the stack, return * <code>null</code>. * </p> * <p> * The parameters stack is used to store <code>CallMethodRule</code> parameters. See {@link #params}. * </p> * * @return the top object on the parameters stack */ public Object popParams() { try { if (log.isTraceEnabled()) { log.trace("Popping params"); } return params.pop(); } catch (EmptyStackException e) { log.warn(sm.getString("digester.emptyStack")); return null; } } /** * <p> * Push a new object onto the top of the parameters stack. * </p> * <p> * The parameters stack is used to store <code>CallMethodRule</code> parameters. See {@link #params}. * </p> * * @param object The new object */ public void pushParams(Object object) { if (log.isTraceEnabled()) { log.trace("Pushing params"); } params.push(object); } /** * Create a SAX exception which also understands about the location in the digester file where the exception occurs * * @param message The error message * @param e The root cause * * @return the new exception */ public SAXException createSAXException(String message, Exception e) { if ((e instanceof InvocationTargetException)) { Throwable t = e.getCause(); if (t instanceof VirtualMachineError) { throw (VirtualMachineError) t; } if (t instanceof Exception) { e = (Exception) t; } } if (locator != null) { String error = sm.getString("digester.errorLocation", Integer.valueOf(locator.getLineNumber()), Integer.valueOf(locator.getColumnNumber()), message); if (e != null) { return new SAXParseException(error, locator, e); } else { return new SAXParseException(error, locator); } } log.error(sm.getString("digester.noLocator")); if (e != null) { return new SAXException(message, e); } else { return new SAXException(message); } } /** * Create a SAX exception which also understands about the location in the digester file where the exception occurs * * @param e The root cause * * @return the new exception */ public SAXException createSAXException(Exception e) { if (e instanceof InvocationTargetException) { Throwable t = e.getCause(); if (t instanceof VirtualMachineError) { throw (VirtualMachineError) t; } if (t instanceof Exception) { e = (Exception) t; } } return createSAXException(e.getMessage(), e); } /** * Create a SAX exception which also understands about the location in the digester file where the exception occurs * * @param message The error message * * @return the new exception */ public SAXException createSAXException(String message) { return createSAXException(message, null); } // ------------------------------------------------------- Private Methods /** * Returns an attributes list which contains all the attributes passed in, with any text of form "${xxx}" in an * attribute value replaced by the appropriate value from the system property. */ private Attributes updateAttributes(Attributes list) { if (list.getLength() == 0) { return list; } AttributesImpl newAttrs = new AttributesImpl(list); int nAttributes = newAttrs.getLength(); for (int i = 0; i < nAttributes; ++i) { String value = newAttrs.getValue(i); try { newAttrs.setValue(i, IntrospectionUtils.replaceProperties(value, null, source, getClassLoader()).intern()); } catch (Exception e) { log.warn(sm.getString("digester.failedToUpdateAttributes", newAttrs.getLocalName(i), value), e); } } return newAttrs; } /** * Return a new StringBuilder containing the same contents as the input buffer, except that data of form ${varname} * have been replaced by the value of that var as defined in the system property. */ private StringBuilder updateBodyText(StringBuilder bodyText) { String in = bodyText.toString(); String out; try { out = IntrospectionUtils.replaceProperties(in, null, source, getClassLoader()); } catch (Exception e) { return bodyText; // return unchanged data } if (out == in) { // No substitutions required. Don't waste memory creating // a new buffer return bodyText; } else { return new StringBuilder(out); } } private static class EntityResolverWrapper implements EntityResolver { private final EntityResolver entityResolver; private final PropertySource[] source; private final ClassLoader classLoader; EntityResolverWrapper(EntityResolver entityResolver, PropertySource[] source, ClassLoader classLoader) { this.entityResolver = entityResolver; this.source = source; this.classLoader = classLoader; } @Override public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { publicId = replace(publicId); systemId = replace(systemId); return entityResolver.resolveEntity(publicId, systemId); } protected String replace(String input) { try { return IntrospectionUtils.replaceProperties(input, null, source, classLoader); } catch (Exception e) { return input; } } } private static class EntityResolver2Wrapper extends EntityResolverWrapper implements EntityResolver2 { private final EntityResolver2 entityResolver2; EntityResolver2Wrapper(EntityResolver2 entityResolver, PropertySource[] source, ClassLoader classLoader) { super(entityResolver, source, classLoader); this.entityResolver2 = entityResolver; } @Override public InputSource getExternalSubset(String name, String baseURI) throws SAXException, IOException { name = replace(name); baseURI = replace(baseURI); return entityResolver2.getExternalSubset(name, baseURI); } @Override public InputSource resolveEntity(String name, String publicId, String baseURI, String systemId) throws SAXException, IOException { name = replace(name); publicId = replace(publicId); baseURI = replace(baseURI); systemId = replace(systemId); return entityResolver2.resolveEntity(name, publicId, baseURI, systemId); } } }
Detected license expression
apache-2.0
Detected license expression (SPDX)
Apache-2.0
Percentage of license text
1.85
Copyrights

      
    
Holders

      
    
Authors

      
    
License detections License expression License expression SPDX
apache_2_0-4bde3f57-78aa-4201-96bf-531cba09e7de apache-2.0 Apache-2.0
URL Start line End line
http://www.apache.org/licenses/LICENSE-2.0 9 9
http://xml.org/sax/features/namespace-prefixes 541 541
http://xml.org/sax/features/validation 547 547
http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description 559 559
http://xml.org/sax/properties/lexical-handler 871 871