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