0001 /* $Id: Digester.java,v 1.2 2004/06/26 21:37:52 remm Exp $ 0002 * 0003 * Copyright 2001-2004 The Apache Software Foundation. 0004 * 0005 * Licensed under the Apache License, Version 2.0 (the "License"); 0006 * you may not use this file except in compliance with the License. 0007 * You may obtain a copy of the License at 0008 * 0009 * http://www.apache.org/licenses/LICENSE-2.0 0010 * 0011 * Unless required by applicable law or agreed to in writing, software 0012 * distributed under the License is distributed on an "AS IS" BASIS, 0013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 0014 * See the License for the specific language governing permissions and 0015 * limitations under the License. 0016 */ 0017 0018 package org.apache.tomcat.util.digester; 0019 0020 0021 import java.io.File; 0022 import java.io.FileInputStream; 0023 import java.io.IOException; 0024 import java.io.InputStream; 0025 import java.io.Reader; 0026 import java.lang.reflect.InvocationTargetException; 0027 import java.util.EmptyStackException; 0028 import java.util.HashMap; 0029 import java.util.Iterator; 0030 import java.util.List; 0031 import java.util.Map; 0032 import java.util.Properties; 0033 0034 import javax.xml.parsers.ParserConfigurationException; 0035 import javax.xml.parsers.SAXParser; 0036 import javax.xml.parsers.SAXParserFactory; 0037 0038 import org.apache.commons.logging.Log; 0039 import org.apache.commons.logging.LogFactory; 0040 import org.apache.tomcat.util.IntrospectionUtils; 0041 import org.xml.sax.Attributes; 0042 import org.xml.sax.EntityResolver; 0043 import org.xml.sax.ErrorHandler; 0044 import org.xml.sax.InputSource; 0045 import org.xml.sax.Locator; 0046 import org.xml.sax.SAXException; 0047 import org.xml.sax.SAXNotRecognizedException; 0048 import org.xml.sax.SAXNotSupportedException; 0049 import org.xml.sax.SAXParseException; 0050 import org.xml.sax.XMLReader; 0051 import org.xml.sax.helpers.AttributesImpl; 0052 import org.xml.sax.helpers.DefaultHandler; 0053 0054 0055 0056 0057 /** 0058 * <p>A <strong>Digester</strong> processes an XML input stream by matching a 0059 * series of element nesting patterns to execute Rules that have been added 0060 * prior to the start of parsing. This package was inspired by the 0061 * <code>XmlMapper</code> class that was part of Tomcat 3.0 and 3.1, 0062 * but is organized somewhat differently.</p> 0063 * 0064 * <p>See the <a href="package-summary.html#package_description">Digester 0065 * Developer Guide</a> for more information.</p> 0066 * 0067 * <p><strong>IMPLEMENTATION NOTE</strong> - A single Digester instance may 0068 * only be used within the context of a single thread at a time, and a call 0069 * to <code>parse()</code> must be completed before another can be initiated 0070 * even from the same thread.</p> 0071 * 0072 * <p><strong>IMPLEMENTATION NOTE</strong> - A bug in Xerces 2.0.2 prevents 0073 * the support of XML schema. You need Xerces 2.1/2.3 and up to make 0074 * this class working with XML schema</p> 0075 */ 0076 0077 public class Digester extends DefaultHandler { 0078 0079 0080 // ---------------------------------------------------------- Static Fields 0081 0082 0083 private static class SystemPropertySource 0084 implements IntrospectionUtils.PropertySource { 0085 public String getProperty( String key ) { 0086 return System.getProperty(key); 0087 } 0088 } 0089 0090 protected static IntrospectionUtils.PropertySource source[] = 0091 new IntrospectionUtils.PropertySource[] { new SystemPropertySource() }; 0092 0093 0094 // --------------------------------------------------------- Constructors 0095 0096 0097 /** 0098 * Construct a new Digester with default properties. 0099 */ 0100 public Digester() { 0101 0102 super(); 0103 0104 } 0105 0106 0107 /** 0108 * Construct a new Digester, allowing a SAXParser to be passed in. This 0109 * allows Digester to be used in environments which are unfriendly to 0110 * JAXP1.1 (such as WebLogic 6.0). Thanks for the request to change go to 0111 * James House (james@interobjective.com). This may help in places where 0112 * you are able to load JAXP 1.1 classes yourself. 0113 */ 0114 public Digester(SAXParser parser) { 0115 0116 super(); 0117 0118 this.parser = parser; 0119 0120 } 0121 0122 0123 /** 0124 * Construct a new Digester, allowing an XMLReader to be passed in. This 0125 * allows Digester to be used in environments which are unfriendly to 0126 * JAXP1.1 (such as WebLogic 6.0). Note that if you use this option you 0127 * have to configure namespace and validation support yourself, as these 0128 * properties only affect the SAXParser and emtpy constructor. 0129 */ 0130 public Digester(XMLReader reader) { 0131 0132 super(); 0133 0134 this.reader = reader; 0135 0136 } 0137 0138 0139 // --------------------------------------------------- Instance Variables 0140 0141 0142 /** 0143 * The body text of the current element. 0144 */ 0145 protected StringBuffer bodyText = new StringBuffer(); 0146 0147 0148 /** 0149 * The stack of body text string buffers for surrounding elements. 0150 */ 0151 protected ArrayStack bodyTexts = new ArrayStack(); 0152 0153 0154 /** 0155 * Stack whose elements are List objects, each containing a list of 0156 * Rule objects as returned from Rules.getMatch(). As each xml element 0157 * in the input is entered, the matching rules are pushed onto this 0158 * stack. After the end tag is reached, the matches are popped again. 0159 * The depth of is stack is therefore exactly the same as the current 0160 * "nesting" level of the input xml. 0161 * 0162 * @since 1.6 0163 */ 0164 protected ArrayStack matches = new ArrayStack(10); 0165 0166 /** 0167 * The class loader to use for instantiating application objects. 0168 * If not specified, the context class loader, or the class loader 0169 * used to load Digester itself, is used, based on the value of the 0170 * <code>useContextClassLoader</code> variable. 0171 */ 0172 protected ClassLoader classLoader = null; 0173 0174 0175 /** 0176 * Has this Digester been configured yet. 0177 */ 0178 protected boolean configured = false; 0179 0180 0181 /** 0182 * The EntityResolver used by the SAX parser. By default it use this class 0183 */ 0184 protected EntityResolver entityResolver; 0185 0186 /** 0187 * The URLs of entityValidator that have been registered, keyed by the public 0188 * identifier that corresponds. 0189 */ 0190 protected HashMap entityValidator = new HashMap(); 0191 0192 0193 /** 0194 * The application-supplied error handler that is notified when parsing 0195 * warnings, errors, or fatal errors occur. 0196 */ 0197 protected ErrorHandler errorHandler = null; 0198 0199 0200 /** 0201 * The SAXParserFactory that is created the first time we need it. 0202 */ 0203 protected SAXParserFactory factory = null; 0204 0205 /** 0206 * @deprecated This is now managed by {@link ParserFeatureSetterFactory} 0207 */ 0208 protected String JAXP_SCHEMA_LANGUAGE = 0209 "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; 0210 0211 0212 /** 0213 * The Locator associated with our parser. 0214 */ 0215 protected Locator locator = null; 0216 0217 0218 /** 0219 * The current match pattern for nested element processing. 0220 */ 0221 protected String match = ""; 0222 0223 0224 /** 0225 * Do we want a "namespace aware" parser. 0226 */ 0227 protected boolean namespaceAware = false; 0228 0229 0230 /** 0231 * Registered namespaces we are currently processing. The key is the 0232 * namespace prefix that was declared in the document. The value is an 0233 * ArrayStack of the namespace URIs this prefix has been mapped to -- 0234 * the top Stack element is the most current one. (This architecture 0235 * is required because documents can declare nested uses of the same 0236 * prefix for different Namespace URIs). 0237 */ 0238 protected HashMap namespaces = new HashMap(); 0239 0240 0241 /** 0242 * The parameters stack being utilized by CallMethodRule and 0243 * CallParamRule rules. 0244 */ 0245 protected ArrayStack params = new ArrayStack(); 0246 0247 0248 /** 0249 * The SAXParser we will use to parse the input stream. 0250 */ 0251 protected SAXParser parser = null; 0252 0253 0254 /** 0255 * The public identifier of the DTD we are currently parsing under 0256 * (if any). 0257 */ 0258 protected String publicId = null; 0259 0260 0261 /** 0262 * The XMLReader used to parse digester rules. 0263 */ 0264 protected XMLReader reader = null; 0265 0266 0267 /** 0268 * The "root" element of the stack (in other words, the last object 0269 * that was popped. 0270 */ 0271 protected Object root = null; 0272 0273 0274 /** 0275 * The <code>Rules</code> implementation containing our collection of 0276 * <code>Rule</code> instances and associated matching policy. If not 0277 * established before the first rule is added, a default implementation 0278 * will be provided. 0279 */ 0280 protected Rules rules = null; 0281 0282 /** 0283 * The XML schema language to use for validating an XML instance. By 0284 * default this value is set to <code>W3C_XML_SCHEMA</code> 0285 */ 0286 protected String schemaLanguage = W3C_XML_SCHEMA; 0287 0288 0289 /** 0290 * The XML schema to use for validating an XML instance. 0291 */ 0292 protected String schemaLocation = null; 0293 0294 0295 /** 0296 * The object stack being constructed. 0297 */ 0298 protected ArrayStack stack = new ArrayStack(); 0299 0300 0301 /** 0302 * Do we want to use the Context ClassLoader when loading classes 0303 * for instantiating new objects. Default is <code>false</code>. 0304 */ 0305 protected boolean useContextClassLoader = false; 0306 0307 0308 /** 0309 * Do we want to use a validating parser. 0310 */ 0311 protected boolean validating = false; 0312 0313 0314 /** 0315 * The Log to which most logging calls will be made. 0316 */ 0317 protected Log log = 0318 LogFactory.getLog("org.apache.commons.digester.Digester"); 0319 0320 0321 /** 0322 * The Log to which all SAX event related logging calls will be made. 0323 */ 0324 protected Log saxLog = 0325 LogFactory.getLog("org.apache.commons.digester.Digester.sax"); 0326 0327 0328 /** 0329 * The schema language supported. By default, we use this one. 0330 */ 0331 protected static final String W3C_XML_SCHEMA = 0332 "http://www.w3.org/2001/XMLSchema"; 0333 0334 /** Stacks used for interrule communication, indexed by name String */ 0335 private HashMap stacksByName = new HashMap(); 0336 0337 // ------------------------------------------------------------- Properties 0338 0339 /** 0340 * Return the currently mapped namespace URI for the specified prefix, 0341 * if any; otherwise return <code>null</code>. These mappings come and 0342 * go dynamically as the document is parsed. 0343 * 0344 * @param prefix Prefix to look up 0345 */ 0346 public String findNamespaceURI(String prefix) { 0347 0348 ArrayStack stack = (ArrayStack) namespaces.get(prefix); 0349 if (stack == null) { 0350 return (null); 0351 } 0352 try { 0353 return ((String) stack.peek()); 0354 } catch (EmptyStackException e) { 0355 return (null); 0356 } 0357 0358 } 0359 0360 0361 /** 0362 * Return the class loader to be used for instantiating application objects 0363 * when required. This is determined based upon the following rules: 0364 * <ul> 0365 * <li>The class loader set by <code>setClassLoader()</code>, if any</li> 0366 * <li>The thread context class loader, if it exists and the 0367 * <code>useContextClassLoader</code> property is set to true</li> 0368 * <li>The class loader used to load the Digester class itself. 0369 * </ul> 0370 */ 0371 public ClassLoader getClassLoader() { 0372 0373 if (this.classLoader != null) { 0374 return (this.classLoader); 0375 } 0376 if (this.useContextClassLoader) { 0377 ClassLoader classLoader = 0378 Thread.currentThread().getContextClassLoader(); 0379 if (classLoader != null) { 0380 return (classLoader); 0381 } 0382 } 0383 return (this.getClass().getClassLoader()); 0384 0385 } 0386 0387 0388 /** 0389 * Set the class loader to be used for instantiating application objects 0390 * when required. 0391 * 0392 * @param classLoader The new class loader to use, or <code>null</code> 0393 * to revert to the standard rules 0394 */ 0395 public void setClassLoader(ClassLoader classLoader) { 0396 0397 this.classLoader = classLoader; 0398 0399 } 0400 0401 0402 /** 0403 * Return the current depth of the element stack. 0404 */ 0405 public int getCount() { 0406 0407 return (stack.size()); 0408 0409 } 0410 0411 0412 /** 0413 * Return the name of the XML element that is currently being processed. 0414 */ 0415 public String getCurrentElementName() { 0416 0417 String elementName = match; 0418 int lastSlash = elementName.lastIndexOf('/'); 0419 if (lastSlash >= 0) { 0420 elementName = elementName.substring(lastSlash + 1); 0421 } 0422 return (elementName); 0423 0424 } 0425 0426 0427 /** 0428 * Return the debugging detail level of our currently enabled logger. 0429 * 0430 * @deprecated This method now always returns 0. Digester uses the apache 0431 * jakarta commons-logging library; see the documentation for that library 0432 * for more information. 0433 */ 0434 public int getDebug() { 0435 0436 return (0); 0437 0438 } 0439 0440 0441 /** 0442 * Set the debugging detail level of our currently enabled logger. 0443 * 0444 * @param debug New debugging detail level (0=off, increasing integers 0445 * for more detail) 0446 * 0447 * @deprecated This method now has no effect at all. Digester uses 0448 * the apache jakarta comons-logging library; see the documentation 0449 * for that library for more information. 0450 */ 0451 public void setDebug(int debug) { 0452 0453 ; // No action is taken 0454 0455 } 0456 0457 0458 /** 0459 * Return the error handler for this Digester. 0460 */ 0461 public ErrorHandler getErrorHandler() { 0462 0463 return (this.errorHandler); 0464 0465 } 0466 0467 0468 /** 0469 * Set the error handler for this Digester. 0470 * 0471 * @param errorHandler The new error handler 0472 */ 0473 public void setErrorHandler(ErrorHandler errorHandler) { 0474 0475 this.errorHandler = errorHandler; 0476 0477 } 0478 0479 0480 /** 0481 * Return the SAXParserFactory we will use, creating one if necessary. 0482 */ 0483 public SAXParserFactory getFactory() { 0484 0485 if (factory == null) { 0486 factory = SAXParserFactory.newInstance(); 0487 factory.setNamespaceAware(namespaceAware); 0488 factory.setValidating(validating); 0489 } 0490 return (factory); 0491 0492 } 0493 0494 0495 /** 0496 * Returns a flag indicating whether the requested feature is supported 0497 * by the underlying implementation of <code>org.xml.sax.XMLReader</code>. 0498 * See <a href="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description" 0499 * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a> 0500 * for information about the standard SAX2 feature flags. 0501 * 0502 * @param feature Name of the feature to inquire about 0503 * 0504 * @exception ParserConfigurationException if a parser configuration error 0505 * occurs 0506 * @exception SAXNotRecognizedException if the property name is 0507 * not recognized 0508 * @exception SAXNotSupportedException if the property name is 0509 * recognized but not supported 0510 */ 0511 public boolean getFeature(String feature) 0512 throws ParserConfigurationException, SAXNotRecognizedException, 0513 SAXNotSupportedException { 0514 0515 return (getFactory().getFeature(feature)); 0516 0517 } 0518 0519 0520 /** 0521 * Sets a flag indicating whether the requested feature is supported 0522 * by the underlying implementation of <code>org.xml.sax.XMLReader</code>. 0523 * See <a href="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description" 0524 * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a> 0525 * for information about the standard SAX2 feature flags. In order to be 0526 * effective, this method must be called <strong>before</strong> the 0527 * <code>getParser()</code> method is called for the first time, either 0528 * directly or indirectly. 0529 * 0530 * @param feature Name of the feature to set the status for 0531 * @param value The new value for this feature 0532 * 0533 * @exception ParserConfigurationException if a parser configuration error 0534 * occurs 0535 * @exception SAXNotRecognizedException if the property name is 0536 * not recognized 0537 * @exception SAXNotSupportedException if the property name is 0538 * recognized but not supported 0539 */ 0540 public void setFeature(String feature, boolean value) 0541 throws ParserConfigurationException, SAXNotRecognizedException, 0542 SAXNotSupportedException { 0543 0544 getFactory().setFeature(feature, value); 0545 0546 } 0547 0548 0549 /** 0550 * Return the current Logger associated with this instance of the Digester 0551 */ 0552 public Log getLogger() { 0553 0554 return log; 0555 0556 } 0557 0558 0559 /** 0560 * Set the current logger for this Digester. 0561 */ 0562 public void setLogger(Log log) { 0563 0564 this.log = log; 0565 0566 } 0567 0568 /** 0569 * Gets the logger used for logging SAX-related information. 0570 * <strong>Note</strong> the output is finely grained. 0571 * 0572 * @since 1.6 0573 */ 0574 public Log getSAXLogger() { 0575 0576 return saxLog; 0577 } 0578 0579 0580 /** 0581 * Sets the logger used for logging SAX-related information. 0582 * <strong>Note</strong> the output is finely grained. 0583 * @param saxLog Log, not null 0584 * 0585 * @since 1.6 0586 */ 0587 public void setSAXLogger(Log saxLog) { 0588 0589 this.saxLog = saxLog; 0590 } 0591 0592 /** 0593 * Return the current rule match path 0594 */ 0595 public String getMatch() { 0596 0597 return match; 0598 0599 } 0600 0601 0602 /** 0603 * Return the "namespace aware" flag for parsers we create. 0604 */ 0605 public boolean getNamespaceAware() { 0606 0607 return (this.namespaceAware); 0608 0609 } 0610 0611 0612 /** 0613 * Set the "namespace aware" flag for parsers we create. 0614 * 0615 * @param namespaceAware The new "namespace aware" flag 0616 */ 0617 public void setNamespaceAware(boolean namespaceAware) { 0618 0619 this.namespaceAware = namespaceAware; 0620 0621 } 0622 0623 0624 /** 0625 * Set the publid id of the current file being parse. 0626 * @param publicId the DTD/Schema public's id. 0627 */ 0628 public void setPublicId(String publicId){ 0629 this.publicId = publicId; 0630 } 0631 0632 0633 /** 0634 * Return the public identifier of the DTD we are currently 0635 * parsing under, if any. 0636 */ 0637 public String getPublicId() { 0638 0639 return (this.publicId); 0640 0641 } 0642 0643 0644 /** 0645 * Return the namespace URI that will be applied to all subsequently 0646 * added <code>Rule</code> objects. 0647 */ 0648 public String getRuleNamespaceURI() { 0649 0650 return (getRules().getNamespaceURI()); 0651 0652 } 0653 0654 0655 /** 0656 * Set the namespace URI that will be applied to all subsequently 0657 * added <code>Rule</code> objects. 0658 * 0659 * @param ruleNamespaceURI Namespace URI that must match on all 0660 * subsequently added rules, or <code>null</code> for matching 0661 * regardless of the current namespace URI 0662 */ 0663 public void setRuleNamespaceURI(String ruleNamespaceURI) { 0664 0665 getRules().setNamespaceURI(ruleNamespaceURI); 0666 0667 } 0668 0669 0670 /** 0671 * Return the SAXParser we will use to parse the input stream. If there 0672 * is a problem creating the parser, return <code>null</code>. 0673 */ 0674 public SAXParser getParser() { 0675 0676 // Return the parser we already created (if any) 0677 if (parser != null) { 0678 return (parser); 0679 } 0680 0681 // Create a new parser 0682 try { 0683 if (validating) { 0684 Properties properties = new Properties(); 0685 properties.put("SAXParserFactory", getFactory()); 0686 if (schemaLocation != null) { 0687 properties.put("schemaLocation", schemaLocation); 0688 properties.put("schemaLanguage", schemaLanguage); 0689 } 0690 parser = ParserFeatureSetterFactory.newSAXParser(properties); } else { 0691 parser = getFactory().newSAXParser(); 0692 } 0693 } catch (Exception e) { 0694 log.error("Digester.getParser: ", e); 0695 return (null); 0696 } 0697 0698 return (parser); 0699 0700 } 0701 0702 0703 /** 0704 * Return the current value of the specified property for the underlying 0705 * <code>XMLReader</code> implementation. 0706 * See <a href="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description" 0707 * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a> 0708 * for information about the standard SAX2 properties. 0709 * 0710 * @param property Property name to be retrieved 0711 * 0712 * @exception SAXNotRecognizedException if the property name is 0713 * not recognized 0714 * @exception SAXNotSupportedException if the property name is 0715 * recognized but not supported 0716 */ 0717 public Object getProperty(String property) 0718 throws SAXNotRecognizedException, SAXNotSupportedException { 0719 0720 return (getParser().getProperty(property)); 0721 0722 } 0723 0724 0725 /** 0726 * Set the current value of the specified property for the underlying 0727 * <code>XMLReader</code> implementation. 0728 * See <a href="http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description" 0729 * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description</a> 0730 * for information about the standard SAX2 properties. 0731 * 0732 * @param property Property name to be set 0733 * @param value Property value to be set 0734 * 0735 * @exception SAXNotRecognizedException if the property name is 0736 * not recognized 0737 * @exception SAXNotSupportedException if the property name is 0738 * recognized but not supported 0739 */ 0740 public void setProperty(String property, Object value) 0741 throws SAXNotRecognizedException, SAXNotSupportedException { 0742 0743 getParser().setProperty(property, value); 0744 0745 } 0746 0747 0748 /** 0749 * By setting the reader in the constructor, you can bypass JAXP and 0750 * be able to use digester in Weblogic 6.0. 0751 * 0752 * @deprecated Use getXMLReader() instead, which can throw a 0753 * SAXException if the reader cannot be instantiated 0754 */ 0755 public XMLReader getReader() { 0756 0757 try { 0758 return (getXMLReader()); 0759 } catch (SAXException e) { 0760 log.error("Cannot get XMLReader", e); 0761 return (null); 0762 } 0763 0764 } 0765 0766 0767 /** 0768 * Return the <code>Rules</code> implementation object containing our 0769 * rules collection and associated matching policy. If none has been 0770 * established, a default implementation will be created and returned. 0771 */ 0772 public Rules getRules() { 0773 0774 if (this.rules == null) { 0775 this.rules = new RulesBase(); 0776 this.rules.setDigester(this); 0777 } 0778 return (this.rules); 0779 0780 } 0781 0782 0783 /** 0784 * Set the <code>Rules</code> implementation object containing our 0785 * rules collection and associated matching policy. 0786 * 0787 * @param rules New Rules implementation 0788 */ 0789 public void setRules(Rules rules) { 0790 0791 this.rules = rules; 0792 this.rules.setDigester(this); 0793 0794 } 0795 0796 0797 /** 0798 * Return the XML Schema URI used for validating an XML instance. 0799 */ 0800 public String getSchema() { 0801 0802 return (this.schemaLocation); 0803 0804 } 0805 0806 0807 /** 0808 * Set the XML Schema URI used for validating a XML Instance. 0809 * 0810 * @param schemaLocation a URI to the schema. 0811 */ 0812 public void setSchema(String schemaLocation){ 0813 0814 this.schemaLocation = schemaLocation; 0815 0816 } 0817 0818 0819 /** 0820 * Return the XML Schema language used when parsing. 0821 */ 0822 public String getSchemaLanguage() { 0823 0824 return (this.schemaLanguage); 0825 0826 } 0827 0828 0829 /** 0830 * Set the XML Schema language used when parsing. By default, we use W3C. 0831 * 0832 * @param schemaLanguage a URI to the schema language. 0833 */ 0834 public void setSchemaLanguage(String schemaLanguage){ 0835 0836 this.schemaLanguage = schemaLanguage; 0837 0838 } 0839 0840 0841 /** 0842 * Return the boolean as to whether the context classloader should be used. 0843 */ 0844 public boolean getUseContextClassLoader() { 0845 0846 return useContextClassLoader; 0847 0848 } 0849 0850 0851 /** 0852 * Determine whether to use the Context ClassLoader (the one found by 0853 * calling <code>Thread.currentThread().getContextClassLoader()</code>) 0854 * to resolve/load classes that are defined in various rules. If not 0855 * using Context ClassLoader, then the class-loading defaults to 0856 * using the calling-class' ClassLoader. 0857 * 0858 * @param use determines whether to use Context ClassLoader. 0859 */ 0860 public void setUseContextClassLoader(boolean use) { 0861 0862 useContextClassLoader = use; 0863 0864 } 0865 0866 0867 /** 0868 * Return the validating parser flag. 0869 */ 0870 public boolean getValidating() { 0871 0872 return (this.validating); 0873 0874 } 0875 0876 0877 /** 0878 * Set the validating parser flag. This must be called before 0879 * <code>parse()</code> is called the first time. 0880 * 0881 * @param validating The new validating parser flag. 0882 */ 0883 public void setValidating(boolean validating) { 0884 0885 this.validating = validating; 0886 0887 } 0888 0889 0890 /** 0891 * Return the XMLReader to be used for parsing the input document. 0892 * 0893 * FIX ME: there is a bug in JAXP/XERCES that prevent the use of a 0894 * parser that contains a schema with a DTD. 0895 * @exception SAXException if no XMLReader can be instantiated 0896 */ 0897 public XMLReader getXMLReader() throws SAXException { 0898 if (reader == null){ 0899 reader = getParser().getXMLReader(); 0900 } 0901 0902 reader.setDTDHandler(this); 0903 reader.setContentHandler(this); 0904 0905 if (entityResolver == null){ 0906 reader.setEntityResolver(this); 0907 } else { 0908 reader.setEntityResolver(entityResolver); 0909 } 0910 0911 reader.setErrorHandler(this); 0912 return reader; 0913 } 0914 0915 // ------------------------------------------------- ContentHandler Methods 0916 0917 0918 /** 0919 * Process notification of character data received from the body of 0920 * an XML element. 0921 * 0922 * @param buffer The characters from the XML document 0923 * @param start Starting offset into the buffer 0924 * @param length Number of characters from the buffer 0925 * 0926 * @exception SAXException if a parsing error is to be reported 0927 */ 0928 public void characters(char buffer[], int start, int length) 0929 throws SAXException { 0930 0931 if (saxLog.isDebugEnabled()) { 0932 saxLog.debug("characters(" + new String(buffer, start, length) + ")"); 0933 } 0934 0935 bodyText.append(buffer, start, length); 0936 0937 } 0938 0939 0940 /** 0941 * Process notification of the end of the document being reached. 0942 * 0943 * @exception SAXException if a parsing error is to be reported 0944 */ 0945 public void endDocument() throws SAXException { 0946 0947 if (saxLog.isDebugEnabled()) { 0948 if (getCount() > 1) { 0949 saxLog.debug("endDocument(): " + getCount() + 0950 " elements left"); 0951 } else { 0952 saxLog.debug("endDocument()"); 0953 } 0954 } 0955 0956 while (getCount() > 1) { 0957 pop(); 0958 } 0959 0960 // Fire "finish" events for all defined rules 0961 Iterator rules = getRules().rules().iterator(); 0962 while (rules.hasNext()) { 0963 Rule rule = (Rule) rules.next(); 0964 try { 0965 rule.finish(); 0966 } catch (Exception e) { 0967 log.error("Finish event threw exception", e); 0968 throw createSAXException(e); 0969 } catch (Error e) { 0970 log.error("Finish event threw error", e); 0971 throw e; 0972 } 0973 } 0974 0975 // Perform final cleanup 0976 clear(); 0977 0978 } 0979 0980 0981 /** 0982 * Process notification of the end of an XML element being reached. 0983 * 0984 * @param namespaceURI - The Namespace URI, or the empty string if the 0985 * element has no Namespace URI or if Namespace processing is not 0986 * being performed. 0987 * @param localName - The local name (without prefix), or the empty 0988 * string if Namespace processing is not being performed. 0989 * @param qName - The qualified XML 1.0 name (with prefix), or the 0990 * empty string if qualified names are not available. 0991 * @exception SAXException if a parsing error is to be reported 0992 */ 0993 public void endElement(String namespaceURI, String localName, 0994 String qName) throws SAXException { 0995 0996 boolean debug = log.isDebugEnabled(); 0997 0998 if (debug) { 0999 if (saxLog.isDebugEnabled()) { 1000 saxLog.debug("endElement(" + namespaceURI + "," + localName + 1001 "," + qName + ")"); 1002 } 1003 log.debug(" match='" + match + "'"); 1004 log.debug(" bodyText='" + bodyText + "'"); 1005 } 1006 1007 // Parse system properties 1008 bodyText = updateBodyText(bodyText); 1009 1010 // the actual element name is either in localName or qName, depending 1011 // on whether the parser is namespace aware 1012 String name = localName; 1013 if ((name == null) || (name.length() < 1)) { 1014 name = qName; 1015 } 1016 1017 // Fire "body" events for all relevant rules 1018 List rules = (List) matches.pop(); 1019 if ((rules != null) && (rules.size() > 0)) { 1020 String bodyText = this.bodyText.toString(); 1021 for (int i = 0; i < rules.size(); i++) { 1022 try { 1023 Rule rule = (Rule) rules.get(i); 1024 if (debug) { 1025 log.debug(" Fire body() for " + rule); 1026 } 1027 rule.body(namespaceURI, name, bodyText); 1028 } catch (Exception e) { 1029 log.error("Body event threw exception", e); 1030 throw createSAXException(e); 1031 } catch (Error e) { 1032 log.error("Body event threw error", e); 1033 throw e; 1034 } 1035 } 1036 } else { 1037 if (debug) { 1038 log.debug(" No rules found matching '" + match + "'."); 1039 } 1040 } 1041 1042 // Recover the body text from the surrounding element 1043 bodyText = (StringBuffer) bodyTexts.pop(); 1044 if (debug) { 1045 log.debug(" Popping body text '" + bodyText.toString() + "'"); 1046 } 1047 1048 // Fire "end" events for all relevant rules in reverse order 1049 if (rules != null) { 1050 for (int i = 0; i < rules.size(); i++) { 1051 int j = (rules.size() - i) - 1; 1052 try { 1053 Rule rule = (Rule) rules.get(j); 1054 if (debug) { 1055 log.debug(" Fire end() for " + rule); 1056 } 1057 rule.end(namespaceURI, name); 1058 } catch (Exception e) { 1059 log.error("End event threw exception", e); 1060 throw createSAXException(e); 1061 } catch (Error e) { 1062 log.error("End event threw error", e); 1063 throw e; 1064 } 1065 } 1066 } 1067 1068 // Recover the previous match expression 1069 int slash = match.lastIndexOf('/'); 1070 if (slash >= 0) { 1071 match = match.substring(0, slash); 1072 } else { 1073 match = ""; 1074 } 1075 1076 } 1077 1078 1079 /** 1080 * Process notification that a namespace prefix is going out of scope. 1081 * 1082 * @param prefix Prefix that is going out of scope 1083 * 1084 * @exception SAXException if a parsing error is to be reported 1085 */ 1086 public void endPrefixMapping(String prefix) throws SAXException { 1087 1088 if (saxLog.isDebugEnabled()) { 1089 saxLog.debug("endPrefixMapping(" + prefix + ")"); 1090 } 1091 1092 // Deregister this prefix mapping 1093 ArrayStack stack = (ArrayStack) namespaces.get(prefix); 1094 if (stack == null) { 1095 return; 1096 } 1097 try { 1098 stack.pop(); 1099 if (stack.empty()) 1100 namespaces.remove(prefix); 1101 } catch (EmptyStackException e) { 1102 throw createSAXException("endPrefixMapping popped too many times"); 1103 } 1104 1105 } 1106 1107 1108 /** 1109 * Process notification of ignorable whitespace received from the body of 1110 * an XML element. 1111 * 1112 * @param buffer The characters from the XML document 1113 * @param start Starting offset into the buffer 1114 * @param len Number of characters from the buffer 1115 * 1116 * @exception SAXException if a parsing error is to be reported 1117 */ 1118 public void ignorableWhitespace(char buffer[], int start, int len) 1119 throws SAXException { 1120 1121 if (saxLog.isDebugEnabled()) { 1122 saxLog.debug("ignorableWhitespace(" + 1123 new String(buffer, start, len) + ")"); 1124 } 1125 1126 ; // No processing required 1127 1128 } 1129 1130 1131 /** 1132 * Process notification of a processing instruction that was encountered. 1133 * 1134 * @param target The processing instruction target 1135 * @param data The processing instruction data (if any) 1136 * 1137 * @exception SAXException if a parsing error is to be reported 1138 */ 1139 public void processingInstruction(String target, String data) 1140 throws SAXException { 1141 1142 if (saxLog.isDebugEnabled()) { 1143 saxLog.debug("processingInstruction('" + target + "','" + data + "')"); 1144 } 1145 1146 ; // No processing is required 1147 1148 } 1149 1150 1151 /** 1152 * Gets the document locator associated with our parser. 1153 * 1154 * @return the Locator supplied by the document parser 1155 */ 1156 public Locator getDocumentLocator() { 1157 1158 return locator; 1159 1160 } 1161 1162 /** 1163 * Sets the document locator associated with our parser. 1164 * 1165 * @param locator The new locator 1166 */ 1167 public void setDocumentLocator(Locator locator) { 1168 1169 if (saxLog.isDebugEnabled()) { 1170 saxLog.debug("setDocumentLocator(" + locator + ")"); 1171 } 1172 1173 this.locator = locator; 1174 1175 } 1176 1177 1178 /** 1179 * Process notification of a skipped entity. 1180 * 1181 * @param name Name of the skipped entity 1182 * 1183 * @exception SAXException if a parsing error is to be reported 1184 */ 1185 public void skippedEntity(String name) throws SAXException { 1186 1187 if (saxLog.isDebugEnabled()) { 1188 saxLog.debug("skippedEntity(" + name + ")"); 1189 } 1190 1191 ; // No processing required 1192 1193 } 1194 1195 1196 /** 1197 * Process notification of the beginning of the document being reached. 1198 * 1199 * @exception SAXException if a parsing error is to be reported 1200 */ 1201 public void startDocument() throws SAXException { 1202 1203 if (saxLog.isDebugEnabled()) { 1204 saxLog.debug("startDocument()"); 1205 } 1206 1207 // ensure that the digester is properly configured, as 1208 // the digester could be used as a SAX ContentHandler 1209 // rather than via the parse() methods. 1210 configure(); 1211 } 1212 1213 1214 /** 1215 * Process notification of the start of an XML element being reached. 1216 * 1217 * @param namespaceURI The Namespace URI, or the empty string if the element 1218 * has no Namespace URI or if Namespace processing is not being performed. 1219 * @param localName The local name (without prefix), or the empty 1220 * string if Namespace processing is not being performed. 1221 * @param qName The qualified name (with prefix), or the empty 1222 * string if qualified names are not available.\ 1223 * @param list The attributes attached to the element. If there are 1224 * no attributes, it shall be an empty Attributes object. 1225 * @exception SAXException if a parsing error is to be reported 1226 */ 1227 public void startElement(String namespaceURI, String localName, 1228 String qName, Attributes list) 1229 throws SAXException { 1230 boolean debug = log.isDebugEnabled(); 1231 1232 if (saxLog.isDebugEnabled()) { 1233 saxLog.debug("startElement(" + namespaceURI + "," + localName + "," + 1234 qName + ")"); 1235 } 1236 1237 // Parse system properties 1238 list = updateAttributes(list); 1239 1240 // Save the body text accumulated for our surrounding element 1241 bodyTexts.push(bodyText); 1242 if (debug) { 1243 log.debug(" Pushing body text '" + bodyText.toString() + "'"); 1244 } 1245 bodyText = new StringBuffer(); 1246 1247 // the actual element name is either in localName or qName, depending 1248 // on whether the parser is namespace aware 1249 String name = localName; 1250 if ((name == null) || (name.length() < 1)) { 1251 name = qName; 1252 } 1253 1254 // Compute the current matching rule 1255 StringBuffer sb = new StringBuffer(match); 1256 if (match.length() > 0) { 1257 sb.append('/'); 1258 } 1259 sb.append(name); 1260 match = sb.toString(); 1261 if (debug) { 1262 log.debug(" New match='" + match + "'"); 1263 } 1264 1265 // Fire "begin" events for all relevant rules 1266 List rules = getRules().match(namespaceURI, match); 1267 matches.push(rules); 1268 if ((rules != null) && (rules.size() > 0)) { 1269 for (int i = 0; i < rules.size(); i++) { 1270 try { 1271 Rule rule = (Rule) rules.get(i); 1272 if (debug) { 1273 log.debug(" Fire begin() for " + rule); 1274 } 1275 rule.begin(namespaceURI, name, list); 1276 } catch (Exception e) { 1277 log.error("Begin event threw exception", e); 1278 throw createSAXException(e); 1279 } catch (Error e) { 1280 log.error("Begin event threw error", e); 1281 throw e; 1282 } 1283 } 1284 } else { 1285 if (debug) { 1286 log.debug(" No rules found matching '" + match + "'."); 1287 } 1288 } 1289 1290 } 1291 1292 1293 /** 1294 * Process notification that a namespace prefix is coming in to scope. 1295 * 1296 * @param prefix Prefix that is being declared 1297 * @param namespaceURI Corresponding namespace URI being mapped to 1298 * 1299 * @exception SAXException if a parsing error is to be reported 1300 */ 1301 public void startPrefixMapping(String prefix, String namespaceURI) 1302 throws SAXException { 1303 1304 if (saxLog.isDebugEnabled()) { 1305 saxLog.debug("startPrefixMapping(" + prefix + "," + namespaceURI + ")"); 1306 } 1307 1308 // Register this prefix mapping 1309 ArrayStack stack = (ArrayStack) namespaces.get(prefix); 1310 if (stack == null) { 1311 stack = new ArrayStack(); 1312 namespaces.put(prefix, stack); 1313 } 1314 stack.push(namespaceURI); 1315 1316 } 1317 1318 1319 // ----------------------------------------------------- DTDHandler Methods 1320 1321 1322 /** 1323 * Receive notification of a notation declaration event. 1324 * 1325 * @param name The notation name 1326 * @param publicId The public identifier (if any) 1327 * @param systemId The system identifier (if any) 1328 */ 1329 public void notationDecl(String name, String publicId, String systemId) { 1330 1331 if (saxLog.isDebugEnabled()) { 1332 saxLog.debug("notationDecl(" + name + "," + publicId + "," + 1333 systemId + ")"); 1334 } 1335 1336 } 1337 1338 1339 /** 1340 * Receive notification of an unparsed entity declaration event. 1341 * 1342 * @param name The unparsed entity name 1343 * @param publicId The public identifier (if any) 1344 * @param systemId The system identifier (if any) 1345 * @param notation The name of the associated notation 1346 */ 1347 public void unparsedEntityDecl(String name, String publicId, 1348 String systemId, String notation) { 1349 1350 if (saxLog.isDebugEnabled()) { 1351 saxLog.debug("unparsedEntityDecl(" + name + "," + publicId + "," + 1352 systemId + "," + notation + ")"); 1353 } 1354 1355 } 1356 1357 1358 // ----------------------------------------------- EntityResolver Methods 1359 1360 /** 1361 * Set the <code>EntityResolver</code> used by SAX when resolving 1362 * public id and system id. 1363 * This must be called before the first call to <code>parse()</code>. 1364 * @param entityResolver a class that implement the <code>EntityResolver</code> interface. 1365 */ 1366 public void setEntityResolver(EntityResolver entityResolver){ 1367 this.entityResolver = entityResolver; 1368 } 1369 1370 1371 /** 1372 * Return the Entity Resolver used by the SAX parser. 1373 * @return Return the Entity Resolver used by the SAX parser. 1374 */ 1375 public EntityResolver getEntityResolver(){ 1376 return entityResolver; 1377 } 1378 1379 /** 1380 * Resolve the requested external entity. 1381 * 1382 * @param publicId The public identifier of the entity being referenced 1383 * @param systemId The system identifier of the entity being referenced 1384 * 1385 * @exception SAXException if a parsing exception occurs 1386 * 1387 */ 1388 public InputSource resolveEntity(String publicId, String systemId) 1389 throws SAXException { 1390 1391 if (saxLog.isDebugEnabled()) { 1392 saxLog.debug("resolveEntity('" + publicId + "', '" + systemId + "')"); 1393 } 1394 1395 if (publicId != null) 1396 this.publicId = publicId; 1397 1398 // Has this system identifier been registered? 1399 String entityURL = null; 1400 if (publicId != null) { 1401 entityURL = (String) entityValidator.get(publicId); 1402 } 1403 1404 // Redirect the schema location to a local destination 1405 if (schemaLocation != null && entityURL == null && systemId != null){ 1406 entityURL = (String)entityValidator.get(systemId); 1407 } 1408 1409 if (entityURL == null) { 1410 if (systemId == null) { 1411 // cannot resolve 1412 if (log.isDebugEnabled()) { 1413 log.debug(" Cannot resolve entity: '" + entityURL + "'"); 1414 } 1415 return (null); 1416 1417 } else { 1418 // try to resolve using system ID 1419 if (log.isDebugEnabled()) { 1420 log.debug(" Trying to resolve using system ID '" + systemId + "'"); 1421 } 1422 entityURL = systemId; 1423 } 1424 } 1425 1426 // Return an input source to our alternative URL 1427 if (log.isDebugEnabled()) { 1428 log.debug(" Resolving to alternate DTD '" + entityURL + "'"); 1429 } 1430 1431 try { 1432 return (new InputSource(entityURL)); 1433 } catch (Exception e) { 1434 throw createSAXException(e); 1435 } 1436 } 1437 1438 1439 // ------------------------------------------------- ErrorHandler Methods 1440 1441 1442 /** 1443 * Forward notification of a parsing error to the application supplied 1444 * error handler (if any). 1445 * 1446 * @param exception The error information 1447 * 1448 * @exception SAXException if a parsing exception occurs 1449 */ 1450 public void error(SAXParseException exception) throws SAXException { 1451 1452 log.error("Parse Error at line " + exception.getLineNumber() + 1453 " column " + exception.getColumnNumber() + ": " + 1454 exception.getMessage(), exception); 1455 if (errorHandler != null) { 1456 errorHandler.error(exception); 1457 } 1458 1459 } 1460 1461 1462 /** 1463 * Forward notification of a fatal parsing error to the application 1464 * supplied error handler (if any). 1465 * 1466 * @param exception The fatal error information 1467 * 1468 * @exception SAXException if a parsing exception occurs 1469 */ 1470 public void fatalError(SAXParseException exception) throws SAXException { 1471 1472 log.error("Parse Fatal Error at line " + exception.getLineNumber() + 1473 " column " + exception.getColumnNumber() + ": " + 1474 exception.getMessage(), exception); 1475 if (errorHandler != null) { 1476 errorHandler.fatalError(exception); 1477 } 1478 1479 } 1480 1481 1482 /** 1483 * Forward notification of a parse warning to the application supplied 1484 * error handler (if any). 1485 * 1486 * @param exception The warning information 1487 * 1488 * @exception SAXException if a parsing exception occurs 1489 */ 1490 public void warning(SAXParseException exception) throws SAXException { 1491 if (errorHandler != null) { 1492 log.warn("Parse Warning Error at line " + exception.getLineNumber() + 1493 " column " + exception.getColumnNumber() + ": " + 1494 exception.getMessage(), exception); 1495 1496 errorHandler.warning(exception); 1497 } 1498 1499 } 1500 1501 1502 // ------------------------------------------------------- Public Methods 1503 1504 1505 /** 1506 * Log a message to our associated logger. 1507 * 1508 * @param message The message to be logged 1509 * @deprecated Call getLogger() and use it's logging methods 1510 */ 1511 public void log(String message) { 1512 1513 log.info(message); 1514 1515 } 1516 1517 1518 /** 1519 * Log a message and exception to our associated logger. 1520 * 1521 * @param message The message to be logged 1522 * @deprecated Call getLogger() and use it's logging methods 1523 */ 1524 public void log(String message, Throwable exception) { 1525 1526 log.error(message, exception); 1527 1528 } 1529 1530 1531 /** 1532 * Parse the content of the specified file using this Digester. Returns 1533 * the root element from the object stack (if any). 1534 * 1535 * @param file File containing the XML data to be parsed 1536 * 1537 * @exception IOException if an input/output error occurs 1538 * @exception SAXException if a parsing exception occurs 1539 */ 1540 public Object parse(File file) throws IOException, SAXException { 1541 1542 configure(); 1543 InputSource input = new InputSource(new FileInputStream(file)); 1544 input.setSystemId("file://" + file.getAbsolutePath()); Rate1545 getXMLReader().parse(input); 1546 return (root); 1547 1548 } 1549 /** 1550 * Parse the content of the specified input source using this Digester. 1551 * Returns the root element from the object stack (if any). 1552 * 1553 * @param input Input source containing the XML data to be parsed 1554 * 1555 * @exception IOException if an input/output error occurs 1556 * @exception SAXException if a parsing exception occurs 1557 */ 1558 public Object parse(InputSource input) throws IOException, SAXException { 1559 1560 configure(); Rate1561 getXMLReader().parse(input); 1562 return (root); 1563 1564 } 1565 1566 1567 /** 1568 * Parse the content of the specified input stream using this Digester. 1569 * Returns the root element from the object stack (if any). 1570 * 1571 * @param input Input stream containing the XML data to be parsed 1572 * 1573 * @exception IOException if an input/output error occurs 1574 * @exception SAXException if a parsing exception occurs 1575 */ 1576 public Object parse(InputStream input) throws IOException, SAXException { 1577 1578 configure(); 1579 InputSource is = new InputSource(input); Rate1580 getXMLReader().parse(is); 1581 return (root); 1582 1583 } 1584 1585 1586 /** 1587 * Parse the content of the specified reader using this Digester. 1588 * Returns the root element from the object stack (if any). 1589 * 1590 * @param reader Reader containing the XML data to be parsed 1591 * 1592 * @exception IOException if an input/output error occurs 1593 * @exception SAXException if a parsing exception occurs 1594 */ 1595 public Object parse(Reader reader) throws IOException, SAXException { 1596 1597 configure(); 1598 InputSource is = new InputSource(reader); Rate1599 getXMLReader().parse(is); 1600 return (root); 1601 1602 } 1603 1604 1605 /** 1606 * Parse the content of the specified URI using this Digester. 1607 * Returns the root element from the object stack (if any). 1608 * 1609 * @param uri URI containing the XML data to be parsed 1610 * 1611 * @exception IOException if an input/output error occurs 1612 * @exception SAXException if a parsing exception occurs 1613 */ 1614 public Object parse(String uri) throws IOException, SAXException { 1615 1616 configure(); 1617 InputSource is = new InputSource(uri); Rate1618 getXMLReader().parse(is); 1619 return (root); 1620 1621 } 1622 1623 1624 /** 1625 * <p>Register the specified DTD URL for the specified public identifier. 1626 * This must be called before the first call to <code>parse()</code>. 1627 * </p><p> 1628 * <code>Digester</code> contains an internal <code>EntityResolver</code> 1629 * implementation. This maps <code>PUBLICID</code>'s to URLs 1630 * (from which the resource will be loaded). A common use case for this 1631 * method is to register local URLs (possibly computed at runtime by a 1632 * classloader) for DTDs. This allows the performance advantage of using 1633 * a local version without having to ensure every <code>SYSTEM</code> 1634 * URI on every processed xml document is local. This implementation provides 1635 * only basic functionality. If more sophisticated features are required, 1636 * using {@link #setEntityResolver} to set a custom resolver is recommended. 1637 * </p><p> 1638 * <strong>Note:</strong> This method will have no effect when a custom 1639 * <code>EntityResolver</code> has been set. (Setting a custom 1640 * <code>EntityResolver</code> overrides the internal implementation.) 1641 * </p> 1642 * @param publicId Public identifier of the DTD to be resolved 1643 * @param entityURL The URL to use for reading this DTD 1644 */ 1645 public void register(String publicId, String entityURL) { 1646 1647 if (log.isDebugEnabled()) { 1648 log.debug("register('" + publicId + "', '" + entityURL + "'"); 1649 } 1650 entityValidator.put(publicId, entityURL); 1651 1652 } 1653 1654 1655 // --------------------------------------------------------- Rule Methods 1656 1657 1658 /** 1659 * <p>Register a new Rule matching the specified pattern. 1660 * This method sets the <code>Digester</code> property on the rule.</p> 1661 * 1662 * @param pattern Element matching pattern 1663 * @param rule Rule to be registered 1664 */ 1665 public void addRule(String pattern, Rule rule) { 1666 1667 rule.setDigester(this); 1668 getRules().add(pattern, rule); 1669 1670 } 1671 1672 1673 /** 1674 * Register a set of Rule instances defined in a RuleSet. 1675 * 1676 * @param ruleSet The RuleSet instance to configure from 1677 */ 1678 public void addRuleSet(RuleSet ruleSet) { 1679 1680 String oldNamespaceURI = getRuleNamespaceURI(); 1681 String newNamespaceURI = ruleSet.getNamespaceURI(); 1682 if (log.isDebugEnabled()) { 1683 if (newNamespaceURI == null) { 1684 log.debug("addRuleSet() with no namespace URI"); 1685 } else { 1686 log.debug("addRuleSet() with namespace URI " + newNamespaceURI); 1687 } 1688 } 1689 setRuleNamespaceURI(newNamespaceURI); 1690 ruleSet.addRuleInstances(this); 1691 setRuleNamespaceURI(oldNamespaceURI); 1692 1693 } 1694 1695 1696 /** 1697 * Add a "bean property setter" rule for the specified parameters. 1698 * 1699 * @param pattern Element matching pattern 1700 * @see BeanPropertySetterRule 1701 */ 1702 public void addBeanPropertySetter(String pattern) { 1703 1704 addRule(pattern, 1705 new BeanPropertySetterRule()); 1706 1707 } 1708 1709 1710 /** 1711 * Add a "bean property setter" rule for the specified parameters. 1712 * 1713 * @param pattern Element matching pattern 1714 * @param propertyName Name of property to set 1715 * @see BeanPropertySetterRule 1716 */ 1717 public void addBeanPropertySetter(String pattern, 1718 String propertyName) { 1719 1720 addRule(pattern, 1721 new BeanPropertySetterRule(propertyName)); 1722 1723 } 1724 1725 /** 1726 * Add an "call method" rule for a method which accepts no arguments. 1727 * 1728 * @param pattern Element matching pattern 1729 * @param methodName Method name to be called 1730 * @see CallMethodRule 1731 */ 1732 public void addCallMethod(String pattern, String methodName) { 1733 1734 addRule( 1735 pattern, 1736 new CallMethodRule(methodName)); 1737 1738 } 1739 1740 /** 1741 * Add an "call method" rule for the specified parameters. 1742 * 1743 * @param pattern Element matching pattern 1744 * @param methodName Method name to be called 1745 * @param paramCount Number of expected parameters (or zero 1746 * for a single parameter from the body of this element) 1747 * @see CallMethodRule 1748 */ 1749 public void addCallMethod(String pattern, String methodName, 1750 int paramCount) { 1751 1752 addRule(pattern, 1753 new CallMethodRule(methodName, paramCount)); 1754 1755 } 1756 1757 1758 /** 1759 * Add an "call method" rule for the specified parameters. 1760 * If <code>paramCount</code> is set to zero the rule will use 1761 * the body of the matched element as the single argument of the 1762 * method, unless <code>paramTypes</code> is null or empty, in this 1763 * case the rule will call the specified method with no arguments. 1764 * 1765 * @param pattern Element matching pattern 1766 * @param methodName Method name to be called 1767 * @param paramCount Number of expected parameters (or zero 1768 * for a single parameter from the body of this element) 1769 * @param paramTypes Set of Java class names for the types 1770 * of the expected parameters 1771 * (if you wish to use a primitive type, specify the corresonding 1772 * Java wrapper class instead, such as <code>java.lang.Boolean</code> 1773 * for a <code>boolean</code> parameter) 1774 * @see CallMethodRule 1775 */ 1776 public void addCallMethod(String pattern, String methodName, 1777 int paramCount, String paramTypes[]) { 1778 1779 addRule(pattern, 1780 new CallMethodRule( 1781 methodName, 1782 paramCount, 1783 paramTypes)); 1784 1785 } 1786 1787 1788 /** 1789 * Add an "call method" rule for the specified parameters. 1790 * If <code>paramCount</code> is set to zero the rule will use 1791 * the body of the matched element as the single argument of the 1792 * method, unless <code>paramTypes</code> is null or empty, in this 1793 * case the rule will call the specified method with no arguments. 1794 * 1795 * @param pattern Element matching pattern 1796 * @param methodName Method name to be called 1797 * @param paramCount Number of expected parameters (or zero 1798 * for a single parameter from the body of this element) 1799 * @param paramTypes The Java class names of the arguments 1800 * (if you wish to use a primitive type, specify the corresonding 1801 * Java wrapper class instead, such as <code>java.lang.Boolean</code> 1802 * for a <code>boolean</code> parameter) 1803 * @see CallMethodRule 1804 */ 1805 public void addCallMethod(String pattern, String methodName, 1806 int paramCount, Class paramTypes[]) { 1807 1808 addRule(pattern, 1809 new CallMethodRule( 1810 methodName, 1811 paramCount, 1812 paramTypes)); 1813 1814 } 1815 1816 1817 /** 1818 * Add a "call parameter" rule for the specified parameters. 1819 * 1820 * @param pattern Element matching pattern 1821 * @param paramIndex Zero-relative parameter index to set 1822 * (from the body of this element) 1823 * @see CallParamRule 1824 */ 1825 public void addCallParam(String pattern, int paramIndex) { 1826 1827 addRule(pattern, 1828 new CallParamRule(paramIndex)); 1829 1830 } 1831 1832 1833 /** 1834 * Add a "call parameter" rule for the specified parameters. 1835 * 1836 * @param pattern Element matching pattern 1837 * @param paramIndex Zero-relative parameter index to set 1838 * (from the specified attribute) 1839 * @param attributeName Attribute whose value is used as the 1840 * parameter value 1841 * @see CallParamRule 1842 */ 1843 public void addCallParam(String pattern, int paramIndex, 1844 String attributeName) { 1845 1846 addRule(pattern, 1847 new CallParamRule(paramIndex, attributeName)); 1848 1849 } 1850 1851 1852 /** 1853 * Add a "call parameter" rule. 1854 * This will either take a parameter from the stack 1855 * or from the current element body text. 1856 * 1857 * @param paramIndex The zero-relative parameter number 1858 * @param fromStack Should the call parameter be taken from the top of the stack? 1859 * @see CallParamRule 1860 */ 1861 public void addCallParam(String pattern, int paramIndex, boolean fromStack) { 1862 1863 addRule(pattern, 1864 new CallParamRule(paramIndex, fromStack)); 1865 1866 } 1867 1868 /** 1869 * Add a "call parameter" rule that sets a parameter from the stack. 1870 * This takes a parameter from the given position on the stack. 1871 * 1872 * @param paramIndex The zero-relative parameter number 1873 * @param stackIndex set the call parameter to the stackIndex'th object down the stack, 1874 * where 0 is the top of the stack, 1 the next element down and so on 1875 * @see CallMethodRule 1876 */ 1877 public void addCallParam(String pattern, int paramIndex, int stackIndex) { 1878 1879 addRule(pattern, 1880 new CallParamRule(paramIndex, stackIndex)); 1881 1882 } 1883 1884 /** 1885 * Add a "call parameter" rule that sets a parameter from the current 1886 * <code>Digester</code> matching path. 1887 * This is sometimes useful when using rules that support wildcards. 1888 * 1889 * @param pattern the pattern that this rule should match 1890 * @param paramIndex The zero-relative parameter number 1891 * @see CallMethodRule 1892 */ 1893 public void addCallParamPath(String pattern,int paramIndex) { 1894 addRule(pattern, new PathCallParamRule(paramIndex)); 1895 } 1896 1897 /** 1898 * Add a "call parameter" rule that sets a parameter from a 1899 * caller-provided object. This can be used to pass constants such as 1900 * strings to methods; it can also be used to pass mutable objects, 1901 * providing ways for objects to do things like "register" themselves 1902 * with some shared object. 1903 * <p> 1904 * Note that when attempting to locate a matching method to invoke, 1905 * the true type of the paramObj is used, so that despite the paramObj 1906 * being passed in here as type Object, the target method can declare 1907 * its parameters as being the true type of the object (or some ancestor 1908 * type, according to the usual type-conversion rules). 1909 * 1910 * @param paramIndex The zero-relative parameter number 1911 * @param paramObj Any arbitrary object to be passed to the target 1912 * method. 1913 * @see CallMethodRule 1914 * 1915 * @since 1.6 1916 */ 1917 public void addObjectParam(String pattern, int paramIndex, 1918 Object paramObj) { 1919 1920 addRule(pattern, 1921 new ObjectParamRule(paramIndex, paramObj)); 1922 1923 } 1924 1925 /** 1926 * Add a "factory create" rule for the specified parameters. 1927 * Exceptions thrown during the object creation process will be propagated. 1928 * 1929 * @param pattern Element matching pattern 1930 * @param className Java class name of the object creation factory class 1931 * @see FactoryCreateRule 1932 */ 1933 public void addFactoryCreate(String pattern, String className) { 1934 1935 addFactoryCreate(pattern, className, false); 1936 1937 } 1938 1939 1940 /** 1941 * Add a "factory create" rule for the specified parameters. 1942 * Exceptions thrown during the object creation process will be propagated. 1943 * 1944 * @param pattern Element matching pattern 1945 * @param clazz Java class of the object creation factory class 1946 * @see FactoryCreateRule 1947 */ 1948 public void addFactoryCreate(String pattern, Class clazz) { 1949 1950 addFactoryCreate(pattern, clazz, false); 1951 1952 } 1953 1954 1955 /** 1956 * Add a "factory create" rule for the specified parameters. 1957 * Exceptions thrown during the object creation process will be propagated. 1958 * 1959 * @param pattern Element matching pattern 1960 * @param className Java class name of the object creation factory class 1961 * @param attributeName Attribute name which, if present, overrides the 1962 * value specified by <code>className</code> 1963 * @see FactoryCreateRule 1964 */ 1965 public void addFactoryCreate(String pattern, String className, 1966 String attributeName) { 1967 1968 addFactoryCreate(pattern, className, attributeName, false); 1969 1970 } 1971 1972 1973 /** 1974 * Add a "factory create" rule for the specified parameters. 1975 * Exceptions thrown during the object creation process will be propagated. 1976 * 1977 * @param pattern Element matching pattern 1978 * @param clazz Java class of the object creation factory class 1979 * @param attributeName Attribute name which, if present, overrides the 1980 * value specified by <code>className</code> 1981 * @see FactoryCreateRule 1982 */ 1983 public void addFactoryCreate(String pattern, Class clazz, 1984 String attributeName) { 1985 1986 addFactoryCreate(pattern, clazz, attributeName, false); 1987 1988 } 1989 1990 1991 /** 1992 * Add a "factory create" rule for the specified parameters. 1993 * Exceptions thrown during the object creation process will be propagated. 1994 * 1995 * @param pattern Element matching pattern 1996 * @param creationFactory Previously instantiated ObjectCreationFactory 1997 * to be utilized 1998 * @see FactoryCreateRule 1999 */ 2000 public void addFactoryCreate(String pattern, 2001 ObjectCreationFactory creationFactory) { 2002 2003 addFactoryCreate(pattern, creationFactory, false); 2004 2005 } 2006 2007 /** 2008 * Add a "factory create" rule for the specified parameters. 2009 * 2010 * @param pattern Element matching pattern 2011 * @param className Java class name of the object creation factory class 2012 * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during 2013 * object creation will be ignored. 2014 * @see FactoryCreateRule 2015 */ 2016 public void addFactoryCreate( 2017 String pattern, 2018 String className, 2019 boolean ignoreCreateExceptions) { 2020 2021 addRule( 2022 pattern, 2023 new FactoryCreateRule(className, ignoreCreateExceptions)); 2024 2025 } 2026 2027 2028 /** 2029 * Add a "factory create" rule for the specified parameters. 2030 * 2031 * @param pattern Element matching pattern 2032 * @param clazz Java class of the object creation factory class 2033 * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during 2034 * object creation will be ignored. 2035 * @see FactoryCreateRule 2036 */ 2037 public void addFactoryCreate( 2038 String pattern, 2039 Class clazz, 2040 boolean ignoreCreateExceptions) { 2041 2042 addRule( 2043 pattern, 2044 new FactoryCreateRule(clazz, ignoreCreateExceptions)); 2045 2046 } 2047 2048 2049 /** 2050 * Add a "factory create" rule for the specified parameters. 2051 * 2052 * @param pattern Element matching pattern 2053 * @param className Java class name of the object creation factory class 2054 * @param attributeName Attribute name which, if present, overrides the 2055 * value specified by <code>className</code> 2056 * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during 2057 * object creation will be ignored. 2058 * @see FactoryCreateRule 2059 */ 2060 public void addFactoryCreate( 2061 String pattern, 2062 String className, 2063 String attributeName, 2064 boolean ignoreCreateExceptions) { 2065 2066 addRule( 2067 pattern, 2068 new FactoryCreateRule(className, attributeName, ignoreCreateExceptions)); 2069 2070 } 2071 2072 2073 /** 2074 * Add a "factory create" rule for the specified parameters. 2075 * 2076 * @param pattern Element matching pattern 2077 * @param clazz Java class of the object creation factory class 2078 * @param attributeName Attribute name which, if present, overrides the 2079 * value specified by <code>className</code> 2080 * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during 2081 * object creation will be ignored. 2082 * @see FactoryCreateRule 2083 */ 2084 public void addFactoryCreate( 2085 String pattern, 2086 Class clazz, 2087 String attributeName, 2088 boolean ignoreCreateExceptions) { 2089 2090 addRule( 2091 pattern, 2092 new FactoryCreateRule(clazz, attributeName, ignoreCreateExceptions)); 2093 2094 } 2095 2096 2097 /** 2098 * Add a "factory create" rule for the specified parameters. 2099 * 2100 * @param pattern Element matching pattern 2101 * @param creationFactory Previously instantiated ObjectCreationFactory 2102 * to be utilized 2103 * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during 2104 * object creation will be ignored. 2105 * @see FactoryCreateRule 2106 */ 2107 public void addFactoryCreate(String pattern, 2108 ObjectCreationFactory creationFactory, 2109 boolean ignoreCreateExceptions) { 2110 2111 creationFactory.setDigester(this); 2112 addRule(pattern, 2113 new FactoryCreateRule(creationFactory, ignoreCreateExceptions)); 2114 2115 } 2116 2117 /** 2118 * Add an "object create" rule for the specified parameters. 2119 * 2120 * @param pattern Element matching pattern 2121 * @param className Java class name to be created 2122 * @see ObjectCreateRule 2123 */ 2124 public void addObjectCreate(String pattern, String className) { 2125 2126 addRule(pattern, 2127 new ObjectCreateRule(className)); 2128 2129 } 2130 2131 2132 /** 2133 * Add an "object create" rule for the specified parameters. 2134 * 2135 * @param pattern Element matching pattern 2136 * @param clazz Java class to be created 2137 * @see ObjectCreateRule 2138 */ 2139 public void addObjectCreate(String pattern, Class clazz) { 2140 2141 addRule(pattern, 2142 new ObjectCreateRule(clazz)); 2143 2144 } 2145 2146 2147 /** 2148 * Add an "object create" rule for the specified parameters. 2149 * 2150 * @param pattern Element matching pattern 2151 * @param className Default Java class name to be created 2152 * @param attributeName Attribute name that optionally overrides 2153 * the default Java class name to be created 2154 * @see ObjectCreateRule 2155 */ 2156 public void addObjectCreate(String pattern, String className, 2157 String attributeName) { 2158 2159 addRule(pattern, 2160 new ObjectCreateRule(className, attributeName)); 2161 2162 } 2163 2164 2165 /** 2166 * Add an "object create" rule for the specified parameters. 2167 * 2168 * @param pattern Element matching pattern 2169 * @param attributeName Attribute name that optionally overrides 2170 * @param clazz Default Java class to be created 2171 * the default Java class name to be created 2172 * @see ObjectCreateRule 2173 */ 2174 public void addObjectCreate(String pattern, 2175 String attributeName, 2176 Class clazz) { 2177 2178 addRule(pattern, 2179 new ObjectCreateRule(attributeName, clazz)); 2180 2181 } 2182 2183 /** 2184 * Add a "set next" rule for the specified parameters. 2185 * 2186 * @param pattern Element matching pattern 2187 * @param methodName Method name to call on the parent element 2188 * @see SetNextRule 2189 */ 2190 public void addSetNext(String pattern, String methodName) { 2191 2192 addRule(pattern, 2193 new SetNextRule(methodName)); 2194 2195 } 2196 2197 2198 /** 2199 * Add a "set next" rule for the specified parameters. 2200 * 2201 * @param pattern Element matching pattern 2202 * @param methodName Method name to call on the parent element 2203 * @param paramType Java class name of the expected parameter type 2204 * (if you wish to use a primitive type, specify the corresonding 2205 * Java wrapper class instead, such as <code>java.lang.Boolean</code> 2206 * for a <code>boolean</code> parameter) 2207 * @see SetNextRule 2208 */ 2209 public void addSetNext(String pattern, String methodName, 2210 String paramType) { 2211 2212 addRule(pattern, 2213 new SetNextRule(methodName, paramType)); 2214 2215 } 2216 2217 2218 /** 2219 * Add {@link SetRootRule} with the specified parameters. 2220 * 2221 * @param pattern Element matching pattern 2222 * @param methodName Method name to call on the root object 2223 * @see SetRootRule 2224 */ 2225 public void addSetRoot(String pattern, String methodName) { 2226 2227 addRule(pattern, 2228 new SetRootRule(methodName)); 2229 2230 } 2231 2232 2233 /** 2234 * Add {@link SetRootRule} with the specified parameters. 2235 * 2236 * @param pattern Element matching pattern 2237 * @param methodName Method name to call on the root object 2238 * @param paramType Java class name of the expected parameter type 2239 * @see SetRootRule 2240 */ 2241 public void addSetRoot(String pattern, String methodName, 2242 String paramType) { 2243 2244 addRule(pattern, 2245 new SetRootRule(methodName, paramType)); 2246 2247 } 2248 2249 /** 2250 * Add a "set properties" rule for the specified parameters. 2251 * 2252 * @param pattern Element matching pattern 2253 * @see SetPropertiesRule 2254 */ 2255 public void addSetProperties(String pattern) { 2256 2257 addRule(pattern, 2258 new SetPropertiesRule()); 2259 2260 } 2261 2262 /** 2263 * Add a "set properties" rule with a single overridden parameter. 2264 * See {@link SetPropertiesRule#SetPropertiesRule(String attributeName, String propertyName)} 2265 * 2266 * @param pattern Element matching pattern 2267 * @param attributeName map this attribute 2268 * @param propertyName to this property 2269 * @see SetPropertiesRule 2270 */ 2271 public void addSetProperties( 2272 String pattern, 2273 String attributeName, 2274 String propertyName) { 2275 2276 addRule(pattern, 2277 new SetPropertiesRule(attributeName, propertyName)); 2278 2279 } 2280 2281 /** 2282 * Add a "set properties" rule with overridden parameters. 2283 * See {@link SetPropertiesRule#SetPropertiesRule(String [] attributeNames, String [] propertyNames)} 2284 * 2285 * @param pattern Element matching pattern 2286 * @param attributeNames names of attributes with custom mappings 2287 * @param propertyNames property names these attributes map to 2288 * @see SetPropertiesRule 2289 */ 2290 public void addSetProperties( 2291 String pattern, 2292 String [] attributeNames, 2293 String [] propertyNames) { 2294 2295 addRule(pattern, 2296 new SetPropertiesRule(attributeNames, propertyNames)); 2297 2298 } 2299 2300 2301 /** 2302 * Add a "set property" rule for the specified parameters. 2303 * 2304 * @param pattern Element matching pattern 2305 * @param name Attribute name containing the property name to be set 2306 * @param value Attribute name containing the property value to set 2307 * @see SetPropertyRule 2308 */ 2309 public void addSetProperty(String pattern, String name, String value) { 2310 2311 addRule(pattern, 2312 new SetPropertyRule(name, value)); 2313 2314 } 2315 2316 2317 /** 2318 * Add a "set top" rule for the specified parameters. 2319 * 2320 * @param pattern Element matching pattern 2321 * @param methodName Method name to call on the parent element 2322 * @see SetTopRule 2323 */ 2324 public void addSetTop(String pattern, String methodName) { 2325 2326 addRule(pattern, 2327 new SetTopRule(methodName)); 2328 2329 } 2330 2331 2332 /** 2333 * Add a "set top" rule for the specified parameters. 2334 * 2335 * @param pattern Element matching pattern 2336 * @param methodName Method name to call on the parent element 2337 * @param paramType Java class name of the expected parameter type 2338 * (if you wish to use a primitive type, specify the corresonding 2339 * Java wrapper class instead, such as <code>java.lang.Boolean</code> 2340 * for a <code>boolean</code> parameter) 2341 * @see SetTopRule 2342 */ 2343 public void addSetTop(String pattern, String methodName, 2344 String paramType) { 2345 2346 addRule(pattern, 2347 new SetTopRule(methodName, paramType)); 2348 2349 } 2350 2351 2352 // --------------------------------------------------- Object Stack Methods 2353 2354 2355 /** 2356 * Clear the current contents of the object stack. 2357 * <p> 2358 * Calling this method <i>might</i> allow another document of the same type 2359 * to be correctly parsed. However this method was not intended for this 2360 * purpose. In general, a separate Digester object should be created for 2361 * each document to be parsed. 2362 */ 2363 public void clear() { 2364 2365 match = ""; 2366 bodyTexts.clear(); 2367 params.clear(); 2368 publicId = null; 2369 stack.clear(); 2370 } 2371 2372 2373 /** 2374 * Return the top object on the stack without removing it. If there are 2375 * no objects on the stack, return <code>null</code>. 2376 */ 2377 public Object peek() { 2378 2379 try { 2380 return (stack.peek()); 2381 } catch (EmptyStackException e) { 2382 log.warn("Empty stack (returning null)"); 2383 return (null); 2384 } 2385 2386 } 2387 2388 2389 /** 2390 * Return the n'th object down the stack, where 0 is the top element 2391 * and [getCount()-1] is the bottom element. If the specified index 2392 * is out of range, return <code>null</code>. 2393 * 2394 * @param n Index of the desired element, where 0 is the top of the stack, 2395 * 1 is the next element down, and so on. 2396 */ 2397 public Object peek(int n) { 2398 2399 try { 2400 return (stack.peek(n)); 2401 } catch (EmptyStackException e) { 2402 log.warn("Empty stack (returning null)"); 2403 return (null); 2404 } 2405 2406 } 2407 2408 2409 /** 2410 * Pop the top object off of the stack, and return it. If there are 2411 * no objects on the stack, return <code>null</code>. 2412 */ 2413 public Object pop() { 2414 2415 try { 2416 return (stack.pop()); 2417 } catch (EmptyStackException e) { 2418 log.warn("Empty stack (returning null)"); 2419 return (null); 2420 } 2421 2422 } 2423 2424 2425 /** 2426 * Push a new object onto the top of the object stack. 2427 * 2428 * @param object The new object 2429 */ 2430 public void push(Object object) { 2431 2432 if (stack.size() == 0) { 2433 root = object; 2434 } 2435 stack.push(object); 2436 2437 } 2438 2439 /** 2440 * Pushes the given object onto the stack with the given name. 2441 * If no stack already exists with the given name then one will be created. 2442 * 2443 * @param stackName the name of the stack onto which the object should be pushed 2444 * @param value the Object to be pushed onto the named stack. 2445 * 2446 * @since 1.6 2447 */ 2448 public void push(String stackName, Object value) { 2449 ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName); 2450 if (namedStack == null) { 2451 namedStack = new ArrayStack(); 2452 stacksByName.put(stackName, namedStack); 2453 } 2454 namedStack.push(value); 2455 } 2456 2457 /** 2458 * <p>Pops (gets and removes) the top object from the stack with the given name.</p> 2459 * 2460 * <p><strong>Note:</strong> a stack is considered empty 2461 * if no objects have been pushed onto it yet.</p> 2462 * 2463 * @param stackName the name of the stack from which the top value is to be popped 2464 * @return the top <code>Object</code> on the stack or or null if the stack is either 2465 * empty or has not been created yet 2466 * @throws EmptyStackException if the named stack is empty 2467 * 2468 * @since 1.6 2469 */ 2470 public Object pop(String stackName) { 2471 Object result = null; 2472 ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName); 2473 if (namedStack == null) { 2474 if (log.isDebugEnabled()) { 2475 log.debug("Stack '" + stackName + "' is empty"); 2476 } 2477 throw new EmptyStackException(); 2478 2479 } else { 2480 2481 result = namedStack.pop(); 2482 } 2483 return result; 2484 } 2485 2486 /** 2487 * <p>Gets the top object from the stack with the given name. 2488 * This method does not remove the object from the stack. 2489 * </p> 2490 * <p><strong>Note:</strong> a stack is considered empty 2491 * if no objects have been pushed onto it yet.</p> 2492 * 2493 * @param stackName the name of the stack to be peeked 2494 * @return the top <code>Object</code> on the stack or null if the stack is either 2495 * empty or has not been created yet 2496 * @throws EmptyStackException if the named stack is empty 2497 * 2498 * @since 1.6 2499 */ 2500 public Object peek(String stackName) { 2501 Object result = null; 2502 ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName); 2503 if (namedStack == null ) { 2504 if (log.isDebugEnabled()) { 2505 log.debug("Stack '" + stackName + "' is empty"); 2506 } 2507 throw new EmptyStackException(); 2508 2509 } else { 2510 2511 result = namedStack.peek(); 2512 } 2513 return result; 2514 } 2515 2516 /** 2517 * <p>Is the stack with the given name empty?</p> 2518 * <p><strong>Note:</strong> a stack is considered empty 2519 * if no objects have been pushed onto it yet.</p> 2520 * @param stackName the name of the stack whose emptiness 2521 * should be evaluated 2522 * @return true if the given stack if empty 2523 * 2524 * @since 1.6 2525 */ 2526 public boolean isEmpty(String stackName) { 2527 boolean result = true; 2528 ArrayStack namedStack = (ArrayStack) stacksByName.get(stackName); 2529 if (namedStack != null ) { 2530 result = namedStack.isEmpty(); 2531 } 2532 return result; 2533 } 2534 2535 /** 2536 * When the Digester is being used as a SAXContentHandler, 2537 * this method allows you to access the root object that has been 2538 * created after parsing. 2539 * 2540 * @return the root object that has been created after parsing 2541 * or null if the digester has not parsed any XML yet. 2542 */ 2543 public Object getRoot() { 2544 return root; 2545 } 2546 2547 2548 // ------------------------------------------------ Parameter Stack Methods 2549 2550 2551 // ------------------------------------------------------ Protected Methods 2552 2553 2554 /** 2555 * <p> 2556 * Provide a hook for lazy configuration of this <code>Digester</code> 2557 * instance. The default implementation does nothing, but subclasses 2558 * can override as needed. 2559 * </p> 2560 * 2561 * <p> 2562 * <strong>Note</strong> This method may be called more than once. 2563 * Once only initialization code should be placed in {@link #initialize} 2564 * or the code should take responsibility by checking and setting the 2565 * {@link #configured} flag. 2566 * </p> 2567 */ 2568 protected void configure() { 2569 2570 // Do not configure more than once 2571 if (configured) { 2572 return; 2573 } 2574 2575 // Perform lazy configuration as needed 2576 initialize(); // call hook method for subclasses that want to be initialized once only 2577 // Nothing else required by default 2578 2579 // Set the configuration flag to avoid repeating 2580 configured = true; 2581 2582 } 2583 2584 /** 2585 * <p> 2586 * Provides a hook for lazy initialization of this <code>Digester</code> 2587 * instance. 2588 * The default implementation does nothing, but subclasses 2589 * can override as needed. 2590 * Digester (by default) only calls this method once. 2591 * </p> 2592 * 2593 * <p> 2594 * <strong>Note</strong> This method will be called by {@link #configure} 2595 * only when the {@link #configured} flag is false. 2596 * Subclasses that override <code>configure</code> or who set <code>configured</code> 2597 * may find that this method may be called more than once. 2598 * </p> 2599 * 2600 * @since 1.6 2601 */ 2602 protected void initialize() { 2603 2604 // Perform lazy initialization as needed 2605 ; // Nothing required by default 2606 2607 } 2608 2609 // -------------------------------------------------------- Package Methods 2610 2611 2612 /** 2613 * Return the set of DTD URL registrations, keyed by public identifier. 2614 */ 2615 Map getRegistrations() { 2616 2617 return (entityValidator); 2618 2619 } 2620 2621 2622 /** 2623 * Return the set of rules that apply to the specified match position. 2624 * The selected rules are those that match exactly, or those rules 2625 * that specify a suffix match and the tail of the rule matches the 2626 * current match position. Exact matches have precedence over 2627 * suffix matches, then (among suffix matches) the longest match 2628 * is preferred. 2629 * 2630 * @param match The current match position 2631 * 2632 * @deprecated Call <code>match()</code> on the <code>Rules</code> 2633 * implementation returned by <code>getRules()</code> 2634 */ 2635 List getRules(String match) { 2636 2637 return (getRules().match(match)); 2638 2639 } 2640 2641 2642 /** 2643 * <p>Return the top object on the parameters stack without removing it. If there are 2644 * no objects on the stack, return <code>null</code>.</p> 2645 * 2646 * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters. 2647 * See {@link #params}.</p> 2648 */ 2649 public Object peekParams() { 2650 2651 try { 2652 return (params.peek()); 2653 } catch (EmptyStackException e) { 2654 log.warn("Empty stack (returning null)"); 2655 return (null); 2656 } 2657 2658 } 2659 2660 2661 /** 2662 * <p>Return the n'th object down the parameters stack, where 0 is the top element 2663 * and [getCount()-1] is the bottom element. If the specified index 2664 * is out of range, return <code>null</code>.</p> 2665 * 2666 * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters. 2667 * See {@link #params}.</p> 2668 * 2669 * @param n Index of the desired element, where 0 is the top of the stack, 2670 * 1 is the next element down, and so on. 2671 */ 2672 public Object peekParams(int n) { 2673 2674 try { 2675 return (params.peek(n)); 2676 } catch (EmptyStackException e) { 2677 log.warn("Empty stack (returning null)"); 2678 return (null); 2679 } 2680 2681 } 2682 2683 2684 /** 2685 * <p>Pop the top object off of the parameters stack, and return it. If there are 2686 * no objects on the stack, return <code>null</code>.</p> 2687 * 2688 * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters. 2689 * See {@link #params}.</p> 2690 */ 2691 public Object popParams() { 2692 2693 try { 2694 if (log.isTraceEnabled()) { 2695 log.trace("Popping params"); 2696 } 2697 return (params.pop()); 2698 } catch (EmptyStackException e) { 2699 log.warn("Empty stack (returning null)"); 2700 return (null); 2701 } 2702 2703 } 2704 2705 2706 /** 2707 * <p>Push a new object onto the top of the parameters stack.</p> 2708 * 2709 * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters. 2710 * See {@link #params}.</p> 2711 * 2712 * @param object The new object 2713 */ 2714 public void pushParams(Object object) { 2715 if (log.isTraceEnabled()) { 2716 log.trace("Pushing params"); 2717 } 2718 params.push(object); 2719 2720 } 2721 2722 /** 2723 * Create a SAX exception which also understands about the location in 2724 * the digester file where the exception occurs 2725 * 2726 * @return the new exception 2727 */ 2728 public SAXException createSAXException(String message, Exception e) { 2729 if ((e != null) && 2730 (e instanceof InvocationTargetException)) { 2731 Throwable t = ((InvocationTargetException) e).getTargetException(); 2732 if ((t != null) && (t instanceof Exception)) { 2733 e = (Exception) t; 2734 } 2735 } 2736 if (locator != null) { 2737 String error = "Error at (" + locator.getLineNumber() + ", " + 2738 locator.getColumnNumber() + ": " + message; 2739 if (e != null) { 2740 return new SAXParseException(error, locator, e); 2741 } else { 2742 return new SAXParseException(error, locator); 2743 } 2744 } 2745 log.error("No Locator!"); 2746 if (e != null) { 2747 return new SAXException(message, e); 2748 } else { 2749 return new SAXException(message); 2750 } 2751 } 2752 2753 /** 2754 * Create a SAX exception which also understands about the location in 2755 * the digester file where the exception occurs 2756 * 2757 * @return the new exception 2758 */ 2759 public SAXException createSAXException(Exception e) { 2760 if (e instanceof InvocationTargetException) { 2761 Throwable t = ((InvocationTargetException) e).getTargetException(); 2762 if ((t != null) && (t instanceof Exception)) { 2763 e = (Exception) t; 2764 } 2765 } 2766 return createSAXException(e.getMessage(), e); 2767 } 2768 2769 /** 2770 * Create a SAX exception which also understands about the location in 2771 * the digester file where the exception occurs 2772 * 2773 * @return the new exception 2774 */ 2775 public SAXException createSAXException(String message) { 2776 return createSAXException(message, null); 2777 } 2778 2779 2780 // ------------------------------------------------------- Private Methods 2781 2782 2783 /** 2784 * Returns an attributes list which contains all the attributes 2785 * passed in, with any text of form "${xxx}" in an attribute value 2786 * replaced by the appropriate value from the system property. 2787 */ 2788 private Attributes updateAttributes(Attributes list) { 2789 2790 if (list.getLength() == 0) { 2791 return list; 2792 } 2793 2794 AttributesImpl newAttrs = new AttributesImpl(list); 2795 int nAttributes = newAttrs.getLength(); 2796 for (int i = 0; i < nAttributes; ++i) { 2797 String value = newAttrs.getValue(i); 2798 try { 2799 String newValue = 2800 IntrospectionUtils.replaceProperties(value, null, source); 2801 if (value != newValue) { 2802 newAttrs.setValue(i, newValue); 2803 } 2804 } 2805 catch (Exception e) { 2806 // ignore - let the attribute have its original value 2807 } 2808 } 2809 2810 return newAttrs; 2811 2812 } 2813 2814 2815 /** 2816 * Return a new StringBuffer containing the same contents as the 2817 * input buffer, except that data of form ${varname} have been 2818 * replaced by the value of that var as defined in the system property. 2819 */ 2820 private StringBuffer updateBodyText(StringBuffer bodyText) { 2821 String in = bodyText.toString(); 2822 String out; 2823 try { 2824 out = IntrospectionUtils.replaceProperties(in, null, source); 2825 } catch(Exception e) { 2826 return bodyText; // return unchanged data 2827 } 2828 2829 if (out == in) { 2830 // No substitutions required. Don't waste memory creating 2831 // a new buffer 2832 return bodyText; 2833 } else { 2834 return new StringBuffer(out); 2835 } 2836 } 2837 2838 2839 }