View Javadoc
1 /* 2 * $Header: /home/cvs/jakarta-commons-sandbox/jelly/src/java/org/apache/commons/jelly/parser/XMLParser.java,v 1.28 2002/09/05 16:42:47 jstrachan Exp $ 3 * $Revision: 1.28 $ 4 * $Date: 2002/09/05 16:42:47 $ 5 * 6 * ==================================================================== 7 * 8 * The Apache Software License, Version 1.1 9 * 10 * Copyright (c) 1999-2002 The Apache Software Foundation. All rights 11 * reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in 22 * the documentation and/or other materials provided with the 23 * distribution. 24 * 25 * 3. The end-user documentation included with the redistribution, if 26 * any, must include the following acknowlegement: 27 * "This product includes software developed by the 28 * Apache Software Foundation (http://www.apache.org/)." 29 * Alternately, this acknowlegement may appear in the software itself, 30 * if and wherever such third-party acknowlegements normally appear. 31 * 32 * 4. The names "The Jakarta Project", "Commons", and "Apache Software 33 * Foundation" must not be used to endorse or promote products derived 34 * from this software without prior written permission. For written 35 * permission, please contact apache@apache.org. 36 * 37 * 5. Products derived from this software may not be called "Apache" 38 * nor may "Apache" appear in their names without prior written 39 * permission of the Apache Group. 40 * 41 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 42 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 43 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 44 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 45 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 46 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 47 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 48 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 49 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 50 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 51 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 52 * SUCH DAMAGE. 53 * ==================================================================== 54 * 55 * This software consists of voluntary contributions made by many 56 * individuals on behalf of the Apache Software Foundation. For more 57 * information on the Apache Software Foundation, please see 58 * <http://www.apache.org/>;. 59 * 60 * $Id: XMLParser.java,v 1.28 2002/09/05 16:42:47 jstrachan Exp $ 61 */ 62 package org.apache.commons.jelly.parser; 63 64 import java.io.File; 65 import java.io.FileReader; 66 import java.io.InputStream; 67 import java.io.IOException; 68 import java.io.PrintWriter; 69 import java.io.Reader; 70 import java.net.URL; 71 import java.net.URLConnection; 72 import java.util.ArrayList; 73 import java.util.EmptyStackException; 74 import java.util.HashMap; 75 import java.util.Iterator; 76 import java.util.List; 77 import java.util.Map; 78 import java.util.Properties; 79 80 import javax.xml.parsers.SAXParser; 81 import javax.xml.parsers.SAXParserFactory; 82 83 import org.apache.commons.collections.ArrayStack; 84 85 import org.apache.commons.jelly.JellyContext; 86 import org.apache.commons.jelly.Script; 87 import org.apache.commons.jelly.Tag; 88 import org.apache.commons.jelly.TagLibrary; 89 import org.apache.commons.jelly.impl.CompositeTextScriptBlock; 90 import org.apache.commons.jelly.impl.ExpressionScript; 91 import org.apache.commons.jelly.impl.StaticTag; 92 import org.apache.commons.jelly.impl.DynaTagScript; 93 import org.apache.commons.jelly.impl.ScriptBlock; 94 import org.apache.commons.jelly.impl.StaticTagScript; 95 import org.apache.commons.jelly.impl.TagFactory; 96 import org.apache.commons.jelly.impl.TagScript; 97 import org.apache.commons.jelly.impl.TextScript; 98 import org.apache.commons.jelly.expression.CompositeExpression; 99 import org.apache.commons.jelly.expression.ConstantExpression; 100 import org.apache.commons.jelly.expression.Expression; 101 import org.apache.commons.jelly.expression.ExpressionFactory; 102 import org.apache.commons.jelly.expression.jexl.JexlExpressionFactory; 103 104 import org.apache.commons.jelly.tags.core.CoreTagLibrary; 105 import org.apache.commons.jelly.tags.xml.XMLTagLibrary; 106 107 import org.apache.commons.logging.Log; 108 import org.apache.commons.logging.LogFactory; 109 110 import org.xml.sax.Attributes; 111 import org.xml.sax.ContentHandler; 112 import org.xml.sax.EntityResolver; 113 import org.xml.sax.ErrorHandler; 114 import org.xml.sax.helpers.DefaultHandler; 115 import org.xml.sax.InputSource; 116 import org.xml.sax.Locator; 117 import org.xml.sax.SAXException; 118 import org.xml.sax.SAXParseException; 119 import org.xml.sax.XMLReader; 120 121 /*** <p><code>XMLParser</code> parses the XML Jelly format. 122 * The SAXParser and XMLReader portions of this code come from Digester.</p> 123 * 124 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> 125 * @version $Revision: 1.28 $ 126 */ 127 public class XMLParser extends DefaultHandler { 128 129 /*** JellyContext which is used to locate tag libraries*/ 130 private JellyContext context = new JellyContext(); 131 132 /*** the expression factory used to evaluate tag attributes */ 133 private ExpressionFactory expressionFactory; 134 135 /*** The current script block */ 136 private ScriptBlock script; 137 138 /*** The current, parent tagScript */ 139 private TagScript tagScript; 140 141 /*** The stack of body scripts. */ 142 private ArrayStack scriptStack = new ArrayStack(); 143 144 /*** The stack of tagScripts - use ArrayList as it allows null. */ 145 private ArrayList tagScriptStack = new ArrayList(); 146 147 /*** The current text buffer where non-custom tags get written */ 148 private StringBuffer textBuffer; 149 150 /*** 151 * The class loader to use for instantiating application objects. 152 * If not specified, the context class loader, or the class loader 153 * used to load XMLParser itself, is used, based on the value of the 154 * <code>useContextClassLoader</code> variable. 155 */ 156 protected ClassLoader classLoader = null; 157 158 /*** 159 * Do we want to use the Context ClassLoader when loading classes 160 * for instantiating new objects? Default is <code>false</code>. 161 */ 162 protected boolean useContextClassLoader = false; 163 164 /*** 165 * The application-supplied error handler that is notified when parsing 166 * warnings, errors, or fatal errors occur. 167 */ 168 protected ErrorHandler errorHandler = null; 169 170 /*** 171 * The SAXParserFactory that is created the first time we need it. 172 */ 173 protected static SAXParserFactory factory = null; 174 175 /*** 176 * The SAXParser we will use to parse the input stream. 177 */ 178 protected SAXParser parser = null; 179 180 /*** 181 * The XMLReader used to parse digester rules. 182 */ 183 protected XMLReader reader = null; 184 185 /*** 186 * The Locator associated with our parser. 187 */ 188 protected Locator locator = null; 189 190 /*** 191 * Registered namespaces we are currently processing. The key is the 192 * namespace prefix that was declared in the document. The value is an 193 * ArrayStack of the namespace URIs this prefix has been mapped to -- 194 * the top Stack element is the most current one. (This architecture 195 * is required because documents can declare nested uses of the same 196 * prefix for different Namespace URIs). 197 */ 198 protected HashMap namespaces = new HashMap(); 199 200 /*** The Map of the namespace prefix -> URIs defined for the current element */ 201 private Map elementNamespaces; 202 203 /*** 204 * The name of the file being parsed that is passed to the TagScript objects 205 * for error reporting 206 */ 207 private String fileName; 208 209 /*** 210 * Do we want to use a validating parser? 211 */ 212 protected boolean validating = false; 213 214 /*** Flag to indicate if this object has been configured */ 215 private boolean configured; 216 217 /*** 218 * The Log to which logging calls will be made. 219 */ 220 private Log log = LogFactory.getLog(XMLParser.class); 221 222 223 224 /*** 225 * Construct a new XMLParser with default properties. 226 */ 227 public XMLParser() { 228 } 229 230 /*** 231 * Construct a new XMLParser, allowing a SAXParser to be passed in. This 232 * allows XMLParser to be used in environments which are unfriendly to 233 * JAXP1.1 (such as WebLogic 6.0). Thanks for the request to change go to 234 * James House (james@interobjective.com). This may help in places where 235 * you are able to load JAXP 1.1 classes yourself. 236 */ 237 public XMLParser(SAXParser parser) { 238 this.parser = parser; 239 } 240 241 /*** 242 * Construct a new XMLParser, allowing an XMLReader to be passed in. This 243 * allows XMLParser to be used in environments which are unfriendly to 244 * JAXP1.1 (such as WebLogic 6.0). Note that if you use this option you 245 * have to configure namespace and validation support yourself, as these 246 * properties only affect the SAXParser and emtpy constructor. 247 */ 248 public XMLParser(XMLReader reader) { 249 this.reader = reader; 250 } 251 252 /*** 253 * Parse the content of the specified file using this XMLParser. Returns 254 * the root element from the object stack (if any). 255 * 256 * @param file File containing the XML data to be parsed 257 * 258 * @exception IOException if an input/output error occurs 259 * @exception SAXException if a parsing exception occurs 260 */ 261 public Script parse(File file) throws IOException, SAXException { 262 ensureConfigured(); 263 this.fileName = file.toString(); 264 getXMLReader().parse(new InputSource(new FileReader(file))); 265 return script; 266 } 267 268 /*** 269 * Parse the content of the specified input source using this XMLParser. 270 * Returns the root element from the object stack (if any). 271 * 272 * @param input Input source containing the XML data to be parsed 273 * 274 * @exception IOException if an input/output error occurs 275 * @exception SAXException if a parsing exception occurs 276 */ 277 public Script parse(InputSource input) throws IOException, SAXException { 278 ensureConfigured(); 279 this.fileName = input.getSystemId(); 280 getXMLReader().parse(input); 281 return script; 282 } 283 284 /*** 285 * Parse the content of the specified input stream using this XMLParser. 286 * Returns the root element from the object stack (if any). 287 * 288 * @param input Input stream containing the XML data to be parsed 289 * 290 * @exception IOException if an input/output error occurs 291 * @exception SAXException if a parsing exception occurs 292 */ 293 public Script parse(InputStream input) throws IOException, SAXException { 294 ensureConfigured(); 295 this.fileName = null; 296 getXMLReader().parse(new InputSource(input)); 297 return script; 298 } 299 300 /*** 301 * Parse the content of the specified reader using this XMLParser. 302 * Returns the root element from the object stack (if any). 303 * 304 * @param reader Reader containing the XML data to be parsed 305 * 306 * @exception IOException if an input/output error occurs 307 * @exception SAXException if a parsing exception occurs 308 */ 309 public Script parse(Reader reader) throws IOException, SAXException { 310 ensureConfigured(); 311 this.fileName = null; 312 getXMLReader().parse(new InputSource(reader)); 313 return script; 314 } 315 316 /*** 317 * Parse the content of the specified URI using this XMLParser. 318 * Returns the root element from the object stack (if any). 319 * 320 * @param uri URI containing the XML data to be parsed 321 * 322 * @exception IOException if an input/output error occurs 323 * @exception SAXException if a parsing exception occurs 324 */ 325 public Script parse(String uri) throws IOException, SAXException { 326 ensureConfigured(); 327 this.fileName = uri; 328 getXMLReader().parse(uri); 329 return script; 330 } 331 332 /*** 333 * Return the currently mapped namespace URI for the specified prefix, 334 * if any; otherwise return <code>null</code>. These mappings come and 335 * go dynamically as the document is parsed. 336 * 337 * @param prefix Prefix to look up 338 */ 339 public String findNamespaceURI(String prefix) { 340 ArrayStack stack = (ArrayStack) namespaces.get(prefix); 341 if (stack == null) { 342 return (null); 343 } 344 try { 345 return ((String) stack.peek()); 346 } 347 catch (EmptyStackException e) { 348 return (null); 349 } 350 } 351 352 // Properties 353 //------------------------------------------------------------------------- 354 public JellyContext getContext() { 355 return context; 356 } 357 358 public void setContext(JellyContext context) { 359 this.context = context; 360 } 361 362 /*** 363 * Return the class loader to be used for instantiating application objects 364 * when required. This is determined based upon the following rules: 365 * <ul> 366 * <li>The class loader set by <code>setClassLoader()</code>, if any</li> 367 * <li>The thread context class loader, if it exists and the 368 * <code>useContextClassLoader</code> property is set to true</li> 369 * <li>The class loader used to load the XMLParser class itself. 370 * </ul> 371 */ 372 public ClassLoader getClassLoader() { 373 if (this.classLoader != null) { 374 return (this.classLoader); 375 } 376 if (this.useContextClassLoader) { 377 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 378 if (classLoader != null) { 379 return (classLoader); 380 } 381 } 382 return (this.getClass().getClassLoader()); 383 } 384 385 /*** 386 * Set the class loader to be used for instantiating application objects 387 * when required. 388 * 389 * @param classLoader The new class loader to use, or <code>null</code> 390 * to revert to the standard rules 391 */ 392 public void setClassLoader(ClassLoader classLoader) { 393 this.classLoader = classLoader; 394 } 395 396 /*** 397 * Return the boolean as to whether the context classloader should be used. 398 */ 399 public boolean getUseContextClassLoader() { 400 return useContextClassLoader; 401 } 402 403 /*** 404 * Determine whether to use the Context ClassLoader (the one found by 405 * calling <code>Thread.currentThread().getContextClassLoader()</code>) 406 * to resolve/load classes. If not 407 * using Context ClassLoader, then the class-loading defaults to 408 * using the calling-class' ClassLoader. 409 * 410 * @param boolean determines whether to use JellyContext ClassLoader. 411 */ 412 public void setUseContextClassLoader(boolean use) { 413 useContextClassLoader = use; 414 } 415 416 /*** 417 * Return the error handler for this XMLParser. 418 */ 419 public ErrorHandler getErrorHandler() { 420 return (this.errorHandler); 421 } 422 423 /*** 424 * Set the error handler for this XMLParser. 425 * 426 * @param errorHandler The new error handler 427 */ 428 public void setErrorHandler(ErrorHandler errorHandler) { 429 this.errorHandler = errorHandler; 430 } 431 432 /*** 433 * Return the current Logger associated with this instance of the XMLParser 434 */ 435 public Log getLogger() { 436 return log; 437 } 438 439 /*** 440 * Set the current logger for this XMLParser. 441 */ 442 public void setLogger(Log log) { 443 this.log = log; 444 } 445 446 /*** @return the expression factory used to evaluate tag attributes */ 447 public ExpressionFactory getExpressionFactory() { 448 if (expressionFactory == null) { 449 expressionFactory = createExpressionFactory(); 450 } 451 return expressionFactory; 452 } 453 454 /*** Sets the expression factory used to evaluate tag attributes */ 455 public void setExpressionFactory(ExpressionFactory expressionFactory) { 456 this.expressionFactory = expressionFactory; 457 } 458 459 /*** 460 * Return the SAXParser we will use to parse the input stream. If there 461 * is a problem creating the parser, return <code>null</code>. 462 */ 463 public SAXParser getParser() { 464 // Return the parser we already created (if any) 465 if (parser != null) { 466 return (parser); 467 } 468 // Create and return a new parser 469 synchronized (this) { 470 try { 471 if (factory == null) { 472 factory = SAXParserFactory.newInstance(); 473 } 474 factory.setNamespaceAware(true); 475 factory.setValidating(validating); 476 parser = factory.newSAXParser(); 477 return (parser); 478 } 479 catch (Exception e) { 480 log.error("XMLParser.getParser: ", e); 481 return (null); 482 } 483 } 484 } 485 486 /*** 487 * By setting the reader in the constructor, you can bypass JAXP and 488 * be able to use digester in Weblogic 6.0. 489 * 490 * @deprecated Use getXMLReader() instead, which can throw a 491 * SAXException if the reader cannot be instantiated 492 */ 493 public XMLReader getReader() { 494 try { 495 return (getXMLReader()); 496 } 497 catch (SAXException e) { 498 log.error("Cannot get XMLReader", e); 499 return (null); 500 } 501 } 502 503 /*** 504 * Return the XMLReader to be used for parsing the input document. 505 * 506 * @exception SAXException if no XMLReader can be instantiated 507 */ 508 public synchronized XMLReader getXMLReader() throws SAXException { 509 if (reader == null) { 510 reader = getParser().getXMLReader(); 511 } 512 //set up the parse 513 reader.setContentHandler(this); 514 reader.setDTDHandler(this); 515 //reader.setEntityResolver(this); 516 reader.setErrorHandler(this); 517 return reader; 518 } 519 520 /*** 521 * Return the validating parser flag. 522 */ 523 public boolean getValidating() { 524 return (this.validating); 525 } 526 527 /*** 528 * Set the validating parser flag. This must be called before 529 * <code>parse()</code> is called the first time. 530 * 531 * @param validating The new validating parser flag. 532 */ 533 public void setValidating(boolean validating) { 534 this.validating = validating; 535 } 536 537 // ContentHandler interface 538 //------------------------------------------------------------------------- 539 /*** 540 * Process notification of the beginning of the document being reached. 541 * 542 * @exception SAXException if a parsing error is to be reported 543 */ 544 public void startDocument() throws SAXException { 545 script = new ScriptBlock(); 546 textBuffer = new StringBuffer(); 547 tagScript = null; 548 scriptStack.clear(); 549 tagScriptStack.clear(); 550 } 551 552 /*** 553 * Process notification of the end of the document being reached. 554 * 555 * @exception SAXException if a parsing error is to be reported 556 */ 557 public void endDocument() throws SAXException { 558 textBuffer = null; 559 } 560 561 /*** 562 * Process notification of the start of an XML element being reached. 563 * 564 * @param uri The Namespace URI, or the empty string if the element 565 * has no Namespace URI or if Namespace processing is not being performed. 566 * @param localName The local name (without prefix), or the empty 567 * string if Namespace processing is not being performed. 568 * @param qName The qualified name (with prefix), or the empty 569 * string if qualified names are not available.\ 570 * @param list The attributes attached to the element. If there are 571 * no attributes, it shall be an empty Attributes object. 572 * @exception SAXException if a parsing error is to be reported 573 */ 574 public void startElement( 575 String namespaceURI, 576 String localName, 577 String qName, 578 Attributes list) 579 throws SAXException { 580 581 try { 582 // add check to ensure namespace URI is "" for no namespace 583 if ( namespaceURI == null ) { 584 namespaceURI = ""; 585 } 586 587 // if this is a tag then create a script to run it 588 // otherwise pass the text to the current body 589 TagScript parentTagScript = tagScript; 590 tagScript = createTag(namespaceURI, localName, list); 591 if (tagScript == null) { 592 tagScript = createStaticTag(namespaceURI, localName, qName, list); 593 } 594 tagScriptStack.add(tagScript); 595 if (tagScript != null) { 596 // set parent relationship... 597 tagScript.setParent(parentTagScript); 598 599 // set the namespace Map 600 if ( elementNamespaces != null ) { 601 tagScript.setNamespacesMap( elementNamespaces ); 602 elementNamespaces = null; 603 } 604 605 // set the line number details 606 if ( locator != null ) { 607 tagScript.setLocator(locator); 608 } 609 // sets the file name element names 610 tagScript.setFileName(fileName); 611 tagScript.setElementName(qName); 612 613 if (textBuffer.length() > 0) { 614 addTextScript(textBuffer.toString()); 615 textBuffer.setLength(0); 616 } 617 script.addScript(tagScript); 618 // start a new body 619 scriptStack.push(script); 620 script = new ScriptBlock(); 621 tagScript.setTagBody(script); 622 } 623 else { 624 // XXXX: might wanna handle empty elements later... 625 textBuffer.append("<"); 626 textBuffer.append(qName); 627 int size = list.getLength(); 628 for (int i = 0; i < size; i++) { 629 textBuffer.append(" "); 630 textBuffer.append(list.getQName(i)); 631 textBuffer.append("="); 632 textBuffer.append("\""); 633 textBuffer.append(list.getValue(i)); 634 textBuffer.append("\""); 635 } 636 textBuffer.append(">"); 637 } 638 } 639 catch (SAXException e) { 640 throw e; 641 } 642 catch (Exception e) { 643 log.error( "Caught exception: " + e, e ); 644 throw new SAXException( "Runtime Exception: " + e, e ); 645 } 646 } 647 648 /*** 649 * Process notification of character data received from the body of 650 * an XML element. 651 * 652 * @param buffer The characters from the XML document 653 * @param start Starting offset into the buffer 654 * @param length Number of characters from the buffer 655 * 656 * @exception SAXException if a parsing error is to be reported 657 */ 658 public void characters(char buffer[], int start, int length) 659 throws SAXException { 660 textBuffer.append(buffer, start, length); 661 } 662 663 /*** 664 * Process notification of the end of an XML element being reached. 665 * 666 * @param uri - The Namespace URI, or the empty string if the 667 * element has no Namespace URI or if Namespace processing is not 668 * being performed. 669 * @param localName - The local name (without prefix), or the empty 670 * string if Namespace processing is not being performed. 671 * @param qName - The qualified XML 1.0 name (with prefix), or the 672 * empty string if qualified names are not available. 673 * @exception SAXException if a parsing error is to be reported 674 */ 675 public void endElement(String namespaceURI, String localName, String qName) 676 throws SAXException { 677 try { 678 tagScript = (TagScript) tagScriptStack.remove(tagScriptStack.size() - 1); 679 if (tagScript != null) { 680 if (textBuffer.length() > 0) { 681 addTextScript(textBuffer.toString()); 682 textBuffer.setLength(0); 683 } 684 script = (ScriptBlock) scriptStack.pop(); 685 } 686 else { 687 textBuffer.append("</"); 688 textBuffer.append(qName); 689 textBuffer.append(">"); 690 } 691 692 // now lets set the parent tag variable 693 if ( tagScriptStack.isEmpty() ) { 694 tagScript = null; 695 } 696 else { 697 tagScript = (TagScript) tagScriptStack.get(tagScriptStack.size() - 1); 698 } 699 } 700 catch (SAXException e) { 701 throw e; 702 } 703 catch (Exception e) { 704 log.error( "Caught exception: " + e, e ); 705 throw new SAXException( "Runtime Exception: " + e, e ); 706 } 707 } 708 709 /*** 710 * Process notification that a namespace prefix is coming in to scope. 711 * 712 * @param prefix Prefix that is being declared 713 * @param namespaceURI Corresponding namespace URI being mapped to 714 * 715 * @exception SAXException if a parsing error is to be reported 716 */ 717 public void startPrefixMapping(String prefix, String namespaceURI) 718 throws SAXException { 719 // Register this prefix mapping 720 ArrayStack stack = (ArrayStack) namespaces.get(prefix); 721 if (stack == null) { 722 stack = new ArrayStack(); 723 namespaces.put(prefix, stack); 724 } 725 stack.push(namespaceURI); 726 727 if ( elementNamespaces == null ) { 728 elementNamespaces = new HashMap(); 729 } 730 elementNamespaces.put(prefix, namespaceURI); 731 } 732 733 /*** 734 * Process notification that a namespace prefix is going out of scope. 735 * 736 * @param prefix Prefix that is going out of scope 737 * 738 * @exception SAXException if a parsing error is to be reported 739 */ 740 public void endPrefixMapping(String prefix) throws SAXException { 741 // Deregister this prefix mapping 742 ArrayStack stack = (ArrayStack) namespaces.get(prefix); 743 if (stack == null) { 744 return; 745 } 746 try { 747 stack.pop(); 748 if (stack.empty()) { 749 namespaces.remove(prefix); 750 } 751 } 752 catch (EmptyStackException e) { 753 throw createSAXException("endPrefixMapping popped too many times"); 754 } 755 } 756 757 /*** 758 * Process notification of ignorable whitespace received from the body of 759 * an XML element. 760 * 761 * @param buffer The characters from the XML document 762 * @param start Starting offset into the buffer 763 * @param length Number of characters from the buffer 764 * 765 * @exception SAXException if a parsing error is to be reported 766 */ 767 public void ignorableWhitespace(char buffer[], int start, int len) 768 throws SAXException { 769 ; // No processing required 770 } 771 772 /*** 773 * Process notification of a processing instruction that was encountered. 774 * 775 * @param target The processing instruction target 776 * @param data The processing instruction data (if any) 777 * 778 * @exception SAXException if a parsing error is to be reported 779 */ 780 public void processingInstruction(String target, String data) 781 throws SAXException { 782 ; // No processing is required 783 } 784 785 /*** 786 * Set the document locator associated with our parser. 787 * 788 * @param locator The new locator 789 */ 790 public void setDocumentLocator(Locator locator) { 791 this.locator = locator; 792 } 793 794 /*** 795 * Process notification of a skipped entity. 796 * 797 * @param name Name of the skipped entity 798 * 799 * @exception SAXException if a parsing error is to be reported 800 */ 801 public void skippedEntity(String name) throws SAXException { 802 ; // No processing required 803 } 804 805 806 // DTDHandler interface 807 //------------------------------------------------------------------------- 808 809 /*** 810 * Receive notification of a notation declaration event. 811 * 812 * @param name The notation name 813 * @param publicId The public identifier (if any) 814 * @param systemId The system identifier (if any) 815 */ 816 public void notationDecl(String name, String publicId, String systemId) { 817 } 818 819 /*** 820 * Receive notification of an unparsed entity declaration event. 821 * 822 * @param name The unparsed entity name 823 * @param publicId The public identifier (if any) 824 * @param systemId The system identifier (if any) 825 * @param notation The name of the associated notation 826 */ 827 public void unparsedEntityDecl( 828 String name, 829 String publicId, 830 String systemId, 831 String notation) { 832 } 833 834 835 // ErrorHandler interface 836 //------------------------------------------------------------------------- 837 838 /*** 839 * Forward notification of a parsing error to the application supplied 840 * error handler (if any). 841 * 842 * @param exception The error information 843 * 844 * @exception SAXException if a parsing exception occurs 845 */ 846 public void error(SAXParseException exception) throws SAXException { 847 log.error( 848 "Parse Error at line " 849 + exception.getLineNumber() 850 + " column " 851 + exception.getColumnNumber() 852 + ": " 853 + exception.getMessage(), 854 exception); 855 if (errorHandler != null) { 856 errorHandler.error(exception); 857 } 858 } 859 860 /*** 861 * Forward notification of a fatal parsing error to the application 862 * supplied error handler (if any). 863 * 864 * @param exception The fatal error information 865 * 866 * @exception SAXException if a parsing exception occurs 867 */ 868 public void fatalError(SAXParseException exception) throws SAXException { 869 log.error( 870 "Parse Fatal Error at line " 871 + exception.getLineNumber() 872 + " column " 873 + exception.getColumnNumber() 874 + ": " 875 + exception.getMessage(), 876 exception); 877 if (errorHandler != null) { 878 errorHandler.fatalError(exception); 879 } 880 } 881 882 /*** 883 * Forward notification of a parse warning to the application supplied 884 * error handler (if any). 885 * 886 * @param exception The warning information 887 * 888 * @exception SAXException if a parsing exception occurs 889 */ 890 public void warning(SAXParseException exception) throws SAXException { 891 log.error( 892 "Parse Warning at line " 893 + exception.getLineNumber() 894 + " column " 895 + exception.getColumnNumber() 896 + ": " 897 + exception.getMessage(), 898 exception); 899 if (errorHandler != null) { 900 errorHandler.warning(exception); 901 } 902 } 903 904 // Implementation methods 905 //------------------------------------------------------------------------- 906 /*** 907 * If this object has not been configured then register the default 908 * namespaces 909 */ 910 private void ensureConfigured() { 911 if (!configured) { 912 configure(); 913 configured = true; 914 } 915 } 916 917 /*** 918 * This method is called only once before parsing occurs 919 * which allows tag libraries to be registered and so forth 920 */ 921 protected void configure() { 922 // load the properties file of libraries available 923 InputStream in = null; 924 URL url = 925 getClassLoader().getResource("org/apache/commons/jelly/jelly.properties"); 926 if (url != null) { 927 log.debug("Loading Jelly default tag libraries from: " + url); 928 Properties properties = new Properties(); 929 try { 930 in = url.openStream(); 931 properties.load(in); 932 for (Iterator iter = properties.entrySet().iterator(); iter.hasNext();) { 933 Map.Entry entry = (Map.Entry) iter.next(); 934 String uri = (String) entry.getKey(); 935 String className = (String) entry.getValue(); 936 String libraryURI = "jelly:" + uri; 937 938 // don't overload any Mock Tags already 939 if ( context.getTagLibrary(libraryURI) == null ) { 940 context.registerTagLibrary(libraryURI, className); 941 } 942 } 943 } 944 catch (IOException e) { 945 log.error("Could not load jelly properties from: " + url + ". Reason: " + e, e); 946 } 947 finally { 948 try { 949 in.close(); 950 } 951 catch (Exception e) { 952 // ignore 953 } 954 } 955 } 956 } 957 958 /*** 959 * Factory method to create new Tag script for the given namespaceURI and name or 960 * return null if this is not a custom Tag. 961 */ 962 protected TagScript createTag( 963 String namespaceURI, 964 String localName, 965 Attributes list) 966 throws SAXException { 967 try { 968 // use the URI to load a taglib 969 TagLibrary taglib = context.getTagLibrary(namespaceURI); 970 if (taglib == null) { 971 if (namespaceURI != null && namespaceURI.startsWith("jelly:")) { 972 String uri = namespaceURI.substring(6); 973 // try to find the class on the claspath 974 try { 975 Class taglibClass = getClassLoader().loadClass(uri); 976 taglib = (TagLibrary) taglibClass.newInstance(); 977 context.registerTagLibrary(namespaceURI, taglib); 978 } 979 catch (ClassNotFoundException e) { 980 log.warn("Could not load class: " + uri + " so disabling the taglib"); 981 } 982 } 983 } 984 if (taglib != null) { 985 TagScript script = taglib.createTagScript(localName, list); 986 if ( script != null ) { 987 // now iterate through through the expressions 988 int size = list.getLength(); 989 for (int i = 0; i < size; i++) { 990 String attributeName = list.getLocalName(i); 991 String attributeValue = list.getValue(i); 992 Expression expression = 993 taglib.createExpression( 994 getExpressionFactory(), 995 localName, 996 attributeName, 997 attributeValue); 998 if (expression == null) { 999 expression = createConstantExpression(localName, attributeName, attributeValue); 1000 } 1001 script.addAttribute(attributeName, expression); 1002 } 1003 } 1004 return script; 1005 } 1006 return null; 1007 } 1008 catch (Exception e) { 1009 log.warn( 1010 "Could not create taglib or URI: " + namespaceURI + " tag name: " + localName, 1011 e); 1012 throw createSAXException(e); 1013 } 1014 catch (Throwable e) { 1015 log.warn( 1016 "Could not create taglib or URI: " + namespaceURI + " tag name: " + localName, 1017 e); 1018 return null; 1019 } 1020 } 1021 1022 /*** 1023 * Factory method to create a static Tag that represents some static content. 1024 */ 1025 protected TagScript createStaticTag( 1026 final String namespaceURI, 1027 final String localName, 1028 final String qName, 1029 Attributes list) 1030 throws SAXException { 1031 try { 1032 StaticTag tag = new StaticTag( namespaceURI, localName, qName ); 1033 StaticTagScript script = new StaticTagScript( 1034 new TagFactory() { 1035 public Tag createTag() { 1036 return new StaticTag( namespaceURI, localName, qName ); 1037 } 1038 } 1039 ); 1040 1041 // now iterate through through the expressions 1042 int size = list.getLength(); 1043 for (int i = 0; i < size; i++) { 1044 String attributeName = list.getLocalName(i); 1045 String attributeValue = list.getValue(i); 1046 Expression expression = CompositeExpression.parse( 1047 attributeValue, getExpressionFactory() 1048 ); 1049 script.addAttribute(attributeName, expression); 1050 } 1051 return script; 1052 } 1053 catch (Exception e) { 1054 log.warn( 1055 "Could not create static tag for URI: " 1056 + namespaceURI 1057 + " tag name: " 1058 + localName, 1059 e); 1060 throw createSAXException(e); 1061 } 1062 } 1063 1064 /*** 1065 * Adds the text to the current script block parsing any embedded 1066 * expressions inot ExpressionScript objects. 1067 */ 1068 protected void addTextScript(String text) throws Exception { 1069 Expression expression = 1070 CompositeExpression.parse(text, getExpressionFactory()); 1071 1072 addExpressionScript(script, expression); 1073 } 1074 1075 1076 /*** 1077 * Adds the given Expression object to the current Script. 1078 */ 1079 protected void addExpressionScript(ScriptBlock script, Expression expression) { 1080 if ( expression instanceof ConstantExpression ) { 1081 ConstantExpression constantExpression 1082 = (ConstantExpression) expression; 1083 Object value = constantExpression.getValue(); 1084 if ( value != null ) { 1085 script.addScript(new TextScript( value.toString() )); 1086 } 1087 } 1088 else 1089 if ( expression instanceof CompositeExpression ) { 1090 CompositeTextScriptBlock newBlock = new CompositeTextScriptBlock(); 1091 script.addScript(newBlock); 1092 1093 CompositeExpression compositeExpression 1094 = (CompositeExpression) expression; 1095 Iterator iter = compositeExpression.getExpressions().iterator(); 1096 while (iter.hasNext()) { 1097 addExpressionScript( newBlock, (Expression) iter.next() ); 1098 } 1099 } 1100 else { 1101 script.addScript(new ExpressionScript(expression)); 1102 } 1103 } 1104 1105 1106 protected Expression createConstantExpression( 1107 String tagName, 1108 String attributeName, 1109 String attributeValue) 1110 throws Exception { 1111 return new ConstantExpression(attributeValue); 1112 } 1113 1114 protected ExpressionFactory createExpressionFactory() { 1115 return new JexlExpressionFactory(); 1116 } 1117 1118 /*** 1119 * Create a SAX exception which also understands about the location in 1120 * the file where the exception occurs 1121 * 1122 * @return the new exception 1123 */ 1124 protected SAXException createSAXException(String message, Exception e) { 1125 log.warn("Underlying exception: " + e); 1126 e.printStackTrace(); 1127 if (locator != null) { 1128 String error = 1129 "Error at (" 1130 + locator.getLineNumber() 1131 + ", " 1132 + locator.getColumnNumber() 1133 + ": " 1134 + message; 1135 if (e != null) { 1136 return new SAXParseException(error, locator, e); 1137 } 1138 else { 1139 return new SAXParseException(error, locator); 1140 } 1141 } 1142 log.error("No Locator!"); 1143 if (e != null) { 1144 return new SAXException(message, e); 1145 } 1146 else { 1147 return new SAXException(message); 1148 } 1149 } 1150 1151 /*** 1152 * Create a SAX exception which also understands about the location in 1153 * the digester file where the exception occurs 1154 * 1155 * @return the new exception 1156 */ 1157 protected SAXException createSAXException(Exception e) { 1158 return createSAXException(e.getMessage(), e); 1159 } 1160 /*** 1161 * Create a SAX exception which also understands about the location in 1162 * the digester file where the exception occurs 1163 * 1164 * @return the new exception 1165 */ 1166 protected SAXException createSAXException(String message) { 1167 return createSAXException(message, null); 1168 } 1169 }

This page was automatically generated by Maven