Coverage report

  %line %branch
org.apache.commons.jelly.parser.XMLParser
68% 
92% 

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

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.