KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdom > transform > JDOMResult


1 /*--
2
3  $Id: JDOMResult.java,v 1.23 2004/08/31 06:10:38 jhunter Exp $
4
5  Copyright (C) 2001-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.transform;
58
59 import java.util.*;
60
61 import javax.xml.transform.sax.*;
62
63 import org.jdom.*;
64 import org.jdom.input.*;
65 import org.xml.sax.*;
66 import org.xml.sax.ext.*;
67 import org.xml.sax.helpers.*;
68
69 /**
70  * A holder for an XSL Transformation result, generally a list of nodes
71  * although it can be a JDOM Document also. As stated by the XSLT 1.0
72  * specification, the result tree generated by an XSL transformation is not
73  * required to be a well-formed XML document. The result tree may have "any
74  * sequence of nodes as children that would be possible for an
75  * element node".
76  * <p>
77  * The following example shows how to apply an XSL Transformation
78  * to a JDOM document and get the transformation result in the form
79  * of a list of JDOM nodes:
80  * <pre><code>
81  * public static List transform(Document doc, String stylesheet)
82  * throws JDOMException {
83  * try {
84  * Transformer transformer = TransformerFactory.newInstance()
85  * .newTransformer(new StreamSource(stylesheet));
86  * JDOMSource in = new JDOMSource(doc);
87  * JDOMResult out = new JDOMResult();
88  * transformer.transform(in, out);
89  * return out.getResult();
90  * }
91  * catch (TransformerException e) {
92  * throw new JDOMException("XSLT Transformation failed", e);
93  * }
94  * }
95  * </code></pre>
96  *
97  * @see org.jdom.transform.JDOMSource
98  *
99  * @version $Revision: 1.23 $, $Date: 2004/08/31 06:10:38 $
100  * @author Laurent Bihanic
101  * @author Jason Hunter
102  */

103 public class JDOMResult extends SAXResult {
104
105     private static final String JavaDoc CVS_ID =
106     "@(#) $RCSfile: JDOMResult.java,v $ $Revision: 1.23 $ $Date: 2004/08/31 06:10:38 $ $Name: $";
107
108   /**
109    * If {@link javax.xml.transform.TransformerFactory#getFeature}
110    * returns <code>true</code> when passed this value as an
111    * argument, the Transformer natively supports JDOM.
112    * <p>
113    * <strong>Note</strong>: This implementation does not override
114    * the {@link SAXResult#FEATURE} value defined by its superclass
115    * to be considered as a SAXResult by Transformer implementations
116    * not natively supporting JDOM.</p>
117    */

118   public final static String JavaDoc JDOM_FEATURE =
119                       "http://org.jdom.transform.JDOMResult/feature";
120
121   /**
122    * The result of a transformation, as set by Transformer
123    * implementations that natively support JDOM, as a JDOM document
124    * or a list of JDOM nodes.
125    */

126   private Object JavaDoc result = null;
127
128   /**
129    * Whether the application queried the result (as a list or a
130    * document) since it was last set.
131    */

132   private boolean queried = false;
133
134   /**
135    * The custom JDOM factory to use when building the transformation
136    * result or <code>null</code> to use the default JDOM classes.
137    */

138   private JDOMFactory factory = null;
139
140   /**
141    * Public default constructor.
142    */

143   public JDOMResult() {
144     // Allocate custom builder object...
145
DocumentBuilder builder = new DocumentBuilder();
146
147     // And use it as ContentHandler and LexicalHandler.
148
super.setHandler(builder);
149     super.setLexicalHandler(builder);
150   }
151
152   /**
153    * Sets the object(s) produced as result of an XSL Transformation.
154    * <p>
155    * <strong>Note</strong>: This method shall be used by the
156    * {@link javax.xml.transform.Transformer} implementations that
157    * natively support JDOM to directly set the transformation
158    * result rather than considering this object as a
159    * {@link SAXResult}. Applications should <i>not</i> use this
160    * method.</p>
161    *
162    * @param result the result of a transformation as a
163    * {@link java.util.List list} of JDOM nodes
164    * (Elements, Texts, Comments, PIs...).
165    *
166    * @see #getResult
167    */

168   public void setResult(List result) {
169     this.result = result;
170     this.queried = false;
171   }
172
173   /**
174    * Returns the result of an XSL Transformation as a list of JDOM
175    * nodes.
176    * <p>
177    * If the result of the transformation is a JDOM document,
178    * this method converts it into a list of JDOM nodes; any
179    * subsequent call to {@link #getDocument} will return
180    * <code>null</code>.</p>
181    *
182    * @return the transformation result as a (possibly empty) list of
183    * JDOM nodes (Elements, Texts, Comments, PIs...).
184    */

185   public List getResult() {
186     List nodes = Collections.EMPTY_LIST;
187
188     // Retrieve result from the document builder if not set.
189
this.retrieveResult();
190
191     if (result instanceof List) {
192       nodes = (List)result;
193     }
194     else {
195       if ((result instanceof Document) && (queried == false)) {
196         List content = ((Document)result).getContent();
197         nodes = new ArrayList(content.size());
198
199         while (content.size() != 0)
200         {
201           Object JavaDoc o = content.remove(0);
202           nodes.add(o);
203         }
204         result = nodes;
205       }
206     }
207     queried = true;
208
209     return (nodes);
210   }
211
212   /**
213    * Sets the document produced as result of an XSL Transformation.
214    * <p>
215    * <strong>Note</strong>: This method shall be used by the
216    * {@link javax.xml.transform.Transformer} implementations that
217    * natively support JDOM to directly set the transformation
218    * result rather than considering this object as a
219    * {@link SAXResult}. Applications should <i>not</i> use this
220    * method.</p>
221    *
222    * @param document the JDOM document result of a transformation.
223    *
224    * @see #setResult
225    * @see #getDocument
226    */

227   public void setDocument(Document document) {
228     this.result = document;
229     this.queried = false;
230   }
231
232   /**
233    * Returns the result of an XSL Transformation as a JDOM document.
234    * <p>
235    * If the result of the transformation is a list of nodes,
236    * this method attempts to convert it into a JDOM document. If
237    * successful, any subsequent call to {@link #getResult} will
238    * return an empty list.</p>
239    * <p>
240    * <strong>Warning</strong>: The XSLT 1.0 specification states that
241    * the output of an XSL transformation is not a well-formed XML
242    * document but a list of nodes. Applications should thus use
243    * {@link #getResult} instead of this method or at least expect
244    * <code>null</code> documents to be returned.
245    *
246    * @return the transformation result as a JDOM document or
247    * <code>null</code> if the result of the transformation
248    * can not be converted into a well-formed document.
249    *
250    * @see #getResult
251    */

252   public Document getDocument() {
253     Document doc = null;
254
255     // Retrieve result from the document builder if not set.
256
this.retrieveResult();
257
258     if (result instanceof Document) {
259       doc = (Document)result;
260     }
261     else {
262       if ((result instanceof List) && (queried == false)) {
263         // Try to create a document from the result nodes
264
try {
265           JDOMFactory f = this.getFactory();
266           if (f == null) { f = new DefaultJDOMFactory(); }
267
268           doc = f.document(null);
269           doc.setContent((List)result);
270
271           result = doc;
272         }
273         catch (RuntimeException JavaDoc ex1) {
274           // Some of the result nodes are not valid children of a
275
// Document node. => return null.
276
}
277       }
278     }
279     queried = true;
280
281     return (doc);
282   }
283
284   /**
285    * Sets a custom JDOMFactory to use when building the
286    * transformation result. Use a custom factory to build the tree
287    * with your own subclasses of the JDOM classes.
288    *
289    * @param factory the custom <code>JDOMFactory</code> to use or
290    * <code>null</code> to use the default JDOM
291    * classes.
292    *
293    * @see #getFactory
294    */

295   public void setFactory(JDOMFactory factory) {
296     this.factory = factory;
297   }
298
299   /**
300    * Returns the custom JDOMFactory used to build the transformation
301    * result.
302    *
303    * @return the custom <code>JDOMFactory</code> used to build the
304    * transformation result or <code>null</code> if the
305    * default JDOM classes are being used.
306    *
307    * @see #setFactory
308    */

309   public JDOMFactory getFactory() {
310     return this.factory;
311   }
312
313   /**
314    * Checks whether a transformation result has been set and, if not,
315    * retrieves the result tree being built by the document builder.
316    */

317   private void retrieveResult() {
318     if (result == null) {
319       this.setResult(((DocumentBuilder)this.getHandler()).getResult());
320     }
321   }
322
323   //-------------------------------------------------------------------------
324
// SAXResult overwritten methods
325
//-------------------------------------------------------------------------
326

327   /**
328    * Sets the target to be a SAX2 ContentHandler.
329    *
330    * @param handler Must be a non-null ContentHandler reference.
331    */

332   public void setHandler(ContentHandler handler) { }
333
334   /**
335    * Sets the SAX2 LexicalHandler for the output.
336    * <p>
337    * This is needed to handle XML comments and the like. If the
338    * lexical handler is not set, an attempt should be made by the
339    * transformer to cast the ContentHandler to a LexicalHandler.</p>
340    *
341    * @param handler A non-null LexicalHandler for
342    * handling lexical parse events.
343    */

344   public void setLexicalHandler(LexicalHandler handler) { }
345
346
347   //=========================================================================
348
// FragmentHandler nested class
349
//=========================================================================
350

351   private static class FragmentHandler extends SAXHandler {
352     /**
353      * A dummy root element required by SAXHandler that can only
354      * cope with well-formed documents.
355      */

356     private Element dummyRoot = new Element("root", null, null);
357
358     /**
359      * Public constructor.
360      */

361     public FragmentHandler(JDOMFactory factory) {
362       super(factory);
363
364       // Add a dummy root element to the being-built document as XSL
365
// transformation can output node lists instead of well-formed
366
// documents.
367
this.pushElement(dummyRoot);
368     }
369
370     /**
371      * Returns the result of an XSL Transformation.
372      *
373      * @return the transformation result as a (possibly empty) list of
374      * JDOM nodes (Elements, Texts, Comments, PIs...).
375      */

376     public List getResult() {
377       // Flush remaining text content in case the last text segment is
378
// outside an element.
379
try {
380         this.flushCharacters();
381       }
382       catch (SAXException e) { /* Ignore... */ }
383       return this.getDetachedContent(dummyRoot);
384     }
385
386     /**
387      * Returns the content of a JDOM Element detached from it.
388      *
389      * @param elt the element to get the content from.
390      *
391      * @return a (possibly empty) list of JDOM nodes, detached from
392      * their parent.
393      */

394     private List getDetachedContent(Element elt) {
395       List content = elt.getContent();
396       List nodes = new ArrayList(content.size());
397
398       while (content.size() != 0)
399       {
400         Object JavaDoc o = content.remove(0);
401         nodes.add(o);
402       }
403       return (nodes);
404     }
405   }
406
407   //=========================================================================
408
// DocumentBuilder inner class
409
//=========================================================================
410

411   private class DocumentBuilder extends XMLFilterImpl
412                                 implements LexicalHandler {
413     /**
414      * The actual JDOM document builder.
415      */

416     private FragmentHandler saxHandler = null;
417
418     /**
419      * Whether the startDocument event was received. Some XSLT
420      * processors such as Oracle's do not fire this event.
421      */

422     private boolean startDocumentReceived = false;
423
424     /**
425      * Public default constructor.
426      */

427     public DocumentBuilder() { }
428
429     /**
430      * Returns the result of an XSL Transformation.
431      *
432      * @return the transformation result as a (possibly empty) list of
433      * JDOM nodes (Elements, Texts, Comments, PIs...) or
434      * <code>null</code> if no new transformation occurred
435      * since the result of the previous one was returned.
436      */

437     public List getResult() {
438       List result = null;
439
440       if (this.saxHandler != null) {
441         // Retrieve result from SAX content handler.
442
result = this.saxHandler.getResult();
443
444         // Detach the (non-reusable) SAXHandler instance.
445
this.saxHandler = null;
446
447         // And get ready for the next transformation.
448
this.startDocumentReceived = false;
449       }
450       return result;
451     }
452
453     private void ensureInitialization() throws SAXException {
454       // Trigger document initialization if XSLT processor failed to
455
// fire the startDocument event.
456
if (this.startDocumentReceived == false) {
457         this.startDocument();
458       }
459     }
460
461     //-----------------------------------------------------------------------
462
// XMLFilterImpl overwritten methods
463
//-----------------------------------------------------------------------
464

465     /**
466      * <i>[SAX ContentHandler interface support]</i> Processes a
467      * start of document event.
468      * <p>
469      * This implementation creates a new JDOM document builder and
470      * marks the current result as "under construction".</p>
471      *
472      * @throws SAXException if any error occurred while creating
473      * the document builder.
474      */

475     public void startDocument() throws SAXException {
476       this.startDocumentReceived = true;
477
478       // Reset any previously set result.
479
setResult(null);
480
481       // Create the actual JDOM document builder and register it as
482
// ContentHandler on the superclass (XMLFilterImpl): this
483
// implementation will take care of propagating the LexicalHandler
484
// events.
485
this.saxHandler = new FragmentHandler(getFactory());
486       super.setContentHandler(this.saxHandler);
487
488       // And propagate event.
489
super.startDocument();
490     }
491
492     /**
493      * <i>[SAX ContentHandler interface support]</i> Receives
494      * notification of the beginning of an element.
495      * <p>
496      * This implementation ensures that startDocument() has been
497      * called prior processing an element.
498      *
499      * @param nsURI the Namespace URI, or the empty string if
500      * the element has no Namespace URI or if
501      * Namespace processing is not being performed.
502      * @param localName the local name (without prefix), or the
503      * empty string if Namespace processing is
504      * not being performed.
505      * @param qName the qualified name (with prefix), or the
506      * empty string if qualified names are not
507      * available.
508      * @param atts The attributes attached to the element. If
509      * there are no attributes, it shall be an
510      * empty Attributes object.
511      *
512      * @throws SAXException if any error occurred while creating
513      * the document builder.
514      */

515     public void startElement(String JavaDoc nsURI, String JavaDoc localName, String JavaDoc qName,
516                                            Attributes atts) throws SAXException
517     {
518       this.ensureInitialization();
519       super.startElement(nsURI, localName, qName, atts);
520     }
521
522     /**
523      * <i>[SAX ContentHandler interface support]</i> Begins the
524      * scope of a prefix-URI Namespace mapping.
525      */

526     public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri)
527                                                         throws SAXException {
528       this.ensureInitialization();
529       super.startPrefixMapping(prefix, uri);
530     }
531
532     /**
533      * <i>[SAX ContentHandler interface support]</i> Receives
534      * notification of character data.
535      */

536     public void characters(char ch[], int start, int length)
537                                                         throws SAXException {
538       this.ensureInitialization();
539       super.characters(ch, start, length);
540     }
541
542     /**
543      * <i>[SAX ContentHandler interface support]</i> Receives
544      * notification of ignorable whitespace in element content.
545      */

546     public void ignorableWhitespace(char ch[], int start, int length)
547                                                         throws SAXException {
548       this.ensureInitialization();
549       super.ignorableWhitespace(ch, start, length);
550     }
551
552     /**
553      * <i>[SAX ContentHandler interface support]</i> Receives
554      * notification of a processing instruction.
555      */

556     public void processingInstruction(String JavaDoc target, String JavaDoc data)
557                                                         throws SAXException {
558       this.ensureInitialization();
559       super.processingInstruction(target, data);
560     }
561
562     /**
563      * <i>[SAX ContentHandler interface support]</i> Receives
564      * notification of a skipped entity.
565      */

566     public void skippedEntity(String JavaDoc name) throws SAXException {
567       this.ensureInitialization();
568       super.skippedEntity(name);
569     }
570
571     //-----------------------------------------------------------------------
572
// LexicalHandler interface support
573
//-----------------------------------------------------------------------
574

575     /**
576      * <i>[SAX LexicalHandler interface support]</i> Reports the
577      * start of DTD declarations, if any.
578      *
579      * @param name the document type name.
580      * @param publicId the declared public identifier for the
581      * external DTD subset, or <code>null</code>
582      * if none was declared.
583      * @param systemId the declared system identifier for the
584      * external DTD subset, or <code>null</code>
585      * if none was declared.
586      *
587      * @throws SAXException The application may raise an exception.
588      */

589     public void startDTD(String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId)
590                                         throws SAXException {
591       this.ensureInitialization();
592       this.saxHandler.startDTD(name, publicId, systemId);
593     }
594
595     /**
596      * <i>[SAX LexicalHandler interface support]</i> Reports the end
597      * of DTD declarations.
598      *
599      * @throws SAXException The application may raise an exception.
600      */

601     public void endDTD() throws SAXException {
602       this.saxHandler.endDTD();
603     }
604
605     /**
606      * <i>[SAX LexicalHandler interface support]</i> Reports the
607      * beginning of some internal and external XML entities.
608      *
609      * @param name the name of the entity. If it is a parameter
610      * entity, the name will begin with '%', and if it
611      * is the external DTD subset, it will be "[dtd]".
612      *
613      * @throws SAXException The application may raise an exception.
614      */

615     public void startEntity(String JavaDoc name) throws SAXException {
616       this.ensureInitialization();
617       this.saxHandler.startEntity(name);
618     }
619
620     /**
621      * <i>[SAX LexicalHandler interface support]</i> Reports the end
622      * of an entity.
623      *
624      * @param name the name of the entity that is ending.
625      *
626      * @throws SAXException The application may raise an exception.
627      */

628     public void endEntity(String JavaDoc name) throws SAXException {
629       this.saxHandler.endEntity(name);
630     }
631
632     /**
633      * <i>[SAX LexicalHandler interface support]</i> Reports the
634      * start of a CDATA section.
635      *
636      * @throws SAXException The application may raise an exception.
637      */

638     public void startCDATA() throws SAXException {
639       this.ensureInitialization();
640       this.saxHandler.startCDATA();
641     }
642
643     /**
644      * <i>[SAX LexicalHandler interface support]</i> Reports the end
645      * of a CDATA section.
646      *
647      * @throws SAXException The application may raise an exception.
648      */

649     public void endCDATA() throws SAXException {
650       this.saxHandler.endCDATA();
651     }
652
653     /**
654      * <i>[SAX LexicalHandler interface support]</i> Reports an XML
655      * comment anywhere in the document.
656      *
657      * @param ch an array holding the characters in the comment.
658      * @param start the starting position in the array.
659      * @param length the number of characters to use from the array.
660      *
661      * @throws SAXException The application may raise an exception.
662      */

663     public void comment(char ch[], int start, int length)
664                                   throws SAXException {
665       this.ensureInitialization();
666       this.saxHandler.comment(ch, start, length);
667     }
668   }
669 }
670
671
Popular Tags