001 /* $Id: Digester.java 992060 2010-09-02 19:09:47Z simonetripodi $ 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one or more 004 * contributor license agreements. See the NOTICE file distributed with 005 * this work for additional information regarding copyright ownership. 006 * The ASF licenses this file to You under the Apache License, Version 2.0 007 * (the "License"); you may not use this file except in compliance with 008 * the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package org.apache.commons.digester; 020 021 022 import java.io.File; 023 import java.io.FileInputStream; 024 import java.io.IOException; 025 import java.io.InputStream; 026 import java.io.Reader; 027 import java.lang.reflect.InvocationTargetException; 028 import java.net.MalformedURLException; 029 import java.net.URL; 030 import java.net.URLConnection; 031 import java.util.ArrayList; 032 import java.util.EmptyStackException; 033 import java.util.HashMap; 034 import java.util.List; 035 import java.util.Map; 036 import java.util.Properties; 037 import java.util.Stack; 038 039 import javax.xml.parsers.ParserConfigurationException; 040 import javax.xml.parsers.SAXParser; 041 import javax.xml.parsers.SAXParserFactory; 042 import javax.xml.validation.Schema; 043 044 import org.apache.commons.logging.Log; 045 import org.apache.commons.logging.LogFactory; 046 import org.xml.sax.Attributes; 047 import org.xml.sax.ContentHandler; 048 import org.xml.sax.EntityResolver; 049 import org.xml.sax.ErrorHandler; 050 import org.xml.sax.InputSource; 051 import org.xml.sax.Locator; 052 import org.xml.sax.SAXException; 053 import org.xml.sax.SAXNotRecognizedException; 054 import org.xml.sax.SAXNotSupportedException; 055 import org.xml.sax.SAXParseException; 056 import org.xml.sax.XMLReader; 057 import org.xml.sax.helpers.DefaultHandler; 058 059 060 061 062 /** 063 * <p>A <strong>Digester</strong> processes an XML input stream by matching a 064 * series of element nesting patterns to execute Rules that have been added 065 * prior to the start of parsing.</p> 066 * 067 * <p>See the <a href="package-summary.html#package_description">Digester 068 * Developer Guide</a> for more information.</p> 069 * 070 * <p><strong>IMPLEMENTATION NOTE</strong> - A single Digester instance may 071 * only be used within the context of a single thread at a time, and a call 072 * to <code>parse()</code> must be completed before another can be initiated 073 * even from the same thread.</p> 074 * 075 * <p>A Digester instance should not be used for parsing more than one input 076 * document. The problem is that the Digester class has quite a few member 077 * variables whose values "evolve" as SAX events are received during a parse. 078 * When reusing the Digester instance, all these members must be reset back 079 * to their initial states before the second parse begins. The "clear()" 080 * method makes a stab at resetting these, but it is actually rather a 081 * difficult problem. If you are determined to reuse Digester instances, then 082 * at the least you should call the clear() method before each parse, and must 083 * call it if the Digester parse terminates due to an exception during a parse. 084 * </p> 085 * 086 * <p><strong>LEGACY IMPLEMENTATION NOTE</strong> - When using the legacy XML 087 * schema support (instead of using the {@link Schema} class), a bug in 088 * Xerces 2.0.2 prevents the support of XML schema. You need Xerces 2.1/2.3 089 * and up to make this class work with the legacy XML schema support.</p> 090 * 091 * <p>This package was inspired by the <code>XmlMapper</code> class that was 092 * part of Tomcat 3.0 and 3.1, but is organized somewhat differently.</p> 093 */ 094 095 public class Digester extends DefaultHandler { 096 097 098 // --------------------------------------------------------- Constructors 099 100 101 /** 102 * Construct a new Digester with default properties. 103 */ 104 public Digester() { 105 106 super(); 107 108 } 109 110 111 /** 112 * Construct a new Digester, allowing a SAXParser to be passed in. This 113 * allows Digester to be used in environments which are unfriendly to 114 * JAXP1.1 (such as WebLogic 6.0). This may help in places where 115 * you are able to load JAXP 1.1 classes yourself. 116 */ 117 public Digester(SAXParser parser) { 118 119 super(); 120 121 this.parser = parser; 122 123 } 124 125 126 /** 127 * Construct a new Digester, allowing an XMLReader to be passed in. This 128 * allows Digester to be used in environments which are unfriendly to 129 * JAXP1.1 (such as WebLogic 6.0). Note that if you use this option you 130 * have to configure namespace and validation support yourself, as these 131 * properties only affect the SAXParser and emtpy constructor. 132 */ 133 public Digester(XMLReader reader) { 134 135 super(); 136 137 this.reader = reader; 138 139 } 140 141 142 // --------------------------------------------------- Instance Variables 143 144 145 /** 146 * The body text of the current element. 147 */ 148 protected StringBuffer bodyText = new StringBuffer(); 149 150 151 /** 152 * The stack of body text string buffers for surrounding elements. 153 */ 154 protected Stack<StringBuffer> bodyTexts = new Stack<StringBuffer>(); 155 156 157 /** 158 * Stack whose elements are List objects, each containing a list of 159 * Rule objects as returned from Rules.getMatch(). As each xml element 160 * in the input is entered, the matching rules are pushed onto this 161 * stack. After the end tag is reached, the matches are popped again. 162 * The depth of is stack is therefore exactly the same as the current 163 * "nesting" level of the input xml. 164 * 165 * @since 1.6 166 */ 167 protected Stack<List<Rule>> matches = new Stack<List<Rule>>(); 168 169 /** 170 * The class loader to use for instantiating application objects. 171 * If not specified, the context class loader, or the class loader 172 * used to load Digester itself, is used, based on the value of the 173 * <code>useContextClassLoader</code> variable. 174 */ 175 protected ClassLoader classLoader = null; 176 177 178 /** 179 * Has this Digester been configured yet. 180 */ 181 protected boolean configured = false; 182 183 184 /** 185 * The EntityResolver used by the SAX parser. By default it use this class 186 */ 187 protected EntityResolver entityResolver; 188 189 /** 190 * The URLs of entityValidator that have been registered, keyed by the public 191 * identifier that corresponds. 192 */ 193 protected HashMap<String, URL> entityValidator = new HashMap<String, URL>(); 194 195 196 /** 197 * The application-supplied error handler that is notified when parsing 198 * warnings, errors, or fatal errors occur. 199 */ 200 protected ErrorHandler errorHandler = null; 201 202 203 /** 204 * The SAXParserFactory that is created the first time we need it. 205 */ 206 protected SAXParserFactory factory = null; 207 208 /** 209 * @deprecated This is now managed by {@link ParserFeatureSetterFactory} 210 */ 211 @Deprecated 212 protected String JAXP_SCHEMA_LANGUAGE = 213 "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; 214 215 216 /** 217 * The Locator associated with our parser. 218 */ 219 protected Locator locator = null; 220 221 222 /** 223 * The current match pattern for nested element processing. 224 */ 225 protected String match = ""; 226 227 228 /** 229 * Do we want a "namespace aware" parser. 230 */ 231 protected boolean namespaceAware = false; 232 233 234 /** 235 * Registered namespaces we are currently processing. The key is the 236 * namespace prefix that was declared in the document. The value is an 237 * Stack of the namespace URIs this prefix has been mapped to -- 238 * the top Stack element is the most current one. (This architecture 239 * is required because documents can declare nested uses of the same 240 * prefix for different Namespace URIs). 241 */ 242 protected HashMap<String, Stack<String>> namespaces = new HashMap<String, Stack<String>>(); 243 244 245 /** 246 * Do we want a "XInclude aware" parser. 247 */ 248 protected boolean xincludeAware = false; 249 250 251 /** 252 * The parameters stack being utilized by CallMethodRule and 253 * CallParamRule rules. 254 * 255 * @since 2.0 256 */ 257 protected Stack<Object> params = new Stack<Object>(); 258 259 260 /** 261 * The SAXParser we will use to parse the input stream. 262 */ 263 protected SAXParser parser = null; 264 265 266 /** 267 * The public identifier of the DTD we are currently parsing under 268 * (if any). 269 */ 270 protected String publicId = null; 271 272 273 /** 274 * The XMLReader used to parse digester rules. 275 */ 276 protected XMLReader reader = null; 277 278 279 /** 280 * The "root" element of the stack (in other words, the last object 281 * that was popped. 282 */ 283 protected Object root = null; 284 285 286 /** 287 * The <code>Rules</code> implementation containing our collection of 288 * <code>Rule</code> instances and associated matching policy. If not 289 * established before the first rule is added, a default implementation 290 * will be provided. 291 */ 292 protected Rules rules = null; 293 294 /** 295 * The XML schema language to use for validating an XML instance. By 296 * default this value is set to <code>W3C_XML_SCHEMA</code> 297 * 298 * @deprecated Use {@link Schema} support instead. 299 */ 300 @Deprecated 301 protected String schemaLanguage = W3C_XML_SCHEMA; 302 303 304 /** 305 * The XML schema to use for validating an XML instance. 306 * 307 * @deprecated Use {@link Schema} support instead. 308 */ 309 @Deprecated 310 protected String schemaLocation = null; 311 312 313 /** 314 * The XML schema to use for validating an XML instance. 315 * 316 * @since 2.0 317 */ 318 protected Schema schema = null; 319 320 321 /** 322 * The object stack being constructed. 323 */ 324 protected Stack<Object> stack = new Stack<Object>(); 325 326 327 /** 328 * Do we want to use the Context ClassLoader when loading classes 329 * for instantiating new objects. Default is <code>false</code>. 330 */ 331 protected boolean useContextClassLoader = false; 332 333 334 /** 335 * Do we want to use a validating parser. 336 */ 337 protected boolean validating = false; 338 339 340 /** 341 * The Log to which most logging calls will be made. 342 */ 343 protected Log log = 344 LogFactory.getLog("org.apache.commons.digester.Digester"); 345 346 347 /** 348 * The Log to which all SAX event related logging calls will be made. 349 */ 350 protected Log saxLog = 351 LogFactory.getLog("org.apache.commons.digester.Digester.sax"); 352 353 354 /** 355 * The schema language supported. By default, we use this one. 356 */ 357 protected static final String W3C_XML_SCHEMA = 358 "http://www.w3.org/2001/XMLSchema"; 359 360 /** 361 * An optional class that substitutes values in attributes and body text. 362 * This may be null and so a null check is always required before use. 363 */ 364 protected Substitutor substitutor; 365 366 /** Stacks used for interrule communication, indexed by name String */ 367 private HashMap<String, Stack<Object>> stacksByName = new HashMap<String, Stack<Object>>(); 368 369 /** 370 * If not null, then calls by the parser to this object's characters, 371 * startElement, endElement and processingInstruction methods are 372 * forwarded to the specified object. This is intended to allow rules 373 * to temporarily "take control" of the sax events. In particular, 374 * this is used by NodeCreateRule. 375 * <p> 376 * See setCustomContentHandler. 377 */ 378 private ContentHandler customContentHandler = null; 379 380 /** 381 * Object which will receive callbacks for every pop/push action 382 * on the default stack or named stacks. 383 */ 384 private StackAction stackAction = null; 385 386 // ------------------------------------------------------------- Properties 387 388 /** 389 * Return the currently mapped namespace URI for the specified prefix, 390 * if any; otherwise return <code>null</code>. These mappings come and 391 * go dynamically as the document is parsed. 392 * 393 * @param prefix Prefix to look up 394 */ 395 public String findNamespaceURI(String prefix) { 396 397 Stack<String> nsStack = namespaces.get(prefix); 398 if (nsStack == null) { 399 return null; 400 } 401 try { 402 return (nsStack.peek()); 403 } catch (EmptyStackException e) { 404 return null; 405 } 406 407 } 408 409 410 /** 411 * Return the class loader to be used for instantiating application objects 412 * when required. This is determined based upon the following rules: 413 * <ul> 414 * <li>The class loader set by <code>setClassLoader()</code>, if any</li> 415 * <li>The thread context class loader, if it exists and the 416 * <code>useContextClassLoader</code> property is set to true</li> 417 * <li>The class loader used to load the Digester class itself. 418 * </ul> 419 */ 420 public ClassLoader getClassLoader() { 421 422 if (this.classLoader != null) { 423 return (this.classLoader); 424 } 425 if (this.useContextClassLoader) { 426 ClassLoader classLoader = 427 Thread.currentThread().getContextClassLoader(); 428 if (classLoader != null) { 429 return (classLoader); 430 } 431 } 432 return (this.getClass().getClassLoader()); 433 434 } 435 436 437 /** 438 * Set the class loader to be used for instantiating application objects 439 * when required. 440 * 441 * @param classLoader The new class loader to use, or <code>null</code> 442 * to revert to the standard rules 443 */ 444 public void setClassLoader(ClassLoader classLoader) { 445 446 this.classLoader = classLoader; 447 448 } 449 450 451 /** 452 * Return the current depth of the element stack. 453 */ 454 public int getCount() { 455 456 return (stack.size()); 457 458 } 459 460 461 /** 462 * Return the name of the XML element that is currently being processed. 463 */ 464 public String getCurrentElementName() { 465 466 String elementName = match; 467 int lastSlash = elementName.lastIndexOf('/'); 468 if (lastSlash >= 0) { 469 elementName = elementName.substring(lastSlash + 1); 470 } 471 return (elementName); 472 473 } 474 475 476 /** 477 * Return the debugging detail level of our currently enabled logger. 478 * 479 * @deprecated This method now always returns 0. Digester uses the apache 480 * jakarta commons-logging library; see the documentation for that library 481 * for more information. 482 */ 483 @Deprecated 484 public int getDebug() { 485 486 return (0); 487 488 } 489 490 491 /** 492 * Set the debugging detail level of our currently enabled logger. 493 * 494 * @param debug New debugging detail level (0=off, increasing integers 495 * for more detail) 496 * 497 * @deprecated This method now has no effect at all. Digester uses 498 * the apache jakarta comons-logging library; see the documentation 499 * for that library for more information. 500 */ 501 @Deprecated 502 public void setDebug(int debug) { 503 504 // No action is taken 505 506 } 507 508 509 /** 510 * Return the error handler for this Digester. 511 */ 512 public ErrorHandler getErrorHandler() { 513 514 return (this.errorHandler); 515 516 } 517 518 519 /** 520 * Set the error handler for this Digester. 521 * 522 * @param errorHandler The new error handler 523 */ 524 public void setErrorHandler(ErrorHandler errorHandler) { 525 526 this.errorHandler = errorHandler; 527 528 } 529 530 531 /** 532 * Return the SAXParserFactory we will use, creating one if necessary. 533 */ 534 public SAXParserFactory getFactory() { 535 536 if (factory == null) { 537 factory = SAXParserFactory.newInstance(); 538 factory.setNamespaceAware(namespaceAware); 539 factory.setXIncludeAware(xincludeAware); 540 factory.setValidating(validating); 541 factory.setSchema(schema); 542 } 543 return (factory); 544 545 } 546 547 548 /** 549 * Returns a flag indicating whether the requested feature is supported 550 * by the underlying implementation of <code>org.xml.sax.XMLReader</code>. 551 * See <a href="http://www.saxproject.org">the saxproject website</a> 552 * for information about the standard SAX2 feature flags. 553 * 554 * @param feature Name of the feature to inquire about 555 * 556 * @exception ParserConfigurationException if a parser configuration error 557 * occurs 558 * @exception SAXNotRecognizedException if the property name is 559 * not recognized 560 * @exception SAXNotSupportedException if the property name is 561 * recognized but not supported 562 */ 563 public boolean getFeature(String feature) 564 throws ParserConfigurationException, SAXNotRecognizedException, 565 SAXNotSupportedException { 566 567 return (getFactory().getFeature(feature)); 568 569 } 570 571 572 /** 573 * Sets a flag indicating whether the requested feature is supported 574 * by the underlying implementation of <code>org.xml.sax.XMLReader</code>. 575 * See <a href="http://www.saxproject.org">the saxproject website</a> 576 * for information about the standard SAX2 feature flags. In order to be 577 * effective, this method must be called <strong>before</strong> the 578 * <code>getParser()</code> method is called for the first time, either 579 * directly or indirectly. 580 * 581 * @param feature Name of the feature to set the status for 582 * @param value The new value for this feature 583 * 584 * @exception ParserConfigurationException if a parser configuration error 585 * occurs 586 * @exception SAXNotRecognizedException if the property name is 587 * not recognized 588 * @exception SAXNotSupportedException if the property name is 589 * recognized but not supported 590 */ 591 public void setFeature(String feature, boolean value) 592 throws ParserConfigurationException, SAXNotRecognizedException, 593 SAXNotSupportedException { 594 595 getFactory().setFeature(feature, value); 596 597 } 598 599 600 /** 601 * Return the current Logger associated with this instance of the Digester 602 */ 603 public Log getLogger() { 604 605 return log; 606 607 } 608 609 610 /** 611 * Set the current logger for this Digester. 612 */ 613 public void setLogger(Log log) { 614 615 this.log = log; 616 617 } 618 619 /** 620 * Gets the logger used for logging SAX-related information. 621 * <strong>Note</strong> the output is finely grained. 622 * 623 * @since 1.6 624 */ 625 public Log getSAXLogger() { 626 627 return saxLog; 628 } 629 630 631 /** 632 * Sets the logger used for logging SAX-related information. 633 * <strong>Note</strong> the output is finely grained. 634 * @param saxLog Log, not null 635 * 636 * @since 1.6 637 */ 638 public void setSAXLogger(Log saxLog) { 639 640 this.saxLog = saxLog; 641 } 642 643 /** 644 * Return the current rule match path 645 */ 646 public String getMatch() { 647 648 return match; 649 650 } 651 652 653 /** 654 * Return the "namespace aware" flag for parsers we create. 655 */ 656 public boolean getNamespaceAware() { 657 658 return (this.namespaceAware); 659 660 } 661 662 663 /** 664 * Set the "namespace aware" flag for parsers we create. 665 * 666 * @param namespaceAware The new "namespace aware" flag 667 */ 668 public void setNamespaceAware(boolean namespaceAware) { 669 670 this.namespaceAware = namespaceAware; 671 672 } 673 674 675 /** 676 * Return the XInclude-aware flag for parsers we create. XInclude 677 * functionality additionally requires namespace-awareness. 678 * 679 * @return The XInclude-aware flag 680 * 681 * @see #getNamespaceAware() 682 * 683 * @since 2.0 684 */ 685 public boolean getXIncludeAware() { 686 687 return (this.xincludeAware); 688 689 } 690 691 692 /** 693 * Set the XInclude-aware flag for parsers we create. This additionally 694 * requires namespace-awareness. 695 * 696 * @param xincludeAware The new XInclude-aware flag 697 * 698 * @see #setNamespaceAware(boolean) 699 * 700 * @since 2.0 701 */ 702 public void setXIncludeAware(boolean xincludeAware) { 703 704 this.xincludeAware = xincludeAware; 705 706 } 707 708 709 /** 710 * Set the publid id of the current file being parse. 711 * @param publicId the DTD/Schema public's id. 712 */ 713 public void setPublicId(String publicId){ 714 this.publicId = publicId; 715 } 716 717 718 /** 719 * Return the public identifier of the DTD we are currently 720 * parsing under, if any. 721 */ 722 public String getPublicId() { 723 724 return (this.publicId); 725 726 } 727 728 729 /** 730 * Return the namespace URI that will be applied to all subsequently 731 * added <code>Rule</code> objects. 732 */ 733 public String getRuleNamespaceURI() { 734 735 return (getRules().getNamespaceURI()); 736 737 } 738 739 740 /** 741 * Set the namespace URI that will be applied to all subsequently 742 * added <code>Rule</code> objects. 743 * 744 * @param ruleNamespaceURI Namespace URI that must match on all 745 * subsequently added rules, or <code>null</code> for matching 746 * regardless of the current namespace URI 747 */ 748 public void setRuleNamespaceURI(String ruleNamespaceURI) { 749 750 getRules().setNamespaceURI(ruleNamespaceURI); 751 752 } 753 754 755 /** 756 * Return the SAXParser we will use to parse the input stream. If there 757 * is a problem creating the parser, return <code>null</code>. 758 */ 759 public SAXParser getParser() { 760 761 // Return the parser we already created (if any) 762 if (parser != null) { 763 return (parser); 764 } 765 766 // Create a new parser 767 try { 768 if (validating && (schemaLocation != null)) { 769 // There is no portable way to specify the location of 770 // an xml schema to be applied to the input document, so 771 // we have to use parser-specific code for this. That code 772 // is hidden behind the ParserFeatureSetterFactory class. 773 774 // The above has changed in JDK 1.5 and no longer true. The 775 // functionality used in this block has now been deprecated. 776 // We now use javax.xml.validation.Schema instead. 777 778 Properties properties = new Properties(); 779 properties.put("SAXParserFactory", getFactory()); 780 if (schemaLocation != null) { 781 properties.put("schemaLocation", schemaLocation); 782 properties.put("schemaLanguage", schemaLanguage); 783 } 784 parser = ParserFeatureSetterFactory.newSAXParser(properties); 785 } else { 786 // The user doesn't want to use any non-portable parsing features, 787 // so we can just use the portable API here. Note that method 788 // getFactory returns a factory already configured with the 789 // appropriate namespaceAware and validating properties. 790 791 parser = getFactory().newSAXParser(); 792 } 793 } catch (Exception e) { 794 log.error("Digester.getParser: ", e); 795 return (null); 796 } 797 798 return (parser); 799 800 } 801 802 803 /** 804 * Return the current value of the specified property for the underlying 805 * <code>XMLReader</code> implementation. 806 * See <a href="http://www.saxproject.org">the saxproject website</a> 807 * for information about the standard SAX2 properties. 808 * 809 * @param property Property name to be retrieved 810 * 811 * @exception SAXNotRecognizedException if the property name is 812 * not recognized 813 * @exception SAXNotSupportedException if the property name is 814 * recognized but not supported 815 */ 816 public Object getProperty(String property) 817 throws SAXNotRecognizedException, SAXNotSupportedException { 818 819 return (getParser().getProperty(property)); 820 821 } 822 823 824 /** 825 * Set the current value of the specified property for the underlying 826 * <code>XMLReader</code> implementation. 827 * See <a href="http://www.saxproject.org">the saxproject website</a> 828 * for information about the standard SAX2 properties. 829 * 830 * @param property Property name to be set 831 * @param value Property value to be set 832 * 833 * @exception SAXNotRecognizedException if the property name is 834 * not recognized 835 * @exception SAXNotSupportedException if the property name is 836 * recognized but not supported 837 */ 838 public void setProperty(String property, Object value) 839 throws SAXNotRecognizedException, SAXNotSupportedException { 840 841 getParser().setProperty(property, value); 842 843 } 844 845 846 /** 847 * By setting the reader in the constructor, you can bypass JAXP and 848 * be able to use digester in Weblogic 6.0. 849 * 850 * @deprecated Use getXMLReader() instead, which can throw a 851 * SAXException if the reader cannot be instantiated 852 */ 853 @Deprecated 854 public XMLReader getReader() { 855 856 try { 857 return (getXMLReader()); 858 } catch (SAXException e) { 859 log.error("Cannot get XMLReader", e); 860 return (null); 861 } 862 863 } 864 865 866 /** 867 * Return the <code>Rules</code> implementation object containing our 868 * rules collection and associated matching policy. If none has been 869 * established, a default implementation will be created and returned. 870 */ 871 public Rules getRules() { 872 873 if (this.rules == null) { 874 this.rules = new RulesBase(); 875 this.rules.setDigester(this); 876 } 877 return (this.rules); 878 879 } 880 881 882 /** 883 * Set the <code>Rules</code> implementation object containing our 884 * rules collection and associated matching policy. 885 * 886 * @param rules New Rules implementation 887 */ 888 public void setRules(Rules rules) { 889 890 this.rules = rules; 891 this.rules.setDigester(this); 892 893 } 894 895 896 /** 897 * Return the XML Schema URI used for validating an XML instance. 898 * 899 * @deprecated Use {@link Schema} for validation instead. 900 * @see #getXMLSchema() 901 * @see #setXMLSchema(Schema) 902 */ 903 @Deprecated 904 public String getSchema() { 905 906 return (this.schemaLocation); 907 908 } 909 910 911 /** 912 * Set the XML Schema URI used for validating the input XML. 913 * <p> 914 * It is often desirable to <i>force</i> the input document to be 915 * validated against a particular schema regardless of what type 916 * the input document declares itself to be. This method allows that 917 * to be done. 918 * <p> 919 * Note, however, that there is no standard API for enabling this 920 * feature on the underlying SAX parser; this method therefore only works 921 * for those parsers explicitly supported by Digester's 922 * ParserFeatureSetterFactory class. If the underlying parser does not 923 * support the feature, or is not one of the supported parsers, then 924 * an exception will be thrown when getParser is called (explicitly, 925 * or implicitly via the parse method). 926 * <p> 927 * See also method setSchemaLanguage which allows the type of the schema 928 * specified here to be defined. By default, the schema is expected to 929 * be a W3C xml schema definition. 930 * <p> 931 * IMPORTANT NOTE: This functionality was never very reliable, and has 932 * been horribly broken since the 1.6 release of Digester. There are 933 * currently no plans to fix it, so you are strongly recommended to 934 * avoid using this method. Instead, create an XMLParser instance 935 * yourself, configure validation appropriately, and pass it as a 936 * parameter to the Digester constructor. 937 * 938 * @param schemaLocation a URI to the schema. 939 * @deprecated Use {@link Schema} for validation instead. 940 * @see #getXMLSchema() 941 * @see #setXMLSchema(Schema) 942 */ 943 @Deprecated 944 public void setSchema(String schemaLocation){ 945 946 this.schemaLocation = schemaLocation; 947 948 } 949 950 951 /** 952 * Return the XML Schema language used when parsing. 953 * 954 * @deprecated Use {@link Schema} for validation instead. 955 * @see #getXMLSchema() 956 * @see #setXMLSchema(Schema) 957 */ 958 @Deprecated 959 public String getSchemaLanguage() { 960 961 return (this.schemaLanguage); 962 963 } 964 965 966 /** 967 * Set the XML Schema language used when parsing. By default, we use W3C. 968 * 969 * @param schemaLanguage a URI to the schema language. 970 * @deprecated Use {@link Schema} for validation instead. 971 * @see #getXMLSchema() 972 * @see #setXMLSchema(Schema) 973 */ 974 @Deprecated 975 public void setSchemaLanguage(String schemaLanguage){ 976 977 this.schemaLanguage = schemaLanguage; 978 979 } 980 981 982 /** 983 * Return the XML Schema used when parsing. 984 * 985 * @return The {@link Schema} instance in use. 986 * 987 * @since 2.0 988 */ 989 public Schema getXMLSchema() { 990 991 return (this.schema); 992 993 } 994 995 996 /** 997 * Set the XML Schema to be used when parsing. 998 * 999 * @param schema The {@link Schema} instance to use. 1000 * 1001 * @since 2.0 1002 */ 1003 public void setXMLSchema(Schema schema){ 1004 1005 this.schema = schema; 1006 1007 } 1008 1009 1010 /** 1011 * Return the boolean as to whether the context classloader should be used. 1012 */ 1013 public boolean getUseContextClassLoader() { 1014 1015 return useContextClassLoader; 1016 1017 } 1018 1019 1020 /** 1021 * Determine whether to use the Context ClassLoader (the one found by 1022 * calling <code>Thread.currentThread().getContextClassLoader()</code>) 1023 * to resolve/load classes that are defined in various rules. If not 1024 * using Context ClassLoader, then the class-loading defaults to 1025 * using the calling-class' ClassLoader. 1026 * 1027 * @param use determines whether to use Context ClassLoader. 1028 */ 1029 public void setUseContextClassLoader(boolean use) { 1030 1031 useContextClassLoader = use; 1032 1033 } 1034 1035 1036 /** 1037 * Return the validating parser flag. 1038 */ 1039 public boolean getValidating() { 1040 1041 return (this.validating); 1042 1043 } 1044 1045 1046 /** 1047 * Set the validating parser flag. This must be called before 1048 * <code>parse()</code> is called the first time. 1049 * 1050 * @param validating The new validating parser flag. 1051 */ 1052 public void setValidating(boolean validating) { 1053 1054 this.validating = validating; 1055 1056 } 1057 1058 1059 /** 1060 * Return the XMLReader to be used for parsing the input document. 1061 * 1062 * FIX ME: there is a bug in JAXP/XERCES that prevent the use of a 1063 * parser that contains a schema with a DTD. 1064 * @exception SAXException if no XMLReader can be instantiated 1065 */ 1066 public XMLReader getXMLReader() throws SAXException { 1067 if (reader == null){ 1068 reader = getParser().getXMLReader(); 1069 } 1070 1071 reader.setDTDHandler(this); 1072 reader.setContentHandler(this); 1073 1074 if (entityResolver == null){ 1075 reader.setEntityResolver(this); 1076 } else { 1077 reader.setEntityResolver(entityResolver); 1078 } 1079 1080 reader.setErrorHandler(this); 1081 return reader; 1082 } 1083 1084 /** 1085 * Gets the <code>Substitutor</code> used to convert attributes and body text. 1086 * @return Substitutor, null if not substitutions are to be performed. 1087 */ 1088 public Substitutor getSubstitutor() { 1089 return substitutor; 1090 } 1091 1092 /** 1093 * Sets the <code>Substitutor</code> to be used to convert attributes and body text. 1094 * @param substitutor the Substitutor to be used to convert attributes and body text 1095 * or null if not substitution of these values is to be performed. 1096 */ 1097 public void setSubstitutor(Substitutor substitutor) { 1098 this.substitutor = substitutor; 1099 } 1100 1101 /* 1102 * See setCustomContentHandler. 1103 * 1104 * @since 1.7 1105 */ 1106 public ContentHandler getCustomContentHandler() { 1107 return customContentHandler; 1108 } 1109 1110 /** 1111 * Redirects (or cancels redirecting) of SAX ContentHandler events to an 1112 * external object. 1113 * <p> 1114 * When this object's customContentHandler is non-null, any SAX events 1115 * received from the parser will simply be passed on to the specified 1116 * object instead of this object handling them. This allows Rule classes 1117 * to take control of the SAX event stream for a while in order to do 1118 * custom processing. Such a rule should save the old value before setting 1119 * a new one, and restore the old value in order to resume normal digester 1120 * processing. 1121 * <p> 1122 * An example of a Rule which needs this feature is NodeCreateRule. 1123 * <p> 1124 * Note that saving the old value is probably not needed as it should always 1125 * be null; a custom rule that wants to take control could only have been 1126 * called when there was no custom content handler. But it seems cleaner 1127 * to properly save/restore the value and maybe some day this will come in 1128 * useful. 1129 * <p> 1130 * Note also that this is not quite equivalent to 1131 * <pre> 1132 * digester.getXMLReader().setContentHandler(handler) 1133 * </pre> 1134 * for these reasons: 1135 * <ul> 1136 * <li>Some xml parsers don't like having setContentHandler called after 1137 * parsing has started. The Aelfred parser is one example.</li> 1138 * <li>Directing the events via the Digester object potentially allows 1139 * us to log information about those SAX events at the digester level.</li> 1140 * </ul> 1141 * 1142 * @since 1.7 1143 */ 1144 public void setCustomContentHandler(ContentHandler handler) { 1145 customContentHandler = handler; 1146 } 1147 1148 /** 1149 * Define a callback object which is invoked whever an object is pushed onto 1150 * a digester object stack, or popped off one. 1151 * 1152 * @since 1.8 1153 */ 1154 public void setStackAction(StackAction stackAction) { 1155 this.stackAction = stackAction; 1156 } 1157 1158 /** 1159 * See setStackAction. 1160 * 1161 * @since 1.8 1162 */ 1163 public StackAction getStackAction() { 1164 return stackAction; 1165 } 1166 1167 /** 1168 * Get the most current namespaces for all prefixes. 1169 * 1170 * @return Map A map with namespace prefixes as keys and most current 1171 * namespace URIs for the corresponding prefixes as values 1172 * 1173 * @since 1.8 1174 */ 1175 public Map<String, String> getCurrentNamespaces() { 1176 if (!namespaceAware) { 1177 log.warn("Digester is not namespace aware"); 1178 } 1179 Map<String, String> currentNamespaces = new HashMap<String, String>(); 1180 for (Map.Entry<String, Stack<String>> nsEntry : namespaces.entrySet()) { 1181 try { 1182 currentNamespaces.put(nsEntry.getKey(), 1183 nsEntry.getValue().peek()); 1184 } catch (RuntimeException e) { 1185 // rethrow, after logging 1186 log.error(e.getMessage(), e); 1187 throw e; 1188 } 1189 } 1190 return currentNamespaces; 1191 } 1192 1193 // ------------------------------------------------- ContentHandler Methods 1194 1195 1196 /** 1197 * Process notification of character data received from the body of 1198 * an XML element. 1199 * 1200 * @param buffer The characters from the XML document 1201 * @param start Starting offset into the buffer 1202 * @param length Number of characters from the buffer 1203 * 1204 * @exception SAXException if a parsing error is to be reported 1205 */ 1206 @Override 1207 public void characters(char buffer[], int start, int length) 1208 throws SAXException { 1209 1210 if (customContentHandler != null) { 1211 // forward calls instead of handling them here 1212 customContentHandler.characters(buffer, start, length); 1213 return; 1214 } 1215 1216 if (saxLog.isDebugEnabled()) { 1217 saxLog.debug("characters(" + new String(buffer, start, length) + ")"); 1218 } 1219 1220 bodyText.append(buffer, start, length); 1221 1222 } 1223 1224 1225 /** 1226 * Process notification of the end of the document being reached. 1227 * 1228 * @exception SAXException if a parsing error is to be reported 1229 */ 1230 @Override 1231 public void endDocument() throws SAXException { 1232 1233 if (saxLog.isDebugEnabled()) { 1234 if (getCount() > 1) { 1235 saxLog.debug("endDocument(): " + getCount() + 1236 " elements left"); 1237 } else { 1238 saxLog.debug("endDocument()"); 1239 } 1240 } 1241 1242 // Fire "finish" events for all defined rules 1243 for (Rule rule : getRules().rules()) { 1244 try { 1245 rule.finish(); 1246 } catch (Exception e) { 1247 log.error("Finish event threw exception", e); 1248 throw createSAXException(e); 1249 } catch (Error e) { 1250 log.error("Finish event threw error", e); 1251 throw e; 1252 } 1253 } 1254 1255 // Perform final cleanup 1256 clear(); 1257 1258 } 1259 1260 1261 /** 1262 * Process notification of the end of an XML element being reached. 1263 * 1264 * @param namespaceURI - The Namespace URI, or the empty string if the 1265 * element has no Namespace URI or if Namespace processing is not 1266 * being performed. 1267 * @param localName - The local name (without prefix), or the empty 1268 * string if Namespace processing is not being performed. 1269 * @param qName - The qualified XML 1.0 name (with prefix), or the 1270 * empty string if qualified names are not available. 1271 * @exception SAXException if a parsing error is to be reported 1272 */ 1273 @Override 1274 public void endElement(String namespaceURI, String localName, 1275 String qName) throws SAXException { 1276 1277 if (customContentHandler != null) { 1278 // forward calls instead of handling them here 1279 customContentHandler.endElement(namespaceURI, localName, qName); 1280 return; 1281 } 1282 1283 boolean debug = log.isDebugEnabled(); 1284 1285 if (debug) { 1286 if (saxLog.isDebugEnabled()) { 1287 saxLog.debug("endElement(" + namespaceURI + "," + localName + 1288 "," + qName + ")"); 1289 } 1290 log.debug(" match='" + match + "'"); 1291 log.debug(" bodyText='" + bodyText + "'"); 1292 } 1293 1294 // the actual element name is either in localName or qName, depending 1295 // on whether the parser is namespace aware 1296 String name = localName; 1297 if ((name == null) || (name.length() < 1)) { 1298 name = qName; 1299 } 1300 1301 // Fire "body" events for all relevant rules 1302 List<Rule> rules = matches.pop(); 1303 if ((rules != null) && (rules.size() > 0)) { 1304 String bodyText = this.bodyText.toString(); 1305 Substitutor substitutor = getSubstitutor(); 1306 if (substitutor!= null) { 1307 bodyText = substitutor.substitute(bodyText); 1308 } 1309 for (int i = 0; i < rules.size(); i++) { 1310 try { 1311 Rule rule = rules.get(i); 1312 if (debug) { 1313 log.debug(" Fire body() for " + rule); 1314 } 1315 rule.body(namespaceURI, name, bodyText); 1316 } catch (Exception e) { 1317 log.error("Body event threw exception", e); 1318 throw createSAXException(e); 1319 } catch (Error e) { 1320 log.error("Body event threw error", e); 1321 throw e; 1322 } 1323 } 1324 } else { 1325 if (debug) { 1326 log.debug(" No rules found matching '" + match + "'."); 1327 } 1328 } 1329 1330 // Recover the body text from the surrounding element 1331 bodyText = bodyTexts.pop(); 1332 if (debug) { 1333 log.debug(" Popping body text '" + bodyText.toString() + "'"); 1334 } 1335 1336 // Fire "end" events for all relevant rules in reverse order 1337 if (rules != null) { 1338 for (int i = 0; i < rules.size(); i++) { 1339 int j = (rules.size() - i) - 1; 1340 try { 1341 Rule rule = rules.get(j); 1342 if (debug) { 1343 log.debug(" Fire end() for " + rule); 1344 } 1345 rule.end(namespaceURI, name); 1346 } catch (Exception e) { 1347 log.error("End event threw exception", e); 1348 throw createSAXException(e); 1349 } catch (Error e) { 1350 log.error("End event threw error", e); 1351 throw e; 1352 } 1353 } 1354 } 1355 1356 // Recover the previous match expression 1357 int slash = match.lastIndexOf('/'); 1358 if (slash >= 0) { 1359 match = match.substring(0, slash); 1360 } else { 1361 match = ""; 1362 } 1363 1364 } 1365 1366 1367 /** 1368 * Process notification that a namespace prefix is going out of scope. 1369 * 1370 * @param prefix Prefix that is going out of scope 1371 * 1372 * @exception SAXException if a parsing error is to be reported 1373 */ 1374 @Override 1375 public void endPrefixMapping(String prefix) throws SAXException { 1376 1377 if (saxLog.isDebugEnabled()) { 1378 saxLog.debug("endPrefixMapping(" + prefix + ")"); 1379 } 1380 1381 // Deregister this prefix mapping 1382 Stack<String> stack = namespaces.get(prefix); 1383 if (stack == null) { 1384 return; 1385 } 1386 try { 1387 stack.pop(); 1388 if (stack.empty()) 1389 namespaces.remove(prefix); 1390 } catch (EmptyStackException e) { 1391 throw createSAXException("endPrefixMapping popped too many times"); 1392 } 1393 1394 } 1395 1396 1397 /** 1398 * Process notification of ignorable whitespace received from the body of 1399 * an XML element. 1400 * 1401 * @param buffer The characters from the XML document 1402 * @param start Starting offset into the buffer 1403 * @param len Number of characters from the buffer 1404 * 1405 * @exception SAXException if a parsing error is to be reported 1406 */ 1407 @Override 1408 public void ignorableWhitespace(char buffer[], int start, int len) 1409 throws SAXException { 1410 1411 if (saxLog.isDebugEnabled()) { 1412 saxLog.debug("ignorableWhitespace(" + 1413 new String(buffer, start, len) + ")"); 1414 } 1415 1416 // No processing required 1417 1418 } 1419 1420 1421 /** 1422 * Process notification of a processing instruction that was encountered. 1423 * 1424 * @param target The processing instruction target 1425 * @param data The processing instruction data (if any) 1426 * 1427 * @exception SAXException if a parsing error is to be reported 1428 */ 1429 @Override 1430 public void processingInstruction(String target, String data) 1431 throws SAXException { 1432 1433 if (customContentHandler != null) { 1434 // forward calls instead of handling them here 1435 customContentHandler.processingInstruction(target, data); 1436 return; 1437 } 1438 1439 if (saxLog.isDebugEnabled()) { 1440 saxLog.debug("processingInstruction('" + target + "','" + data + "')"); 1441 } 1442 1443 // No processing is required 1444 1445 } 1446 1447 1448 /** 1449 * Gets the document locator associated with our parser. 1450 * 1451 * @return the Locator supplied by the document parser 1452 */ 1453 public Locator getDocumentLocator() { 1454 1455 return locator; 1456 1457 } 1458 1459 /** 1460 * Sets the document locator associated with our parser. 1461 * 1462 * @param locator The new locator 1463 */ 1464 @Override 1465 public void setDocumentLocator(Locator locator) { 1466 1467 if (saxLog.isDebugEnabled()) { 1468 saxLog.debug("setDocumentLocator(" + locator + ")"); 1469 } 1470 1471 this.locator = locator; 1472 1473 } 1474 1475 1476 /** 1477 * Process notification of a skipped entity. 1478 * 1479 * @param name Name of the skipped entity 1480 * 1481 * @exception SAXException if a parsing error is to be reported 1482 */ 1483 @Override 1484 public void skippedEntity(String name) throws SAXException { 1485 1486 if (saxLog.isDebugEnabled()) { 1487 saxLog.debug("skippedEntity(" + name + ")"); 1488 } 1489 1490 // No processing required 1491 1492 } 1493 1494 1495 /** 1496 * Process notification of the beginning of the document being reached. 1497 * 1498 * @exception SAXException if a parsing error is to be reported 1499 */ 1500 @Override 1501 public void startDocument() throws SAXException { 1502 1503 if (saxLog.isDebugEnabled()) { 1504 saxLog.debug("startDocument()"); 1505 } 1506 1507 // ensure that the digester is properly configured, as 1508 // the digester could be used as a SAX ContentHandler 1509 // rather than via the parse() methods. 1510 configure(); 1511 } 1512 1513 1514 /** 1515 * Process notification of the start of an XML element being reached. 1516 * 1517 * @param namespaceURI The Namespace URI, or the empty string if the element 1518 * has no Namespace URI or if Namespace processing is not being performed. 1519 * @param localName The local name (without prefix), or the empty 1520 * string if Namespace processing is not being performed. 1521 * @param qName The qualified name (with prefix), or the empty 1522 * string if qualified names are not available.\ 1523 * @param list The attributes attached to the element. If there are 1524 * no attributes, it shall be an empty Attributes object. 1525 * @exception SAXException if a parsing error is to be reported 1526 */ 1527 @Override 1528 public void startElement(String namespaceURI, String localName, 1529 String qName, Attributes list) 1530 throws SAXException { 1531 boolean debug = log.isDebugEnabled(); 1532 1533 if (customContentHandler != null) { 1534 // forward calls instead of handling them here 1535 customContentHandler.startElement(namespaceURI, localName, qName, list); 1536 return; 1537 } 1538 1539 if (saxLog.isDebugEnabled()) { 1540 saxLog.debug("startElement(" + namespaceURI + "," + localName + "," + 1541 qName + ")"); 1542 } 1543 1544 // Save the body text accumulated for our surrounding element 1545 bodyTexts.push(bodyText); 1546 if (debug) { 1547 log.debug(" Pushing body text '" + bodyText.toString() + "'"); 1548 } 1549 bodyText = new StringBuffer(); 1550 1551 // the actual element name is either in localName or qName, depending 1552 // on whether the parser is namespace aware 1553 String name = localName; 1554 if ((name == null) || (name.length() < 1)) { 1555 name = qName; 1556 } 1557 1558 // Compute the current matching rule 1559 StringBuffer sb = new StringBuffer(match); 1560 if (match.length() > 0) { 1561 sb.append('/'); 1562 } 1563 sb.append(name); 1564 match = sb.toString(); 1565 if (debug) { 1566 log.debug(" New match='" + match + "'"); 1567 } 1568 1569 // Fire "begin" events for all relevant rules 1570 List<Rule> rules = getRules().match(namespaceURI, match); 1571 matches.push(rules); 1572 if ((rules != null) && (rules.size() > 0)) { 1573 Substitutor substitutor = getSubstitutor(); 1574 if (substitutor!= null) { 1575 list = substitutor.substitute(list); 1576 } 1577 for (int i = 0; i < rules.size(); i++) { 1578 try { 1579 Rule rule = rules.get(i); 1580 if (debug) { 1581 log.debug(" Fire begin() for " + rule); 1582 } 1583 rule.begin(namespaceURI, name, list); 1584 } catch (Exception e) { 1585 log.error("Begin event threw exception", e); 1586 throw createSAXException(e); 1587 } catch (Error e) { 1588 log.error("Begin event threw error", e); 1589 throw e; 1590 } 1591 } 1592 } else { 1593 if (debug) { 1594 log.debug(" No rules found matching '" + match + "'."); 1595 } 1596 } 1597 1598 } 1599 1600 1601 /** 1602 * Process notification that a namespace prefix is coming in to scope. 1603 * 1604 * @param prefix Prefix that is being declared 1605 * @param namespaceURI Corresponding namespace URI being mapped to 1606 * 1607 * @exception SAXException if a parsing error is to be reported 1608 */ 1609 @Override 1610 public void startPrefixMapping(String prefix, String namespaceURI) 1611 throws SAXException { 1612 1613 if (saxLog.isDebugEnabled()) { 1614 saxLog.debug("startPrefixMapping(" + prefix + "," + namespaceURI + ")"); 1615 } 1616 1617 // Register this prefix mapping 1618 Stack<String> stack = namespaces.get(prefix); 1619 if (stack == null) { 1620 stack = new Stack<String>(); 1621 namespaces.put(prefix, stack); 1622 } 1623 stack.push(namespaceURI); 1624 1625 } 1626 1627 1628 // ----------------------------------------------------- DTDHandler Methods 1629 1630 1631 /** 1632 * Receive notification of a notation declaration event. 1633 * 1634 * @param name The notation name 1635 * @param publicId The public identifier (if any) 1636 * @param systemId The system identifier (if any) 1637 */ 1638 @Override 1639 public void notationDecl(String name, String publicId, String systemId) { 1640 1641 if (saxLog.isDebugEnabled()) { 1642 saxLog.debug("notationDecl(" + name + "," + publicId + "," + 1643 systemId + ")"); 1644 } 1645 1646 } 1647 1648 1649 /** 1650 * Receive notification of an unparsed entity declaration event. 1651 * 1652 * @param name The unparsed entity name 1653 * @param publicId The public identifier (if any) 1654 * @param systemId The system identifier (if any) 1655 * @param notation The name of the associated notation 1656 */ 1657 @Override 1658 public void unparsedEntityDecl(String name, String publicId, 1659 String systemId, String notation) { 1660 1661 if (saxLog.isDebugEnabled()) { 1662 saxLog.debug("unparsedEntityDecl(" + name + "," + publicId + "," + 1663 systemId + "," + notation + ")"); 1664 } 1665 1666 } 1667 1668 1669 // ----------------------------------------------- EntityResolver Methods 1670 1671 /** 1672 * Set the <code>EntityResolver</code> used by SAX when resolving 1673 * public id and system id. 1674 * This must be called before the first call to <code>parse()</code>. 1675 * @param entityResolver a class that implement the <code>EntityResolver</code> interface. 1676 */ 1677 public void setEntityResolver(EntityResolver entityResolver){ 1678 this.entityResolver = entityResolver; 1679 } 1680 1681 1682 /** 1683 * Return the Entity Resolver used by the SAX parser. 1684 * @return Return the Entity Resolver used by the SAX parser. 1685 */ 1686 public EntityResolver getEntityResolver(){ 1687 return entityResolver; 1688 } 1689 1690 /** 1691 * Resolve the requested external entity. 1692 * 1693 * @param publicId The public identifier of the entity being referenced 1694 * @param systemId The system identifier of the entity being referenced 1695 * 1696 * @exception SAXException if a parsing exception occurs 1697 * 1698 */ 1699 @Override 1700 public InputSource resolveEntity(String publicId, String systemId) 1701 throws SAXException { 1702 1703 if (saxLog.isDebugEnabled()) { 1704 saxLog.debug("resolveEntity('" + publicId + "', '" + systemId + "')"); 1705 } 1706 1707 if (publicId != null) 1708 this.publicId = publicId; 1709 1710 // Has this system identifier been registered? 1711 URL entityURL = null; 1712 if (publicId != null) { 1713 entityURL = entityValidator.get(publicId); 1714 } 1715 1716 // Redirect the schema location to a local destination 1717 if (schemaLocation != null && entityURL == null && systemId != null){ 1718 entityURL = entityValidator.get(systemId); 1719 } 1720 1721 if (entityURL == null) { 1722 if (systemId == null) { 1723 // cannot resolve 1724 if (log.isDebugEnabled()) { 1725 log.debug(" Cannot resolve null entity, returning null InputSource"); 1726 } 1727 return (null); 1728 1729 } else { 1730 // try to resolve using system ID 1731 if (log.isDebugEnabled()) { 1732 log.debug(" Trying to resolve using system ID '" + systemId + "'"); 1733 } 1734 try { 1735 entityURL = new URL(systemId); 1736 } catch (MalformedURLException e) { 1737 throw new IllegalArgumentException("Malformed URL '" + systemId 1738 + "' : " + e.getMessage()); 1739 } 1740 } 1741 } 1742 1743 // Return an input source to our alternative URL 1744 if (log.isDebugEnabled()) { 1745 log.debug(" Resolving to alternate DTD '" + entityURL + "'"); 1746 } 1747 1748 try { 1749 return createInputSourceFromURL(entityURL); 1750 } catch (Exception e) { 1751 throw createSAXException(e); 1752 } 1753 } 1754 1755 1756 // ------------------------------------------------- ErrorHandler Methods 1757 1758 1759 /** 1760 * Forward notification of a parsing error to the application supplied 1761 * error handler (if any). 1762 * 1763 * @param exception The error information 1764 * 1765 * @exception SAXException if a parsing exception occurs 1766 */ 1767 @Override 1768 public void error(SAXParseException exception) throws SAXException { 1769 1770 log.error("Parse Error at line " + exception.getLineNumber() + 1771 " column " + exception.getColumnNumber() + ": " + 1772 exception.getMessage(), exception); 1773 if (errorHandler != null) { 1774 errorHandler.error(exception); 1775 } 1776 1777 } 1778 1779 1780 /** 1781 * Forward notification of a fatal parsing error to the application 1782 * supplied error handler (if any). 1783 * 1784 * @param exception The fatal error information 1785 * 1786 * @exception SAXException if a parsing exception occurs 1787 */ 1788 @Override 1789 public void fatalError(SAXParseException exception) throws SAXException { 1790 1791 log.error("Parse Fatal Error at line " + exception.getLineNumber() + 1792 " column " + exception.getColumnNumber() + ": " + 1793 exception.getMessage(), exception); 1794 if (errorHandler != null) { 1795 errorHandler.fatalError(exception); 1796 } 1797 1798 } 1799 1800 1801 /** 1802 * Forward notification of a parse warning to the application supplied 1803 * error handler (if any). 1804 * 1805 * @param exception The warning information 1806 * 1807 * @exception SAXException if a parsing exception occurs 1808 */ 1809 @Override 1810 public void warning(SAXParseException exception) throws SAXException { 1811 if (errorHandler != null) { 1812 log.warn("Parse Warning Error at line " + exception.getLineNumber() + 1813 " column " + exception.getColumnNumber() + ": " + 1814 exception.getMessage(), exception); 1815 1816 errorHandler.warning(exception); 1817 } 1818 1819 } 1820 1821 1822 // ------------------------------------------------------- Public Methods 1823 1824 1825 /** 1826 * Log a message to our associated logger. 1827 * 1828 * @param message The message to be logged 1829 * @deprecated Call getLogger() and use it's logging methods 1830 */ 1831 @Deprecated 1832 public void log(String message) { 1833 1834 log.info(message); 1835 1836 } 1837 1838 1839 /** 1840 * Log a message and exception to our associated logger. 1841 * 1842 * @param message The message to be logged 1843 * @deprecated Call getLogger() and use it's logging methods 1844 */ 1845 @Deprecated 1846 public void log(String message, Throwable exception) { 1847 1848 log.error(message, exception); 1849 1850 } 1851 1852 1853 /** 1854 * Parse the content of the specified file using this Digester. Returns 1855 * the root element from the object stack (if any). 1856 * 1857 * @param file File containing the XML data to be parsed 1858 * 1859 * @exception IOException if an input/output error occurs 1860 * @exception SAXException if a parsing exception occurs 1861 */ 1862 public Object parse(File file) throws IOException, SAXException { 1863 1864 if (file == null) { 1865 throw new IllegalArgumentException("File to parse is null"); 1866 } 1867 1868 configure(); 1869 InputSource input = new InputSource(new FileInputStream(file)); 1870 input.setSystemId(file.toURI().toURL().toString()); 1871 getXMLReader().parse(input); 1872 cleanup(); 1873 return (root); 1874 1875 } 1876 /** 1877 * Parse the content of the specified input source using this Digester. 1878 * Returns the root element from the object stack (if any). 1879 * 1880 * @param input Input source containing the XML data to be parsed 1881 * 1882 * @exception IOException if an input/output error occurs 1883 * @exception SAXException if a parsing exception occurs 1884 */ 1885 public Object parse(InputSource input) throws IOException, SAXException { 1886 1887 if (input == null) { 1888 throw new IllegalArgumentException("InputSource to parse is null"); 1889 } 1890 1891 configure(); 1892 getXMLReader().parse(input); 1893 cleanup(); 1894 return (root); 1895 1896 } 1897 1898 1899 /** 1900 * Parse the content of the specified input stream using this Digester. 1901 * Returns the root element from the object stack (if any). 1902 * 1903 * @param input Input stream containing the XML data to be parsed 1904 * 1905 * @exception IOException if an input/output error occurs 1906 * @exception SAXException if a parsing exception occurs 1907 */ 1908 public Object parse(InputStream input) throws IOException, SAXException { 1909 1910 if (input == null) { 1911 throw new IllegalArgumentException("InputStream to parse is null"); 1912 } 1913 1914 configure(); 1915 InputSource is = new InputSource(input); 1916 getXMLReader().parse(is); 1917 cleanup(); 1918 return (root); 1919 1920 } 1921 1922 1923 /** 1924 * Parse the content of the specified reader using this Digester. 1925 * Returns the root element from the object stack (if any). 1926 * 1927 * @param reader Reader containing the XML data to be parsed 1928 * 1929 * @exception IOException if an input/output error occurs 1930 * @exception SAXException if a parsing exception occurs 1931 */ 1932 public Object parse(Reader reader) throws IOException, SAXException { 1933 1934 if (reader == null) { 1935 throw new IllegalArgumentException("Reader to parse is null"); 1936 } 1937 1938 configure(); 1939 InputSource is = new InputSource(reader); 1940 getXMLReader().parse(is); 1941 cleanup(); 1942 return (root); 1943 1944 } 1945 1946 1947 /** 1948 * Parse the content of the specified URI using this Digester. 1949 * Returns the root element from the object stack (if any). 1950 * 1951 * @param uri URI containing the XML data to be parsed 1952 * 1953 * @exception IOException if an input/output error occurs 1954 * @exception SAXException if a parsing exception occurs 1955 */ 1956 public Object parse(String uri) throws IOException, SAXException { 1957 1958 if (uri == null) { 1959 throw new IllegalArgumentException("String URI to parse is null"); 1960 } 1961 1962 configure(); 1963 InputSource is = createInputSourceFromURL(uri); 1964 getXMLReader().parse(is); 1965 cleanup(); 1966 return (root); 1967 1968 } 1969 1970 1971 /** 1972 * Parse the content of the specified URL using this Digester. 1973 * Returns the root element from the object stack (if any). 1974 * 1975 * @param url URL containing the XML data to be parsed 1976 * 1977 * @exception IOException if an input/output error occurs 1978 * @exception SAXException if a parsing exception occurs 1979 * 1980 * @since 1.8 1981 */ 1982 public Object parse(URL url) throws IOException, SAXException { 1983 1984 if (url == null) { 1985 throw new IllegalArgumentException("URL to parse is null"); 1986 } 1987 1988 configure(); 1989 InputSource is = createInputSourceFromURL(url); 1990 getXMLReader().parse(is); 1991 cleanup(); 1992 return (root); 1993 1994 } 1995 1996 1997 /** 1998 * <p>Register the specified DTD URL for the specified public identifier. 1999 * This must be called before the first call to <code>parse()</code>. 2000 * </p><p> 2001 * <code>Digester</code> contains an internal <code>EntityResolver</code> 2002 * implementation. This maps <code>PUBLICID</code>'s to URLs 2003 * (from which the resource will be loaded). A common use case for this 2004 * method is to register local URLs (possibly computed at runtime by a 2005 * classloader) for DTDs. This allows the performance advantage of using 2006 * a local version without having to ensure every <code>SYSTEM</code> 2007 * URI on every processed xml document is local. This implementation provides 2008 * only basic functionality. If more sophisticated features are required, 2009 * using {@link #setEntityResolver} to set a custom resolver is recommended. 2010 * </p><p> 2011 * <strong>Note:</strong> This method will have no effect when a custom 2012 * <code>EntityResolver</code> has been set. (Setting a custom 2013 * <code>EntityResolver</code> overrides the internal implementation.) 2014 * </p> 2015 * @param publicId Public identifier of the DTD to be resolved 2016 * @param entityURL The URL to use for reading this DTD 2017 * 2018 * @since 1.8 2019 */ 2020 public void register(String publicId, URL entityURL) { 2021 2022 if (log.isDebugEnabled()) { 2023 log.debug("register('" + publicId + "', '" + entityURL + "'"); 2024 } 2025 entityValidator.put(publicId, entityURL); 2026 2027 } 2028 2029 2030 /** 2031 * <p>Convenience method that registers the string version of an entity URL 2032 * instead of a URL version.</p> 2033 * 2034 * @param publicId Public identifier of the entity to be resolved 2035 * @param entityURL The URL to use for reading this entity 2036 */ 2037 public void register(String publicId, String entityURL) { 2038 2039 if (log.isDebugEnabled()) { 2040 log.debug("register('" + publicId + "', '" + entityURL + "'"); 2041 } 2042 try { 2043 entityValidator.put(publicId, new URL(entityURL)); 2044 } catch (MalformedURLException e) { 2045 throw new IllegalArgumentException("Malformed URL '" + entityURL 2046 + "' : " + e.getMessage()); 2047 } 2048 2049 } 2050 2051 2052 /** 2053 * <p><code>List</code> of <code>InputSource</code> instances 2054 * created by a <code>createInputSourceFromURL()</code> method 2055 * call. These represent open input streams that need to be 2056 * closed to avoid resource leaks, as well as potentially locked 2057 * JAR files on Windows.</p> 2058 */ 2059 protected List<InputSource> inputSources = new ArrayList<InputSource>(5); 2060 2061 2062 /** 2063 * Given a URL, return an InputSource that reads from that URL. 2064 * <p> 2065 * Ideally this function would not be needed and code could just use 2066 * <code>new InputSource(entityURL)</code>. Unfortunately it appears 2067 * that when the entityURL points to a file within a jar archive a 2068 * caching mechanism inside the InputSource implementation causes a 2069 * file-handle to the jar file to remain open. On Windows systems 2070 * this then causes the jar archive file to be locked on disk 2071 * ("in use") which makes it impossible to delete the jar file - 2072 * and that really stuffs up "undeploy" in webapps in particular. 2073 * <p> 2074 * In JDK1.4 and later, Apache XercesJ is used as the xml parser. 2075 * The InputSource object provided is converted into an XMLInputSource, 2076 * and eventually passed to an instance of XMLDocumentScannerImpl to 2077 * specify the source data to be converted into tokens for the rest 2078 * of the XMLReader code to handle. XMLDocumentScannerImpl calls 2079 * fEntityManager.startDocumentEntity(source), where fEntityManager 2080 * is declared in ancestor class XMLScanner to be an XMLEntityManager. In 2081 * that class, if the input source stream is null, then: 2082 * <pre> 2083 * URL location = new URL(expandedSystemId); 2084 * URLConnection connect = location.openConnection(); 2085 * if (connect instanceof HttpURLConnection) { 2086 * setHttpProperties(connect,xmlInputSource); 2087 * } 2088 * stream = connect.getInputStream(); 2089 * </pre> 2090 * This method pretty much duplicates the standard behaviour, except 2091 * that it calls URLConnection.setUseCaches(false) before opening 2092 * the connection. 2093 * 2094 * @since 1.8 2095 */ 2096 public InputSource createInputSourceFromURL(URL url) 2097 throws MalformedURLException, IOException { 2098 2099 URLConnection connection = url.openConnection(); 2100 connection.setUseCaches(false); 2101 InputStream stream = connection.getInputStream(); 2102 InputSource source = new InputSource(stream); 2103 source.setSystemId(url.toExternalForm()); 2104 inputSources.add(source); 2105 return source; 2106 2107 } 2108 2109 2110 /** 2111 * <p>Convenience method that creates an <code>InputSource</code> 2112 * from the string version of a URL.</p> 2113 * 2114 * @param url URL for which to create an <code>InputSource</code> 2115 * 2116 * @since 1.8 2117 */ 2118 public InputSource createInputSourceFromURL(String url) 2119 throws MalformedURLException, IOException { 2120 2121 return createInputSourceFromURL(new URL(url)); 2122 2123 } 2124 2125 2126 // --------------------------------------------------------- Rule Methods 2127 2128 2129 /** 2130 * <p>Register a new Rule matching the specified pattern. 2131 * This method sets the <code>Digester</code> property on the rule.</p> 2132 * 2133 * @param pattern Element matching pattern 2134 * @param rule Rule to be registered 2135 */ 2136 public void addRule(String pattern, Rule rule) { 2137 2138 rule.setDigester(this); 2139 getRules().add(pattern, rule); 2140 2141 } 2142 2143 2144 /** 2145 * Register a set of Rule instances defined in a RuleSet. 2146 * 2147 * @param ruleSet The RuleSet instance to configure from 2148 */ 2149 public void addRuleSet(RuleSet ruleSet) { 2150 2151 String oldNamespaceURI = getRuleNamespaceURI(); 2152 String newNamespaceURI = ruleSet.getNamespaceURI(); 2153 if (log.isDebugEnabled()) { 2154 if (newNamespaceURI == null) { 2155 log.debug("addRuleSet() with no namespace URI"); 2156 } else { 2157 log.debug("addRuleSet() with namespace URI " + newNamespaceURI); 2158 } 2159 } 2160 setRuleNamespaceURI(newNamespaceURI); 2161 ruleSet.addRuleInstances(this); 2162 setRuleNamespaceURI(oldNamespaceURI); 2163 2164 } 2165 2166 2167 /** 2168 * Add a "bean property setter" rule for the specified parameters. 2169 * 2170 * @param pattern Element matching pattern 2171 * @see BeanPropertySetterRule 2172 */ 2173 public void addBeanPropertySetter(String pattern) { 2174 2175 addRule(pattern, 2176 new BeanPropertySetterRule()); 2177 2178 } 2179 2180 2181 /** 2182 * Add a "bean property setter" rule for the specified parameters. 2183 * 2184 * @param pattern Element matching pattern 2185 * @param propertyName Name of property to set 2186 * @see BeanPropertySetterRule 2187 */ 2188 public void addBeanPropertySetter(String pattern, 2189 String propertyName) { 2190 2191 addRule(pattern, 2192 new BeanPropertySetterRule(propertyName)); 2193 2194 } 2195 2196 /** 2197 * Add an "call method" rule for a method which accepts no arguments. 2198 * 2199 * @param pattern Element matching pattern 2200 * @param methodName Method name to be called 2201 * @see CallMethodRule 2202 */ 2203 public void addCallMethod(String pattern, String methodName) { 2204 2205 addRule( 2206 pattern, 2207 new CallMethodRule(methodName)); 2208 2209 } 2210 2211 /** 2212 * Add an "call method" rule for the specified parameters. 2213 * 2214 * @param pattern Element matching pattern 2215 * @param methodName Method name to be called 2216 * @param paramCount Number of expected parameters (or zero 2217 * for a single parameter from the body of this element) 2218 * @see CallMethodRule 2219 */ 2220 public void addCallMethod(String pattern, String methodName, 2221 int paramCount) { 2222 2223 addRule(pattern, 2224 new CallMethodRule(methodName, paramCount)); 2225 2226 } 2227 2228 2229 /** 2230 * Add an "call method" rule for the specified parameters. 2231 * If <code>paramCount</code> is set to zero the rule will use 2232 * the body of the matched element as the single argument of the 2233 * method, unless <code>paramTypes</code> is null or empty, in this 2234 * case the rule will call the specified method with no arguments. 2235 * 2236 * @param pattern Element matching pattern 2237 * @param methodName Method name to be called 2238 * @param paramCount Number of expected parameters (or zero 2239 * for a single parameter from the body of this element) 2240 * @param paramTypes Set of Java class names for the types 2241 * of the expected parameters 2242 * (if you wish to use a primitive type, specify the corresonding 2243 * Java wrapper class instead, such as <code>java.lang.Boolean</code> 2244 * for a <code>boolean</code> parameter) 2245 * @see CallMethodRule 2246 */ 2247 public void addCallMethod(String pattern, String methodName, 2248 int paramCount, String paramTypes[]) { 2249 2250 addRule(pattern, 2251 new CallMethodRule( 2252 methodName, 2253 paramCount, 2254 paramTypes)); 2255 2256 } 2257 2258 2259 /** 2260 * Add an "call method" rule for the specified parameters. 2261 * If <code>paramCount</code> is set to zero the rule will use 2262 * the body of the matched element as the single argument of the 2263 * method, unless <code>paramTypes</code> is null or empty, in this 2264 * case the rule will call the specified method with no arguments. 2265 * 2266 * @param pattern Element matching pattern 2267 * @param methodName Method name to be called 2268 * @param paramCount Number of expected parameters (or zero 2269 * for a single parameter from the body of this element) 2270 * @param paramTypes The Java class names of the arguments 2271 * (if you wish to use a primitive type, specify the corresonding 2272 * Java wrapper class instead, such as <code>java.lang.Boolean</code> 2273 * for a <code>boolean</code> parameter) 2274 * @see CallMethodRule 2275 */ 2276 public void addCallMethod(String pattern, String methodName, 2277 int paramCount, Class<?> paramTypes[]) { 2278 2279 addRule(pattern, 2280 new CallMethodRule( 2281 methodName, 2282 paramCount, 2283 paramTypes)); 2284 2285 } 2286 2287 2288 /** 2289 * Add a "call parameter" rule for the specified parameters. 2290 * 2291 * @param pattern Element matching pattern 2292 * @param paramIndex Zero-relative parameter index to set 2293 * (from the body of this element) 2294 * @see CallParamRule 2295 */ 2296 public void addCallParam(String pattern, int paramIndex) { 2297 2298 addRule(pattern, 2299 new CallParamRule(paramIndex)); 2300 2301 } 2302 2303 2304 /** 2305 * Add a "call parameter" rule for the specified parameters. 2306 * 2307 * @param pattern Element matching pattern 2308 * @param paramIndex Zero-relative parameter index to set 2309 * (from the specified attribute) 2310 * @param attributeName Attribute whose value is used as the 2311 * parameter value 2312 * @see CallParamRule 2313 */ 2314 public void addCallParam(String pattern, int paramIndex, 2315 String attributeName) { 2316 2317 addRule(pattern, 2318 new CallParamRule(paramIndex, attributeName)); 2319 2320 } 2321 2322 2323 /** 2324 * Add a "call parameter" rule. 2325 * This will either take a parameter from the stack 2326 * or from the current element body text. 2327 * 2328 * @param paramIndex The zero-relative parameter number 2329 * @param fromStack Should the call parameter be taken from the top of the stack? 2330 * @see CallParamRule 2331 */ 2332 public void addCallParam(String pattern, int paramIndex, boolean fromStack) { 2333 2334 addRule(pattern, 2335 new CallParamRule(paramIndex, fromStack)); 2336 2337 } 2338 2339 /** 2340 * Add a "call parameter" rule that sets a parameter from the stack. 2341 * This takes a parameter from the given position on the stack. 2342 * 2343 * @param paramIndex The zero-relative parameter number 2344 * @param stackIndex set the call parameter to the stackIndex'th object down the stack, 2345 * where 0 is the top of the stack, 1 the next element down and so on 2346 * @see CallMethodRule 2347 */ 2348 public void addCallParam(String pattern, int paramIndex, int stackIndex) { 2349 2350 addRule(pattern, 2351 new CallParamRule(paramIndex, stackIndex)); 2352 2353 } 2354 2355 /** 2356 * Add a "call parameter" rule that sets a parameter from the current 2357 * <code>Digester</code> matching path. 2358 * This is sometimes useful when using rules that support wildcards. 2359 * 2360 * @param pattern the pattern that this rule should match 2361 * @param paramIndex The zero-relative parameter number 2362 * @see CallMethodRule 2363 */ 2364 public void addCallParamPath(String pattern,int paramIndex) { 2365 addRule(pattern, new PathCallParamRule(paramIndex)); 2366 } 2367 2368 /** 2369 * Add a "call parameter" rule that sets a parameter from a 2370 * caller-provided object. This can be used to pass constants such as 2371 * strings to methods; it can also be used to pass mutable objects, 2372 * providing ways for objects to do things like "register" themselves 2373 * with some shared object. 2374 * <p> 2375 * Note that when attempting to locate a matching method to invoke, 2376 * the true type of the paramObj is used, so that despite the paramObj 2377 * being passed in here as type Object, the target method can declare 2378 * its parameters as being the true type of the object (or some ancestor 2379 * type, according to the usual type-conversion rules). 2380 * 2381 * @param paramIndex The zero-relative parameter number 2382 * @param paramObj Any arbitrary object to be passed to the target 2383 * method. 2384 * @see CallMethodRule 2385 * 2386 * @since 1.6 2387 */ 2388 public void addObjectParam(String pattern, int paramIndex, 2389 Object paramObj) { 2390 2391 addRule(pattern, 2392 new ObjectParamRule(paramIndex, paramObj)); 2393 2394 } 2395 2396 /** 2397 * Add a "factory create" rule for the specified parameters. 2398 * Exceptions thrown during the object creation process will be propagated. 2399 * 2400 * @param pattern Element matching pattern 2401 * @param className Java class name of the object creation factory class 2402 * @see FactoryCreateRule 2403 */ 2404 public void addFactoryCreate(String pattern, String className) { 2405 2406 addFactoryCreate(pattern, className, false); 2407 2408 } 2409 2410 2411 /** 2412 * Add a "factory create" rule for the specified parameters. 2413 * Exceptions thrown during the object creation process will be propagated. 2414 * 2415 * @param pattern Element matching pattern 2416 * @param clazz Java class of the object creation factory class 2417 * @see FactoryCreateRule 2418 */ 2419 public void addFactoryCreate(String pattern, Class<?> clazz) { 2420 2421 addFactoryCreate(pattern, clazz, false); 2422 2423 } 2424 2425 2426 /** 2427 * Add a "factory create" rule for the specified parameters. 2428 * Exceptions thrown during the object creation process will be propagated. 2429 * 2430 * @param pattern Element matching pattern 2431 * @param className Java class name of the object creation factory class 2432 * @param attributeName Attribute name which, if present, overrides the 2433 * value specified by <code>className</code> 2434 * @see FactoryCreateRule 2435 */ 2436 public void addFactoryCreate(String pattern, String className, 2437 String attributeName) { 2438 2439 addFactoryCreate(pattern, className, attributeName, false); 2440 2441 } 2442 2443 2444 /** 2445 * Add a "factory create" rule for the specified parameters. 2446 * Exceptions thrown during the object creation process will be propagated. 2447 * 2448 * @param pattern Element matching pattern 2449 * @param clazz Java class of the object creation factory class 2450 * @param attributeName Attribute name which, if present, overrides the 2451 * value specified by <code>className</code> 2452 * @see FactoryCreateRule 2453 */ 2454 public void addFactoryCreate(String pattern, Class<?> clazz, 2455 String attributeName) { 2456 2457 addFactoryCreate(pattern, clazz, attributeName, false); 2458 2459 } 2460 2461 2462 /** 2463 * Add a "factory create" rule for the specified parameters. 2464 * Exceptions thrown during the object creation process will be propagated. 2465 * 2466 * @param pattern Element matching pattern 2467 * @param creationFactory Previously instantiated ObjectCreationFactory 2468 * to be utilized 2469 * @see FactoryCreateRule 2470 */ 2471 public void addFactoryCreate(String pattern, 2472 ObjectCreationFactory creationFactory) { 2473 2474 addFactoryCreate(pattern, creationFactory, false); 2475 2476 } 2477 2478 /** 2479 * Add a "factory create" rule for the specified parameters. 2480 * 2481 * @param pattern Element matching pattern 2482 * @param className Java class name of the object creation factory class 2483 * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during 2484 * object creation will be ignored. 2485 * @see FactoryCreateRule 2486 */ 2487 public void addFactoryCreate( 2488 String pattern, 2489 String className, 2490 boolean ignoreCreateExceptions) { 2491 2492 addRule( 2493 pattern, 2494 new FactoryCreateRule(className, ignoreCreateExceptions)); 2495 2496 } 2497 2498 2499 /** 2500 * Add a "factory create" rule for the specified parameters. 2501 * 2502 * @param pattern Element matching pattern 2503 * @param clazz Java class of the object creation factory class 2504 * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during 2505 * object creation will be ignored. 2506 * @see FactoryCreateRule 2507 */ 2508 public void addFactoryCreate( 2509 String pattern, 2510 Class<?> clazz, 2511 boolean ignoreCreateExceptions) { 2512 2513 addRule( 2514 pattern, 2515 new FactoryCreateRule(clazz, ignoreCreateExceptions)); 2516 2517 } 2518 2519 2520 /** 2521 * Add a "factory create" rule for the specified parameters. 2522 * 2523 * @param pattern Element matching pattern 2524 * @param className Java class name of the object creation factory class 2525 * @param attributeName Attribute name which, if present, overrides the 2526 * value specified by <code>className</code> 2527 * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during 2528 * object creation will be ignored. 2529 * @see FactoryCreateRule 2530 */ 2531 public void addFactoryCreate( 2532 String pattern, 2533 String className, 2534 String attributeName, 2535 boolean ignoreCreateExceptions) { 2536 2537 addRule( 2538 pattern, 2539 new FactoryCreateRule(className, attributeName, ignoreCreateExceptions)); 2540 2541 } 2542 2543 2544 /** 2545 * Add a "factory create" rule for the specified parameters. 2546 * 2547 * @param pattern Element matching pattern 2548 * @param clazz Java class of the object creation factory class 2549 * @param attributeName Attribute name which, if present, overrides the 2550 * value specified by <code>className</code> 2551 * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during 2552 * object creation will be ignored. 2553 * @see FactoryCreateRule 2554 */ 2555 public void addFactoryCreate( 2556 String pattern, 2557 Class<?> clazz, 2558 String attributeName, 2559 boolean ignoreCreateExceptions) { 2560 2561 addRule( 2562 pattern, 2563 new FactoryCreateRule(clazz, attributeName, ignoreCreateExceptions)); 2564 2565 } 2566 2567 2568 /** 2569 * Add a "factory create" rule for the specified parameters. 2570 * 2571 * @param pattern Element matching pattern 2572 * @param creationFactory Previously instantiated ObjectCreationFactory 2573 * to be utilized 2574 * @param ignoreCreateExceptions when <code>true</code> any exceptions thrown during 2575 * object creation will be ignored. 2576 * @see FactoryCreateRule 2577 */ 2578 public void addFactoryCreate(String pattern, 2579 ObjectCreationFactory creationFactory, 2580 boolean ignoreCreateExceptions) { 2581 2582 creationFactory.setDigester(this); 2583 addRule(pattern, 2584 new FactoryCreateRule(creationFactory, ignoreCreateExceptions)); 2585 2586 } 2587 2588 /** 2589 * Add an "object create" rule for the specified parameters. 2590 * 2591 * @param pattern Element matching pattern 2592 * @param className Java class name to be created 2593 * @see ObjectCreateRule 2594 */ 2595 public void addObjectCreate(String pattern, String className) { 2596 2597 addRule(pattern, 2598 new ObjectCreateRule(className)); 2599 2600 } 2601 2602 2603 /** 2604 * Add an "object create" rule for the specified parameters. 2605 * 2606 * @param pattern Element matching pattern 2607 * @param clazz Java class to be created 2608 * @see ObjectCreateRule 2609 */ 2610 public void addObjectCreate(String pattern, Class<?> clazz) { 2611 2612 addRule(pattern, 2613 new ObjectCreateRule(clazz)); 2614 2615 } 2616 2617 2618 /** 2619 * Add an "object create" rule for the specified parameters. 2620 * 2621 * @param pattern Element matching pattern 2622 * @param className Default Java class name to be created 2623 * @param attributeName Attribute name that optionally overrides 2624 * the default Java class name to be created 2625 * @see ObjectCreateRule 2626 */ 2627 public void addObjectCreate(String pattern, String className, 2628 String attributeName) { 2629 2630 addRule(pattern, 2631 new ObjectCreateRule(className, attributeName)); 2632 2633 } 2634 2635 2636 /** 2637 * Add an "object create" rule for the specified parameters. 2638 * 2639 * @param pattern Element matching pattern 2640 * @param attributeName Attribute name that optionally overrides 2641 * @param clazz Default Java class to be created 2642 * the default Java class name to be created 2643 * @see ObjectCreateRule 2644 */ 2645 public void addObjectCreate(String pattern, 2646 String attributeName, 2647 Class<?> clazz) { 2648 2649 addRule(pattern, 2650 new ObjectCreateRule(attributeName, clazz)); 2651 2652 } 2653 2654 /** 2655 * Adds an {@link SetNestedPropertiesRule}. 2656 * 2657 * @param pattern register the rule with this pattern 2658 * 2659 * @since 1.6 2660 */ 2661 public void addSetNestedProperties(String pattern) { 2662 2663 addRule(pattern, new SetNestedPropertiesRule()); 2664 } 2665 2666 /** 2667 * Adds an {@link SetNestedPropertiesRule}. 2668 * 2669 * @param pattern register the rule with this pattern 2670 * @param elementName elment name that a property maps to 2671 * @param propertyName property name of the element mapped from 2672 * 2673 * @since 1.6 2674 */ 2675 public void addSetNestedProperties(String pattern, String elementName, String propertyName) { 2676 2677 addRule(pattern, new SetNestedPropertiesRule(elementName, propertyName)); 2678 } 2679 2680 /** 2681 * Adds an {@link SetNestedPropertiesRule}. 2682 * 2683 * @param pattern register the rule with this pattern 2684 * @param elementNames elment names that (in order) map to properties 2685 * @param propertyNames property names that (in order) elements are mapped to 2686 * 2687 * @since 1.6 2688 */ 2689 public void addSetNestedProperties(String pattern, String[] elementNames, String[] propertyNames) { 2690 2691 addRule(pattern, new SetNestedPropertiesRule(elementNames, propertyNames)); 2692 } 2693 2694 2695 /** 2696 * Add a "set next" rule for the specified parameters. 2697 * 2698 * @param pattern Element matching pattern 2699 * @param methodName Method name to call on the parent element 2700 * @see SetNextRule 2701 */ 2702 public void addSetNext(String pattern, String methodName) { 2703 2704 addRule(pattern, 2705 new SetNextRule(methodName)); 2706 2707 } 2708 2709 2710 /** 2711 * Add a "set next" rule for the specified parameters. 2712 * 2713 * @param pattern Element matching pattern 2714 * @param methodName Method name to call on the parent element 2715 * @param paramType Java class name of the expected parameter type 2716 * (if you wish to use a primitive type, specify the corresonding 2717 * Java wrapper class instead, such as <code>java.lang.Boolean</code> 2718 * for a <code>boolean</code> parameter) 2719 * @see SetNextRule 2720 */ 2721 public void addSetNext(String pattern, String methodName, 2722 String paramType) { 2723 2724 addRule(pattern, 2725 new SetNextRule(methodName, paramType)); 2726 2727 } 2728 2729 2730 /** 2731 * Add {@link SetRootRule} with the specified parameters. 2732 * 2733 * @param pattern Element matching pattern 2734 * @param methodName Method name to call on the root object 2735 * @see SetRootRule 2736 */ 2737 public void addSetRoot(String pattern, String methodName) { 2738 2739 addRule(pattern, 2740 new SetRootRule(methodName)); 2741 2742 } 2743 2744 2745 /** 2746 * Add {@link SetRootRule} with the specified parameters. 2747 * 2748 * @param pattern Element matching pattern 2749 * @param methodName Method name to call on the root object 2750 * @param paramType Java class name of the expected parameter type 2751 * @see SetRootRule 2752 */ 2753 public void addSetRoot(String pattern, String methodName, 2754 String paramType) { 2755 2756 addRule(pattern, 2757 new SetRootRule(methodName, paramType)); 2758 2759 } 2760 2761 /** 2762 * Add a "set properties" rule for the specified parameters. 2763 * 2764 * @param pattern Element matching pattern 2765 * @see SetPropertiesRule 2766 */ 2767 public void addSetProperties(String pattern) { 2768 2769 addRule(pattern, 2770 new SetPropertiesRule()); 2771 2772 } 2773 2774 /** 2775 * Add a "set properties" rule with a single overridden parameter. 2776 * See {@link SetPropertiesRule#SetPropertiesRule(String attributeName, String propertyName)} 2777 * 2778 * @param pattern Element matching pattern 2779 * @param attributeName map this attribute 2780 * @param propertyName to this property 2781 * @see SetPropertiesRule 2782 */ 2783 public void addSetProperties( 2784 String pattern, 2785 String attributeName, 2786 String propertyName) { 2787 2788 addRule(pattern, 2789 new SetPropertiesRule(attributeName, propertyName)); 2790 2791 } 2792 2793 /** 2794 * Add a "set properties" rule with overridden parameters. 2795 * See {@link SetPropertiesRule#SetPropertiesRule(String [] attributeNames, String [] propertyNames)} 2796 * 2797 * @param pattern Element matching pattern 2798 * @param attributeNames names of attributes with custom mappings 2799 * @param propertyNames property names these attributes map to 2800 * @see SetPropertiesRule 2801 */ 2802 public void addSetProperties( 2803 String pattern, 2804 String [] attributeNames, 2805 String [] propertyNames) { 2806 2807 addRule(pattern, 2808 new SetPropertiesRule(attributeNames, propertyNames)); 2809 2810 } 2811 2812 2813 /** 2814 * Add a "set property" rule for the specified parameters. 2815 * 2816 * @param pattern Element matching pattern 2817 * @param name Attribute name containing the property name to be set 2818 * @param value Attribute name containing the property value to set 2819 * @see SetPropertyRule 2820 */ 2821 public void addSetProperty(String pattern, String name, String value) { 2822 2823 addRule(pattern, 2824 new SetPropertyRule(name, value)); 2825 2826 } 2827 2828 2829 /** 2830 * Add a "set top" rule for the specified parameters. 2831 * 2832 * @param pattern Element matching pattern 2833 * @param methodName Method name to call on the parent element 2834 * @see SetTopRule 2835 */ 2836 public void addSetTop(String pattern, String methodName) { 2837 2838 addRule(pattern, 2839 new SetTopRule(methodName)); 2840 2841 } 2842 2843 2844 /** 2845 * Add a "set top" rule for the specified parameters. 2846 * 2847 * @param pattern Element matching pattern 2848 * @param methodName Method name to call on the parent element 2849 * @param paramType Java class name of the expected parameter type 2850 * (if you wish to use a primitive type, specify the corresonding 2851 * Java wrapper class instead, such as <code>java.lang.Boolean</code> 2852 * for a <code>boolean</code> parameter) 2853 * @see SetTopRule 2854 */ 2855 public void addSetTop(String pattern, String methodName, 2856 String paramType) { 2857 2858 addRule(pattern, 2859 new SetTopRule(methodName, paramType)); 2860 2861 } 2862 2863 2864 // --------------------------------------------------- Object Stack Methods 2865 2866 2867 /** 2868 * Clear the current contents of the default object stack, the param stack, 2869 * all named stacks, and other internal variables. 2870 * <p> 2871 * Calling this method <i>might</i> allow another document of the same type 2872 * to be correctly parsed. However this method was not intended for this 2873 * purpose (just to tidy up memory usage). In general, a separate Digester 2874 * object should be created for each document to be parsed. 2875 * <p> 2876 * Note that this method is called automatically after a document has been 2877 * successfully parsed by a Digester instance. However it is not invoked 2878 * automatically when a parse fails, so when reusing a Digester instance 2879 * (which is not recommended) this method <i>must</i> be called manually 2880 * after a parse failure. 2881 */ 2882 public void clear() { 2883 2884 match = ""; 2885 bodyTexts.clear(); 2886 params.clear(); 2887 publicId = null; 2888 stack.clear(); 2889 stacksByName.clear(); 2890 customContentHandler = null; 2891 } 2892 2893 2894 /** 2895 * Return the top object on the stack without removing it. If there are 2896 * no objects on the stack, return <code>null</code>. 2897 */ 2898 public Object peek() { 2899 2900 try { 2901 return (stack.peek()); 2902 } catch (EmptyStackException e) { 2903 log.warn("Empty stack (returning null)"); 2904 return (null); 2905 } 2906 2907 } 2908 2909 2910 /** 2911 * Return the n'th object down the stack, where 0 is the top element 2912 * and [getCount()-1] is the bottom element. If the specified index 2913 * is out of range, return <code>null</code>. 2914 * 2915 * @param n Index of the desired element, where 0 is the top of the stack, 2916 * 1 is the next element down, and so on. 2917 */ 2918 public Object peek(int n) { 2919 2920 int index = (stack.size() - 1) - n; 2921 if (index < 0) { 2922 log.warn("Empty stack (returning null)"); 2923 return (null); 2924 } 2925 try { 2926 return (stack.get(index)); 2927 } catch (EmptyStackException e) { 2928 log.warn("Empty stack (returning null)"); 2929 return (null); 2930 } 2931 2932 } 2933 2934 2935 /** 2936 * Pop the top object off of the stack, and return it. If there are 2937 * no objects on the stack, return <code>null</code>. 2938 */ 2939 public Object pop() { 2940 2941 try { 2942 Object popped = stack.pop(); 2943 if (stackAction != null) { 2944 popped = stackAction.onPop(this, null, popped); 2945 } 2946 return popped; 2947 } catch (EmptyStackException e) { 2948 log.warn("Empty stack (returning null)"); 2949 return (null); 2950 } 2951 2952 } 2953 2954 2955 /** 2956 * Push a new object onto the top of the object stack. 2957 * 2958 * @param object The new object 2959 */ 2960 public void push(Object object) { 2961 2962 if (stackAction != null) { 2963 object = stackAction.onPush(this, null, object); 2964 } 2965 2966 if (stack.size() == 0) { 2967 root = object; 2968 } 2969 stack.push(object); 2970 } 2971 2972 /** 2973 * Pushes the given object onto the stack with the given name. 2974 * If no stack already exists with the given name then one will be created. 2975 * 2976 * @param stackName the name of the stack onto which the object should be pushed 2977 * @param value the Object to be pushed onto the named stack. 2978 * 2979 * @since 1.6 2980 */ 2981 public void push(String stackName, Object value) { 2982 if (stackAction != null) { 2983 value = stackAction.onPush(this, stackName, value); 2984 } 2985 2986 Stack<Object> namedStack = stacksByName.get(stackName); 2987 if (namedStack == null) { 2988 namedStack = new Stack<Object>(); 2989 stacksByName.put(stackName, namedStack); 2990 } 2991 namedStack.push(value); 2992 } 2993 2994 /** 2995 * <p>Pops (gets and removes) the top object from the stack with the given name.</p> 2996 * 2997 * <p><strong>Note:</strong> a stack is considered empty 2998 * if no objects have been pushed onto it yet.</p> 2999 * 3000 * @param stackName the name of the stack from which the top value is to be popped. 3001 * @return the top <code>Object</code> on the stack or or null if the stack is either 3002 * empty or has not been created yet 3003 * @throws EmptyStackException if the named stack is empty 3004 * 3005 * @since 1.6 3006 */ 3007 public Object pop(String stackName) { 3008 Object result = null; 3009 Stack<Object> namedStack = stacksByName.get(stackName); 3010 if (namedStack == null) { 3011 if (log.isDebugEnabled()) { 3012 log.debug("Stack '" + stackName + "' is empty"); 3013 } 3014 throw new EmptyStackException(); 3015 } 3016 3017 result = namedStack.pop(); 3018 3019 if (stackAction != null) { 3020 result = stackAction.onPop(this, stackName, result); 3021 } 3022 3023 return result; 3024 } 3025 3026 /** 3027 * <p>Gets the top object from the stack with the given name. 3028 * This method does not remove the object from the stack. 3029 * </p> 3030 * <p><strong>Note:</strong> a stack is considered empty 3031 * if no objects have been pushed onto it yet.</p> 3032 * 3033 * @param stackName the name of the stack to be peeked 3034 * @return the top <code>Object</code> on the stack or null if the stack is either 3035 * empty or has not been created yet 3036 * @throws EmptyStackException if the named stack is empty 3037 * 3038 * @since 1.6 3039 */ 3040 public Object peek(String stackName) { 3041 return peek(stackName, 0); 3042 } 3043 3044 /** 3045 * <p>Gets the top object from the stack with the given name. 3046 * This method does not remove the object from the stack. 3047 * </p> 3048 * <p><strong>Note:</strong> a stack is considered empty 3049 * if no objects have been pushed onto it yet.</p> 3050 * 3051 * @param stackName the name of the stack to be peeked 3052 * @param n Index of the desired element, where 0 is the top of the stack, 3053 * 1 is the next element down, and so on. 3054 * @return the specified <code>Object</code> on the stack. 3055 * @throws EmptyStackException if the named stack is empty 3056 * 3057 * @since 1.6 3058 */ 3059 public Object peek(String stackName, int n) { 3060 Object result = null; 3061 Stack<Object> namedStack = stacksByName.get(stackName); 3062 if (namedStack == null ) { 3063 if (log.isDebugEnabled()) { 3064 log.debug("Stack '" + stackName + "' is empty"); 3065 } 3066 throw new EmptyStackException(); 3067 3068 } else { 3069 int index = (namedStack.size() - 1) - n; 3070 if (index < 0) { 3071 throw new EmptyStackException(); 3072 } 3073 result = namedStack.get(index); 3074 } 3075 return result; 3076 } 3077 3078 /** 3079 * <p>Is the stack with the given name empty?</p> 3080 * <p><strong>Note:</strong> a stack is considered empty 3081 * if no objects have been pushed onto it yet.</p> 3082 * @param stackName the name of the stack whose emptiness 3083 * should be evaluated 3084 * @return true if the given stack if empty 3085 * 3086 * @since 1.6 3087 */ 3088 public boolean isEmpty(String stackName) { 3089 boolean result = true; 3090 Stack<Object> namedStack = stacksByName.get(stackName); 3091 if (namedStack != null ) { 3092 result = namedStack.isEmpty(); 3093 } 3094 return result; 3095 } 3096 3097 /** 3098 * Returns the root element of the tree of objects created as a result 3099 * of applying the rule objects to the input XML. 3100 * <p> 3101 * If the digester stack was "primed" by explicitly pushing a root 3102 * object onto the stack before parsing started, then that root object 3103 * is returned here. 3104 * <p> 3105 * Alternatively, if a Rule which creates an object (eg ObjectCreateRule) 3106 * matched the root element of the xml, then the object created will be 3107 * returned here. 3108 * <p> 3109 * In other cases, the object most recently pushed onto an empty digester 3110 * stack is returned. This would be a most unusual use of digester, however; 3111 * one of the previous configurations is much more likely. 3112 * <p> 3113 * Note that when using one of the Digester.parse methods, the return 3114 * value from the parse method is exactly the same as the return value 3115 * from this method. However when the Digester is being used as a 3116 * SAXContentHandler, no such return value is available; in this case, this 3117 * method allows you to access the root object that has been created 3118 * after parsing has completed. 3119 * 3120 * @return the root object that has been created after parsing 3121 * or null if the digester has not parsed any XML yet. 3122 */ 3123 public Object getRoot() { 3124 return root; 3125 } 3126 3127 /** 3128 * This method allows the "root" variable to be reset to null. 3129 * <p> 3130 * It is not considered safe for a digester instance to be reused 3131 * to parse multiple xml documents. However if you are determined to 3132 * do so, then you should call both clear() and resetRoot() before 3133 * each parse. 3134 * 3135 * @since 1.7 3136 */ 3137 public void resetRoot() { 3138 root = null; 3139 } 3140 3141 // ------------------------------------------------ Parameter Stack Methods 3142 3143 3144 // ------------------------------------------------------ Protected Methods 3145 3146 3147 /** 3148 * <p>Clean up allocated resources after parsing is complete. The 3149 * default method closes input streams that have been created by 3150 * Digester itself. If you override this method in a subclass, be 3151 * sure to call <code>super.cleanup()</code> to invoke this logic.</p> 3152 * 3153 * @since 1.8 3154 */ 3155 protected void cleanup() { 3156 3157 // If we created any InputSource objects in this instance, 3158 // they each have an input stream that should be closed 3159 for (InputSource source : inputSources) { 3160 try { 3161 source.getByteStream().close(); 3162 } catch (IOException e) { 3163 // Fall through so we get them all 3164 } 3165 } 3166 inputSources.clear(); 3167 3168 } 3169 3170 3171 /** 3172 * <p> 3173 * Provide a hook for lazy configuration of this <code>Digester</code> 3174 * instance. The default implementation does nothing, but subclasses 3175 * can override as needed. 3176 * </p> 3177 * 3178 * <p> 3179 * <strong>Note</strong> This method may be called more than once. 3180 * Once only initialization code should be placed in {@link #initialize} 3181 * or the code should take responsibility by checking and setting the 3182 * {@link #configured} flag. 3183 * </p> 3184 */ 3185 protected void configure() { 3186 3187 // Do not configure more than once 3188 if (configured) { 3189 return; 3190 } 3191 3192 // Perform lazy configuration as needed 3193 initialize(); // call hook method for subclasses that want to be initialized once only 3194 // Nothing else required by default 3195 3196 // Set the configuration flag to avoid repeating 3197 configured = true; 3198 3199 } 3200 3201 /** 3202 * <p> 3203 * Provides a hook for lazy initialization of this <code>Digester</code> 3204 * instance. 3205 * The default implementation does nothing, but subclasses 3206 * can override as needed. 3207 * Digester (by default) only calls this method once. 3208 * </p> 3209 * 3210 * <p> 3211 * <strong>Note</strong> This method will be called by {@link #configure} 3212 * only when the {@link #configured} flag is false. 3213 * Subclasses that override <code>configure</code> or who set <code>configured</code> 3214 * may find that this method may be called more than once. 3215 * </p> 3216 * 3217 * @since 1.6 3218 */ 3219 protected void initialize() { 3220 3221 // Perform lazy initialization as needed 3222 // Nothing required by default 3223 3224 } 3225 3226 // -------------------------------------------------------- Package Methods 3227 3228 3229 /** 3230 * Return the set of DTD URL registrations, keyed by public identifier. 3231 */ 3232 Map<String, URL> getRegistrations() { 3233 3234 return (entityValidator); 3235 3236 } 3237 3238 3239 /** 3240 * Return the set of rules that apply to the specified match position. 3241 * The selected rules are those that match exactly, or those rules 3242 * that specify a suffix match and the tail of the rule matches the 3243 * current match position. Exact matches have precedence over 3244 * suffix matches, then (among suffix matches) the longest match 3245 * is preferred. 3246 * 3247 * @param match The current match position 3248 * 3249 * @deprecated Call <code>match()</code> on the <code>Rules</code> 3250 * implementation returned by <code>getRules()</code> 3251 */ 3252 @Deprecated 3253 List<Rule> getRules(String match) { 3254 3255 return (getRules().match(match)); 3256 3257 } 3258 3259 3260 /** 3261 * <p>Return the top object on the parameters stack without removing it. If there are 3262 * no objects on the stack, return <code>null</code>.</p> 3263 * 3264 * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters. 3265 * See {@link #params}.</p> 3266 */ 3267 public Object peekParams() { 3268 3269 try { 3270 return (params.peek()); 3271 } catch (EmptyStackException e) { 3272 log.warn("Empty stack (returning null)"); 3273 return (null); 3274 } 3275 3276 } 3277 3278 3279 /** 3280 * <p>Return the n'th object down the parameters stack, where 0 is the top element 3281 * and [getCount()-1] is the bottom element. If the specified index 3282 * is out of range, return <code>null</code>.</p> 3283 * 3284 * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters. 3285 * See {@link #params}.</p> 3286 * 3287 * @param n Index of the desired element, where 0 is the top of the stack, 3288 * 1 is the next element down, and so on. 3289 */ 3290 public Object peekParams(int n) { 3291 3292 int index = (params.size() - 1) - n; 3293 if (index < 0) { 3294 log.warn("Empty stack (returning null)"); 3295 return (null); 3296 } 3297 try { 3298 return (params.get(index)); 3299 } catch (EmptyStackException e) { 3300 log.warn("Empty stack (returning null)"); 3301 return (null); 3302 } 3303 3304 } 3305 3306 3307 /** 3308 * <p>Pop the top object off of the parameters stack, and return it. If there are 3309 * no objects on the stack, return <code>null</code>.</p> 3310 * 3311 * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters. 3312 * See {@link #params}.</p> 3313 */ 3314 public Object popParams() { 3315 3316 try { 3317 if (log.isTraceEnabled()) { 3318 log.trace("Popping params"); 3319 } 3320 return (params.pop()); 3321 } catch (EmptyStackException e) { 3322 log.warn("Empty stack (returning null)"); 3323 return (null); 3324 } 3325 3326 } 3327 3328 3329 /** 3330 * <p>Push a new object onto the top of the parameters stack.</p> 3331 * 3332 * <p>The parameters stack is used to store <code>CallMethodRule</code> parameters. 3333 * See {@link #params}.</p> 3334 * 3335 * @param object The new object 3336 */ 3337 public void pushParams(Object object) { 3338 if (log.isTraceEnabled()) { 3339 log.trace("Pushing params"); 3340 } 3341 params.push(object); 3342 3343 } 3344 3345 /** 3346 * Create a SAX exception which also understands about the location in 3347 * the digester file where the exception occurs 3348 * 3349 * @return the new exception 3350 */ 3351 public SAXException createSAXException(String message, Exception e) { 3352 if ((e != null) && 3353 (e instanceof InvocationTargetException)) { 3354 Throwable t = ((InvocationTargetException) e).getTargetException(); 3355 if ((t != null) && (t instanceof Exception)) { 3356 e = (Exception) t; 3357 } 3358 } 3359 if (locator != null) { 3360 String error = "Error at line " + locator.getLineNumber() + " char " + 3361 locator.getColumnNumber() + ": " + message; 3362 if (e != null) { 3363 return new SAXParseException(error, locator, e); 3364 } else { 3365 return new SAXParseException(error, locator); 3366 } 3367 } 3368 log.error("No Locator!"); 3369 if (e != null) { 3370 return new SAXException(message, e); 3371 } else { 3372 return new SAXException(message); 3373 } 3374 } 3375 3376 /** 3377 * Create a SAX exception which also understands about the location in 3378 * the digester file where the exception occurs 3379 * 3380 * @return the new exception 3381 */ 3382 public SAXException createSAXException(Exception e) { 3383 if (e instanceof InvocationTargetException) { 3384 Throwable t = ((InvocationTargetException) e).getTargetException(); 3385 if ((t != null) && (t instanceof Exception)) { 3386 e = (Exception) t; 3387 } 3388 } 3389 return createSAXException(e.getMessage(), e); 3390 } 3391 3392 /** 3393 * Create a SAX exception which also understands about the location in 3394 * the digester file where the exception occurs 3395 * 3396 * @return the new exception 3397 */ 3398 public SAXException createSAXException(String message) { 3399 return createSAXException(message, null); 3400 } 3401 3402 }