KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdom > contrib > input > scanner > ElementScanner


1 /*--
2
3  $Id: ElementScanner.java,v 1.13 2004/09/03 06:12:44 jhunter Exp $
4
5  Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
6  All rights reserved.
7
8  Redistribution and use in source and binary forms, with or without
9  modification, are permitted provided that the following conditions
10  are met:
11
12  1. Redistributions of source code must retain the above copyright
13     notice, this list of conditions, and the following disclaimer.
14
15  2. Redistributions in binary form must reproduce the above copyright
16     notice, this list of conditions, and the disclaimer that follows
17     these conditions in the documentation and/or other materials
18     provided with the distribution.
19
20  3. The name "JDOM" must not be used to endorse or promote products
21     derived from this software without prior written permission. For
22     written permission, please contact <request_AT_jdom_DOT_org>.
23
24  4. Products derived from this software may not be called "JDOM", nor
25     may "JDOM" appear in their name, without prior written permission
26     from the JDOM Project Management <request_AT_jdom_DOT_org>.
27
28  In addition, we request (but do not require) that you include in the
29  end-user documentation provided with the redistribution and/or in the
30  software itself an acknowledgement equivalent to the following:
31      "This product includes software developed by the
32       JDOM Project (http://www.jdom.org/)."
33  Alternatively, the acknowledgment may be graphical using the logos
34  available at http://www.jdom.org/images/logos.
35
36  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
40  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  SUCH DAMAGE.
48
49  This software consists of voluntary contributions made by many
50  individuals on behalf of the JDOM Project and was originally
51  created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
52  Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
53  on the JDOM Project, please see <http://www.jdom.org/>.
54
55  */

56
57 package org.jdom.contrib.input.scanner;
58
59 import java.io.IOException JavaDoc;
60 import java.util.*;
61
62 import org.xml.sax.Attributes JavaDoc;
63 import org.xml.sax.ContentHandler JavaDoc;
64 import org.xml.sax.InputSource JavaDoc;
65 import org.xml.sax.XMLReader JavaDoc;
66 import org.xml.sax.SAXException JavaDoc;
67 import org.xml.sax.SAXNotRecognizedException JavaDoc;
68 import org.xml.sax.SAXNotSupportedException JavaDoc;
69 import org.xml.sax.helpers.XMLFilterImpl JavaDoc;
70
71 import org.jdom.*;
72 import org.jdom.DefaultJDOMFactory;
73 import org.jdom.JDOMFactory;
74 import org.jdom.input.SAXBuilder;
75 import org.jdom.input.SAXHandler;
76
77 /**
78  * An XML filter that uses XPath-like expressions to select the
79  * element nodes to build and notifies listeners when these
80  * elements becomes available during the parse.
81  * <p>
82  * ElementScanner does not aim at providing a faster parsing of XML
83  * documents. Its primary focus is to allow the application to
84  * control the parse and to consume the XML data while they are
85  * being parsed. ElementScanner can be viewed as a high-level SAX
86  * parser that fires events conveying JDOM {@link Element elements}
87  * rather that XML tags and character data.</p>
88  * <p>
89  * ElementScanner only notifies of the parsing of element nodes and
90  * does not support reporting the parsing of DOCTYPE data, processing
91  * instructions or comments except for those present within the
92  * selected elements. Application needing such data shall register
93  * a specific {@link ContentHandler} of this filter to receive them
94  * in the form of raw SAX events.</p>
95  * <p>
96  * To be notified of the parsing of JDOM Elements, an application
97  * shall {@link #addElementListener register} objects implementing
98  * the {@link ElementListener} interface. For each registration,
99  * an XPath-like expression defines the elements to be parsed and
100  * reported.</p>
101  * <p>
102  * Opposite to XPath, there is no concept of <i>current context</i>
103  * or <i>current node</i> in ElementScanner. And thus, the syntax
104  * of the "XPath-like expressions" is not as strict as in XPath and
105  * closer to what one uses in XSLT stylesheets in the
106  * <code>match</code> specification of the XSL templates:<br>
107  * In ElementScanner, the expression "<code>x</code>" matches any
108  * element named &quot;x&quot; at any level of the document and not
109  * only the root element (as expected in strict XPath if the
110  * document is considered the <i>current context</i>). Thus, in
111  * ElementScanner, "<code>x</code>" is equivalent to
112  * "<code>//x</code>".</p>
113  * <p>
114  * Example:
115  * <blockquote><pre>
116  * ElementScanner f = new ElementScanner();
117  *
118  * // All descendants of x named y
119  * f.addElementListener(new MyImpl(), "x//y");
120  * // All grandchilden of y named t
121  * f.addElementListener(new MyImpl(), "y/*&thinsp;/t");
122  *
123  * ElementListener l2 = new MyImpl2();
124  * f.addElementListener(l2, "/*"); // Root element
125  * f.addElementListener(l2, "z"); // Any node named z
126  *
127  * ElementListener l3 = new MyImpl3();
128  * // Any node having an attribute "name" whose value contains ".1"
129  * f.addElementListener(l3, "*[contains(@name,'.1')]");
130  * // Any node named y having at least one "y" descendant
131  * f.addElementListener(l3, "y[.//y]");
132  *
133  * f.parse(new InputSource("test.xml"));
134  * </pre></blockquote>
135  * </p>
136  * <p>
137  * The XPath interpreter can be changed (see {@link XPathMatcher}).
138  * The default implementation is a mix of the
139  * <a HREF="http://jakarta.apache.org/regexp/index.html">Jakarta
140  * RegExp package</a> and the
141  * <a HREF="http://www.jaxen.org">Jaxen XPath interpreter</a>.</p>
142  * <p>
143  * ElementScanner splits XPath expressions in 2 parts: a node
144  * selection pattern and an optional test expression (the part of
145  * the XPath between square backets that follow the node selection
146  * pattern).</p>
147  * <p>
148  * Regular expressions are used to match nodes applying the node
149  * selection pattern. This allows matching node without requiring to
150  * build them (as Jaxen does).<br>
151  * If a test expression appears in an XPath expression, Jaxen is used
152  * to match the built elements against it and filter out those not
153  * matching the test.</p>
154  * <p>
155  * As a consequence of using regular expressions, the <i>or</i>"
156  * operator ("<code>|</code>" in XPath) is not supported in node
157  * selection patterns but can be achieved by registering the same
158  * listener several times with different node patterns.</p>
159  * <p>
160  * <strong>Note</strong>: The methods marked with
161  * "<i>[ContentHandler interface support]</i>" below shall not be
162  * invoked by the application. Their usage is reserved to
163  * the XML parser.</p>
164  *
165  * @author Laurent Bihanic
166  */

167 public class ElementScanner extends XMLFilterImpl JavaDoc {
168
169    /**
170     * The registered element listeners, each wrapped in a
171     * XPathMatcher instance.
172     */

173    private final Collection listeners = new ArrayList();
174
175    /**
176     * The <i>SAXBuilder</i> instance to build the JDOM objects used
177     * for parsing the input XML documents. We actually do not need
178     * SAXBuilder per se, we just want to reuse the tons of Java code
179     * this class implements!
180     */

181    private ParserBuilder parserBuilder = new ParserBuilder();
182
183    /**
184     * The <i>SAXHandler</i> instance to build the JDOM Elements.
185     */

186    private SAXHandler saxHandler = null;
187
188    /**
189     * The path of the being parsed element.
190     */

191    private StringBuffer JavaDoc currentPath = new StringBuffer JavaDoc();
192
193    /**
194     * The matching rules active for the current path. It includes
195     * the matching rules active for all the ancestors of the
196     * current node.
197     */

198    private Map activeRules = new HashMap();
199
200    /**
201     * Construct an ElementScanner, with no parent.
202     * <p>
203     * If no parent has been assigned when {@link #parse} is invoked,
204     * ElementScanner will use JAXP to get an instance of the default
205     * SAX parser installed.</p>
206     */

207    public ElementScanner() {
208       super();
209    }
210
211    /**
212     * Constructs an ElementScanner with the specified parent.
213     */

214    public ElementScanner(XMLReader JavaDoc parent) {
215       super(parent);
216    }
217
218    //-------------------------------------------------------------------------
219
// Specific implementation
220
//-------------------------------------------------------------------------
221

222    /**
223     * Adds a new element listener to the list of listeners
224     * maintained by this filter.
225     * <p>
226     * The same listener can be registered several times using
227     * different patterns and several listeners can be registered
228     * using the same pattern.</p>
229     *
230     * @param listener the element listener to add.
231     * @param pattern the XPath expression to select the elements
232     * the listener is interested in.
233     *
234     * @throws JDOMException if <code>listener</code> is null or
235     * the expression is invalid.
236     */

237    public void addElementListener(ElementListener listener, String JavaDoc pattern)
238                                                         throws JDOMException {
239       if (listener != null) {
240          this.listeners.add(XPathMatcher.newXPathMatcher(pattern, listener));
241       }
242       else {
243          throw (new JDOMException("Invalid listener object: <null>"));
244       }
245    }
246
247    /**
248     * Removes element listeners from the list of listeners maintained
249     * by this filter.
250     * <p>
251     * if <code>pattern</code> is <code>null</code>, this method
252     * removes all registrations of <code>listener</code>, regardless
253     * the pattern(s) used for creating the registrations.</p>
254     * <p>
255     * if <code>listener</code> is <code>null</code>, this method
256     * removes all listeners registered for <code>pattern</code>.</p>
257     * <p>
258     * if both <code>listener</code> and <code>pattern</code> are
259     * <code>null</code>, this method performs no action!</p>
260     *
261     * @param listener the element listener to remove.
262     */

263    public void removeElementListener(ElementListener listener, String JavaDoc pattern) {
264       if ((listener != null) || (pattern != null)) {
265          for (Iterator i=this.listeners.iterator(); i.hasNext(); ) {
266             XPathMatcher m = (XPathMatcher)(i.next());
267
268             if (((m.getListener().equals(listener)) || (listener == null)) &&
269                 ((m.getExpression().equals(pattern)) || (pattern == null))) {
270                i.remove();
271             }
272          }
273       }
274       // Else: Both null => Just ignore that dummy call!
275
}
276
277    /**
278     * Returns the list of rules that match the element path and
279     * attributes.
280     *
281     * @param path the current element path.
282     * @param attrs the attributes of the element.
283     *
284     * @return the list of matching rules or <code>null</code> if
285     * no match was found.
286     */

287    private Collection getMatchingRules(String JavaDoc path, Attributes JavaDoc attrs) {
288       Collection matchingRules = null;
289
290       for (Iterator i=this.listeners.iterator(); i.hasNext(); ) {
291          XPathMatcher rule = (XPathMatcher)(i.next());
292
293          if (rule.match(path, attrs)) {
294             if (matchingRules == null) {
295                matchingRules = new ArrayList();
296             }
297             matchingRules.add(rule);
298          }
299       }
300       return (matchingRules);
301    }
302
303    //-------------------------------------------------------------------------
304
// SAXBuilder / SAXHandler configuration helper methods
305
//-------------------------------------------------------------------------
306

307    /**
308     * Sets a custom JDOMFactory for the builder. Use this to build
309     * the tree with your own subclasses of the JDOM classes.
310     *
311     * @param factory <code>JDOMFactory</code> to use.
312     */

313    public void setFactory(JDOMFactory factory) {
314       this.parserBuilder.setFactory(factory);
315    }
316
317    /**
318     * Activates or desactivates validation for the builder.
319     *
320     * @param validate whether XML validation should occur.
321     */

322    public void setValidation(boolean validate) {
323       this.parserBuilder.setValidation(validate);
324    }
325
326    /**
327     * Specifies whether or not the parser should elminate whitespace
328     * in element content (sometimes known as "ignorable whitespace")
329     * when building the document. Only whitespace which is contained
330     * within element content that has an element only content model
331     * will be eliminated (see XML Rec 3.2.1). For this setting to
332     * take effect requires that validation be turned on.
333     * <p>
334     * The default value is <code>false</code>.</p>
335     *
336     * @param ignoringWhite whether to ignore ignorable whitespace.
337     */

338    public void setIgnoringElementContentWhitespace(boolean ignoringWhite) {
339       this.parserBuilder.setIgnoringElementContentWhitespace(ignoringWhite);
340    }
341
342    /**
343     * Sets whether or not to expand entities for the builder.
344     * <p>
345     * A value <code>true</code> means to expand entities as normal
346     * content; <code>false</code> means to leave entities unexpanded
347     * as <code>EntityRef</code> objects.</p>
348     * <p>
349     * The default value is <code>true</code>.</p>
350     *
351     * @param expand whether entity expansion should occur.
352     */

353    public void setExpandEntities(boolean expand) {
354       this.parserBuilder.setExpandEntities(expand);
355    }
356
357    //-------------------------------------------------------------------------
358
// XMLFilterImpl overwritten methods
359
//-------------------------------------------------------------------------
360

361    //-------------------------------------------------------------------------
362
// XMLReader interface support
363
//-------------------------------------------------------------------------
364

365    /**
366     * Sets the state of a feature.
367     *
368     * @param name the feature name, which is a fully-qualified
369     * URI.
370     * @param state the requested state of the feature.
371     *
372     * @throws SAXNotRecognizedException when the XMLReader does not
373     * recognize the feature name.
374     * @throws SAXNotSupportedException when the XMLReader
375     * recognizes the feature name but cannot set the
376     * requested value.
377     */

378    public void setFeature(String JavaDoc name, boolean state)
379                 throws SAXNotRecognizedException JavaDoc, SAXNotSupportedException JavaDoc {
380       if (this.getParent() != null) {
381          this.getParent().setFeature(name, state);
382       }
383       this.parserBuilder.setFeature(name, state);
384    }
385
386    /**
387     * Set the value of a property.
388     *
389     * @param name the property name, which is a fully-qualified
390     * URI.
391     * @param value the requested value for the property.
392     *
393     * @throws SAXNotRecognizedException when the XMLReader does not
394     * recognize the property name.
395     * @throws SAXNotSupportedException when the XMLReader
396     * recognizes the property name but cannot set the
397     * requested value.
398     */

399    public void setProperty(String JavaDoc name, Object JavaDoc value)
400                 throws SAXNotRecognizedException JavaDoc, SAXNotSupportedException JavaDoc {
401       if (this.getParent() != null) {
402          this.getParent().setProperty(name, value);
403       }
404       this.parserBuilder.setProperty(name, value);
405    }
406
407    /**
408     * Parses an XML document.
409     * <p>
410     * The application can use this method to instruct ElementScanner
411     * to begin parsing an XML document from any valid input source
412     * (a character stream, a byte stream, or a URI).</p>
413     * <p>
414     * Applications may not invoke this method while a parse is in
415     * progress. Once a parse is complete, an application may reuse
416     * the same ElementScanner object, possibly with a different input
417     * source.</p>
418     * <p>
419     * This method is synchronous: it will not return until parsing
420     * has ended. If a client application wants to terminate parsing
421     * early, it should throw an exception.</p>
422     *
423     * @param source the input source for the XML document.
424     *
425     * @throws SAXException any SAX exception, possibly wrapping
426     * another exception.
427     * @throws IOException an IO exception from the parser,
428     * possibly from a byte stream or character
429     * stream supplied by the application.
430     */

431    public void parse(InputSource JavaDoc source) throws IOException JavaDoc, SAXException JavaDoc {
432       // Allocate the element builder (SAXHandler subclass).
433
this.saxHandler = this.parserBuilder.getContentHandler();
434
435       // Allocate (if not provided) and configure the parent parser.
436
this.setParent(this.parserBuilder.getXMLReader(
437                                 this.getParent(), this.saxHandler));
438
439       // And delegate to superclass now that everything has been set-up.
440
// Note: super.parse() forces the registration of this filter as
441
// ContentHandler, ErrorHandler, DTDHandler and EntityResolver.
442
super.parse(source);
443    }
444
445    //-------------------------------------------------------------------------
446
// ContentHandler interface support
447
//-------------------------------------------------------------------------
448

449    /**
450     * <i>[ContentHandler interface support]</i> Receives notification
451     * of the beginning of a document.
452     *
453     * @throws SAXException any SAX exception, possibly wrapping
454     * another exception.
455     */

456    public void startDocument() throws SAXException JavaDoc {
457       // Reset state.
458
this.currentPath.setLength(0);
459       this.activeRules.clear();
460
461       // Propagate event.
462
this.saxHandler.startDocument();
463       super.startDocument();
464    }
465
466    /**
467     * <i>[ContentHandler interface support]</i> Receives notification
468     * of the end of a document.
469     *
470     * @throws SAXException any SAX exception, possibly wrapping
471     * another exception.
472     */

473    public void endDocument() throws SAXException JavaDoc {
474       // Propagate event.
475
this.saxHandler.endDocument();
476       super.endDocument();
477    }
478
479    /**
480     * <i>[ContentHandler interface support]</i> Begins the scope of
481     * a prefix-URI Namespace mapping.
482     *
483     * @param prefix the Namespace prefix being declared.
484     * @param uri the Namespace URI the prefix is mapped to.
485     *
486     * @throws SAXException any SAX exception, possibly wrapping
487     * another exception.
488     */

489    public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri)
490                                                         throws SAXException JavaDoc {
491       // Propagate event.
492
this.saxHandler.startPrefixMapping(prefix, uri);
493       super.startPrefixMapping(prefix, uri);
494    }
495
496    /**
497     * <i>[ContentHandler interface support]</i> Ends the scope of a
498     * prefix-URI Namespace mapping.
499     *
500     * @param prefix the prefix that was being mapped.
501     *
502     * @throws SAXException any SAX exception, possibly wrapping
503     * another exception.
504     */

505    public void endPrefixMapping(String JavaDoc prefix) throws SAXException JavaDoc {
506       // Propagate event.
507
this.saxHandler.endPrefixMapping(prefix);
508       super.endPrefixMapping(prefix);
509    }
510
511    /**
512     * <i>[ContentHandler interface support]</i> Receives notification
513     * of the beginning of an element.
514     *
515     * @param nsUri the Namespace URI, or the empty string if
516     * the element has no Namespace URI or if
517     * Namespace processing is not being performed.
518     * @param localName the local name (without prefix), or the
519     * empty string if Namespace processing is
520     * not being performed.
521     * @param qName the qualified name (with prefix), or the
522     * empty string if qualified names are not
523     * available.
524     * @param attrs the attributes attached to the element. If
525     * there are no attributes, it shall be an
526     * empty Attributes object.
527     *
528     * @throws SAXException any SAX exception, possibly wrapping
529     * another exception.
530     */

531    public void startElement(String JavaDoc nsUri, String JavaDoc localName,
532                             String JavaDoc qName, Attributes JavaDoc attrs)
533                                                         throws SAXException JavaDoc {
534       // Append new element to the current path.
535
this.currentPath.append('/').append(localName);
536
537       // Retrieve the matching rules for this element.
538
String JavaDoc eltPath = this.currentPath.substring(0);
539       Collection matchingRules = this.getMatchingRules(eltPath, attrs);
540       if (matchingRules != null) {
541          // Matching rules found.
542
// => Make them active to trigger element building.
543
this.activeRules.put(eltPath, matchingRules);
544       }
545
546       // Propagate event.
547
if (this.activeRules.size() != 0) {
548          this.saxHandler.startElement(nsUri, localName, qName, attrs);
549       }
550       super.startElement(nsUri, localName, qName, attrs);
551    }
552
553    /**
554     * <i>[ContentHandler interface support]</i> Receives notification
555     * of the end of an element.
556     *
557     * @param nsUri the Namespace URI, or the empty string if
558     * the element has no Namespace URI or if
559     * Namespace processing is not being performed.
560     * @param localName the local name (without prefix), or the
561     * empty string if Namespace processing is
562     * not being performed.
563     * @param qName the qualified name (with prefix), or the
564     * empty string if qualified names are not
565     * available.
566     *
567     * @throws SAXException any SAX exception, possibly wrapping
568     * another exception.
569     */

570    public void endElement(String JavaDoc nsUri, String JavaDoc localName, String JavaDoc qName)
571                                                         throws SAXException JavaDoc {
572       // Grab the being-built element.
573
Element elt = this.saxHandler.getCurrentElement();
574
575       // Complete element building before making use of it.
576
// (This sets the current element to the parent of elt.)
577
if (this.activeRules.size() != 0) {
578          this.saxHandler.endElement(nsUri, localName, qName);
579       }
580
581       // Get the matching rules for this element (if any).
582
String JavaDoc eltPath = this.currentPath.substring(0);
583       Collection matchingRules = (Collection)(this.activeRules.remove(eltPath));
584       if (matchingRules != null) {
585          // Matching rules exist. => Notify all matching listeners.
586
try {
587             for (Iterator i=matchingRules.iterator(); i.hasNext(); ) {
588                XPathMatcher matcher = (XPathMatcher)(i.next());
589
590                if (matcher.match(eltPath, elt)) {
591                   matcher.getListener().elementMatched(eltPath, elt);
592                }
593             }
594          }
595          catch (JDOMException ex1) {
596             // Oops! Listener-originated exception.
597
// => Fire a SAXException to abort parsing.
598
throw (new SAXException JavaDoc(ex1.getMessage(), ex1));
599          }
600       }
601       // Remove notified element from the current path.
602
this.currentPath.setLength(
603                         this.currentPath.length() - (localName.length() + 1));
604       // Propagate event.
605
super.endElement(nsUri, localName, qName);
606    }
607
608    /**
609     * <i>[ContentHandler interface support]</i> Receives notification
610     * of character data.
611     *
612     * @param ch the characters from the XML document.
613     * @param start the start position in the array.
614     * @param length the number of characters to read from the array.
615     *
616     * @throws SAXException any SAX exception, possibly wrapping
617     * another exception.
618     */

619    public void characters(char[] ch, int start, int length)
620                                                         throws SAXException JavaDoc {
621       // Propagate event.
622
if (this.activeRules.size() != 0) {
623          this.saxHandler.characters(ch, start, length);
624       }
625       super.characters(ch, start, length);
626    }
627
628    /**
629     * <i>[ContentHandler interface support]</i> Receives notification
630     * of ignorable whitespace in element content.
631     *
632     * @param ch the characters from the XML document.
633     * @param start the start position in the array.
634     * @param length the number of characters to read from the array.
635     *
636     * @throws SAXException any SAX exception, possibly wrapping
637     * another exception.
638     */

639    public void ignorableWhitespace(char[] ch, int start, int length)
640                                                         throws SAXException JavaDoc {
641       // Propagate event.
642
if (this.activeRules.size() != 0) {
643          this.saxHandler.ignorableWhitespace(ch, start, length);
644       }
645       super.ignorableWhitespace(ch, start, length);
646    }
647
648    /**
649     * <i>[ContentHandler interface support]</i> Receives notification
650     * of processing instruction.
651     *
652     * @param target the processing instruction target.
653     * @param data the processing instruction data, or
654     * <code>null</code> if none was supplied.
655     *
656     * @throws SAXException any SAX exception, possibly wrapping
657     * another exception.
658     */

659    public void processingInstruction(String JavaDoc target, String JavaDoc data)
660                                                         throws SAXException JavaDoc {
661       // Propagate event.
662
if (this.activeRules.size() != 0) {
663          this.saxHandler.processingInstruction(target, data);
664       }
665       super.processingInstruction(target, data);
666    }
667
668    /**
669     * <i>[ContentHandler interface support]</i> Receives notification
670     * of a skipped entity.
671     *
672     * @param name the name of the skipped entity.
673     *
674     * @throws SAXException any SAX exception, possibly wrapping
675     * another exception.
676     */

677    public void skippedEntity(String JavaDoc name) throws SAXException JavaDoc {
678       // Propagate event.
679
if (this.activeRules.size() != 0) {
680          this.saxHandler.skippedEntity(name);
681       }
682       super.skippedEntity(name);
683    }
684
685
686    //=========================================================================
687
// Deviant implementations of JDOM builder objects
688
//=========================================================================
689

690    //-------------------------------------------------------------------------
691
// ParserBuilder nested class
692
//-------------------------------------------------------------------------
693

694    /**
695     * ParserBuilder extends SAXBuilder to provide access to the
696     * protected methods of this latter and to allow us using
697     * customized SAXHandler and JDOMFactory implementations.
698     */

699    private static class ParserBuilder extends SAXBuilder {
700
701       public ParserBuilder() {
702          super();
703       }
704
705       //----------------------------------------------------------------------
706
// SAXBuilder overwritten methods
707
//----------------------------------------------------------------------
708

709       protected SAXHandler createContentHandler() {
710          return (new FragmentHandler(new EmptyDocumentFactory(getFactory())));
711       }
712
713       //----------------------------------------------------------------------
714
// Specific implementation
715
//----------------------------------------------------------------------
716

717       /**
718        * Allocates and configures a new XMLReader instance. If a
719        * parser is provided, it will be used; otherwise a new parser
720        * will be created.
721        *
722        * @param parser the parser to configure, <code>null</code>
723        * if one shall be allocated.
724        * @param handler the SAX ContentHandler to register on the
725        * parser.
726        *
727        * @return the configured parser.
728        *
729        * @throws SAXException any SAX exception, possibly wrapping
730        * another exception.
731        */

732       public XMLReader JavaDoc getXMLReader(XMLReader JavaDoc parser, SAXHandler handler)
733                                                         throws SAXException JavaDoc {
734          try {
735             // Allocate a SAX parser if none was provided.
736
if (parser == null) {
737                parser = this.createParser();
738             }
739             // And configure the parser with the ContentHandler.
740
this.configureParser(parser, handler);
741
742             return (parser);
743          }
744          catch (Exception JavaDoc ex1) {
745             throw (new SAXException JavaDoc(ex1.getMessage(), ex1));
746          }
747       }
748
749       /**
750        * Allocates and configures a new SAXHandler object.
751        *
752        * @return the configured SAXHandler.
753        *
754        * @throws SAXException any SAX exception, possibly wrapping
755        * another exception.
756        */

757       public SAXHandler getContentHandler() throws SAXException JavaDoc {
758          try {
759             SAXHandler handler = this.createContentHandler();
760             this.configureContentHandler(handler);
761
762             return (handler);
763          }
764          catch (Exception JavaDoc ex1) {
765             throw (new SAXException JavaDoc(ex1.getMessage(), ex1));
766          }
767       }
768    }
769
770    //-------------------------------------------------------------------------
771
// FragmentHandler nested class
772
//-------------------------------------------------------------------------
773

774    /**
775     * FragmentHandler extends SAXHandler to support matching nodes
776     * without a common ancestor. This class inserts a dummy root
777     * element in the being-built document. This prevents the document
778     * to have, from SAXHandler's point of view, multiple root
779     * elements (which would cause the parse to fail).
780     */

781    private static class FragmentHandler extends SAXHandler {
782       /**
783        * Public constructor.
784        */

785       public FragmentHandler(JDOMFactory factory) {
786          super(factory);
787
788          // Add a dummy root element to the being-built document as XSL
789
// transformation can output node lists instead of well-formed
790
// documents.
791
this.pushElement(new Element("root", null, null));
792       }
793    }
794
795    //-------------------------------------------------------------------------
796
// EmptyDocumentFactory nested class
797
//-------------------------------------------------------------------------
798

799    /**
800     * EmptyDocumentFactory is an implementation of JDOMFactory that
801     * wraps an application-provided factory and delegates all
802     * method calls to this latter except for document creation calls
803     * it intercepts to return instances of EmptyDocument.
804     */

805    private static class EmptyDocumentFactory implements JDOMFactory {
806
807       /**
808        * The wrapped JDOM factory implementation.
809        */

810       private final JDOMFactory wrapped;
811
812       /**
813        * Creates a new EmptyDocumentFactory wrapping the specified
814        * JDOM factory implementation.
815        *
816        * @param factory the JDOM factory implementation to wrap
817        * or <code>null</code> to use the default
818        * JDOM factory.
819        */

820       public EmptyDocumentFactory(JDOMFactory factory) {
821          this.wrapped = (factory != null)? factory: new DefaultJDOMFactory();
822       }
823
824       //----------------------------------------------------------------------
825
// Redefined JDOMFactory methods
826
//----------------------------------------------------------------------
827

828       public Document document(Element rootElement, DocType docType) {
829          return (new EmptyDocument());
830       }
831
832       public Document document(Element rootElement, DocType docType,
833                                String JavaDoc baseURI) {
834          return (new EmptyDocument());
835       }
836
837       public Document document(Element rootElement) {
838          return (new EmptyDocument());
839       }
840
841       //----------------------------------------------------------------------
842
// Delegated JDOMFactory methods
843
//----------------------------------------------------------------------
844

845       public Attribute attribute(String JavaDoc name, String JavaDoc value,
846                                               Namespace namespace) {
847          return(this.wrapped.attribute(name, value, namespace));
848       }
849       public Attribute attribute(String JavaDoc name, String JavaDoc value, int type,
850                                               Namespace namespace) {
851          return(this.wrapped.attribute(name, value, type, namespace));
852       }
853       public Attribute attribute(String JavaDoc name, String JavaDoc value) {
854          return(this.wrapped.attribute(name, value));
855       }
856       public Attribute attribute(String JavaDoc name, String JavaDoc value, int type) {
857          return(this.wrapped.attribute(name, value, type));
858       }
859       public CDATA cdata(String JavaDoc text) {
860          return(this.wrapped.cdata(text));
861       }
862       public Text text(String JavaDoc text) {
863          return(this.wrapped.text(text));
864       }
865       public Comment comment(String JavaDoc text) {
866          return(this.wrapped.comment(text));
867       }
868       public DocType docType(String JavaDoc elementName,
869                              String JavaDoc publicID, String JavaDoc systemID) {
870          return(this.wrapped.docType(elementName, publicID, systemID));
871       }
872       public DocType docType(String JavaDoc elementName, String JavaDoc systemID) {
873          return(this.wrapped.docType(elementName, systemID));
874       }
875       public DocType docType(String JavaDoc elementName) {
876          return(this.wrapped.docType(elementName));
877       }
878       public Element element(String JavaDoc name, Namespace namespace) {
879          return(this.wrapped.element(name, namespace));
880       }
881       public Element element(String JavaDoc name) {
882          return(this.wrapped.element(name));
883       }
884       public Element element(String JavaDoc name, String JavaDoc uri) {
885          return(this.wrapped.element(name, uri));
886       }
887       public Element element(String JavaDoc name, String JavaDoc prefix, String JavaDoc uri) {
888          return(this.wrapped.element(name, prefix, uri));
889       }
890       public ProcessingInstruction processingInstruction(String JavaDoc target,
891                                                          Map data) {
892          return(this.wrapped.processingInstruction(target, data));
893       }
894       public ProcessingInstruction processingInstruction(String JavaDoc target,
895                                                          String JavaDoc data) {
896          return(this.wrapped.processingInstruction(target, data));
897       }
898       public EntityRef entityRef(String JavaDoc name) {
899          return(this.wrapped.entityRef(name));
900       }
901       public EntityRef entityRef(String JavaDoc name,
902                                  String JavaDoc publicID, String JavaDoc systemID) {
903          return(this.wrapped.entityRef(name, publicID, systemID));
904       }
905       public EntityRef entityRef(String JavaDoc name, String JavaDoc systemID) {
906          return(this.wrapped.entityRef(name, systemID));
907       }
908
909        public void addContent(Parent parent, Content c) {
910            if (parent instanceof Element) {
911                ((Element) parent).addContent(c);
912            }
913            else {
914                ((Document) parent).addContent(c);
915            }
916        }
917
918        public void setAttribute(Element element, Attribute a) {
919            element.setAttribute(a);
920        }
921
922        public void addNamespaceDeclaration(Element element, Namespace additional) {
923            element.addNamespaceDeclaration(additional);
924        }
925
926
927    }
928
929    //-------------------------------------------------------------------------
930
// EmptyDocument nested class
931
//-------------------------------------------------------------------------
932

933    /**
934     * EmptyDocument extends the standard JDOM Document object to
935     * overwrite all methods setting document-level information to
936     * ensure the document always remains empty.
937     * <p>
938     * This implementation is a kludge to prevent SAXHandler to
939     * actually build a document with the matched elements as root
940     * elements (yes there can be several in our case)!</p>
941     */

942    private static class EmptyDocument extends Document {
943
944       /**
945        * Default constructor.
946        */

947       public EmptyDocument() {
948          super();
949       }
950
951       //----------------------------------------------------------------------
952
// Document overwritten methods
953
//----------------------------------------------------------------------
954

955       public Document setRootElement(Element root) { return(this); }
956       public Document addContent(Comment comment) { return(this); }
957       public Document addContent(ProcessingInstruction pi) { return(this); }
958       public Document setContent(List newContent) { return(this); }
959       public Document setDocType(DocType docType) { return(this); }
960    }
961 }
962
963
Popular Tags