KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > nu > xom > xslt > XSLTransform


1 /* Copyright 2002-2005 Elliotte Rusty Harold
2    
3    This library is free software; you can redistribute it and/or modify
4    it under the terms of version 2.1 of the GNU Lesser General Public
5    License as published by the Free Software Foundation.
6    
7    This library is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10    GNU Lesser General Public License for more details.
11    
12    You should have received a copy of the GNU Lesser General Public
13    License along with this library; if not, write to the
14    Free Software Foundation, Inc., 59 Temple Place, Suite 330,
15    Boston, MA 02111-1307 USA
16    
17    You can contact Elliotte Rusty Harold by sending e-mail to
18    elharo@metalab.unc.edu. Please include the word "XOM" in the
19    subject line. The XOM home page is located at http://www.xom.nu/
20 */

21
22 package nu.xom.xslt;
23
24 import javax.xml.transform.OutputKeys JavaDoc;
25 import javax.xml.transform.Source JavaDoc;
26 import javax.xml.transform.Templates JavaDoc;
27 import javax.xml.transform.Transformer JavaDoc;
28 import javax.xml.transform.TransformerConfigurationException JavaDoc;
29 import javax.xml.transform.TransformerException JavaDoc;
30 import javax.xml.transform.TransformerFactoryConfigurationError JavaDoc;
31 import javax.xml.transform.TransformerFactory JavaDoc;
32
33 import org.xml.sax.SAXParseException JavaDoc;
34
35 import nu.xom.Document;
36 import nu.xom.Element;
37 import nu.xom.NodeFactory;
38 import nu.xom.Nodes;
39 import nu.xom.XMLException;
40
41 /**
42  * <p>
43  * Serves as an interface to a TrAX aware XSLT processor such as Xalan
44  * or Saxon. The following example shows how to apply an XSL
45  * Transformation to a XOM document and get the transformation result
46  * in the form of a XOM <code>Nodes</code>:</p>
47  * <blockquote><pre>public static Nodes transform(Document in)
48  * throws XSLException, ParsingException, IOException {
49  * Builder builder = new Builder();
50  * Document stylesheet = builder.build("mystylesheet.xsl");
51  * XSLTransform stylesheet = new XSLTransform(stylesheet);
52  * return stylesheet.transform(doc);
53  * } </pre></blockquote>
54  *
55  * <p>
56  * XOM relies on TrAX to perform the transformation.
57  * The <code>javax.xml.transform.TransformerFactory</code> Java
58  * system property determines which XSLT engine TrAX uses. Its
59  * value should be the fully qualified name of the implementation
60  * of the abstract <code>javax.xml.transform.TransformerFactory</code>
61  * class. Values of this property for popular XSLT processors include:
62  * </p>
63  * <ul>
64  * <li>Saxon 6.x:
65  * <code>com.icl.saxon.TransformerFactoryImpl</code>
66  * </li>
67  * <li>Saxon 7.x and 8.x:
68  * <code>net.sf.saxon.TransformerFactoryImpl</code>
69  * </li>
70  * <li>Xalan interpretive:
71  * <code>org.apache.xalan.processor.TransformerFactoryImpl</code>
72  * </li>
73  * <li>Xalan XSLTC:
74  * <code>org.apache.xalan.xsltc.trax.TransformerFactoryImpl</code>
75  * </li>
76  * <li>jd.xslt:
77  * <code>jd.xml.xslt.trax.TransformerFactoryImpl</code>
78  * </li>
79  * <li>Oracle:
80  * <code>oracle.xml.jaxp.JXSAXTransformerFactory</code>
81  * </li>
82  * <li>Java 1.5 bundled Xalan:
83  * <code>com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl</code>
84  * </li>
85  * </ul>
86  * <p>
87  * This property can be set in all the usual ways a Java system
88  * property can be set. TrAX picks from them in this order:</p>
89  * <ol>
90  * <li>The most recent value specified by invoking
91  * <code>System.setProperty("javax.xml.transform.TransformerFactory",
92  * "<i><code>classname</code></i>")</code></li>
93  * <li>The value specified at the command line using the
94  * <samp>-Djavax.xml.transform.TransformerFactory=<i>classname</i></samp>
95  * option to the <b>java</b> interpreter</li>
96  * <li>The class named in the <code>lib/jaxp.properties</code>
97  * properties file in the JRE directory, in a line like this one:
98  * <pre>javax.xml.parsers.DocumentBuilderFactory=<i>classname</i></pre>
99  * </li>
100  * <li>The class named in the
101  * <code>META-INF/services/javax.xml.transform.TransformerFactory</code>
102  * file in the JAR archives available to the runtime</li>
103  * <li>Finally, if all of the above options fail,
104  * a default implementation is chosen. In Sun's JDK 1.4.0 and 1.4.1,
105  * this is Xalan 2.2d10. In JDK 1.4.2, this is Xalan 2.4.
106  * In JDK 1.4.2_02, this is Xalan 2.4.1.
107  * In JDK 1.4.2_03, 1.5 beta 2, and 1.5 RC1 this is Xalan 2.5.2.
108  * In JDK 1.4.2_05, this is Xalan 2.4.1. (Yes, Sun appears to have
109  * reverted to 2.4.1 in 1.4.2_05.)
110  * </li>
111  * </ol>
112  *
113  * @author Elliotte Rusty Harold
114  * @version 1.0
115  */

116 public final class XSLTransform {
117
118     
119     /**
120      * <p>
121      * The compiled form of the XSLT stylesheet that this object
122      * represents. This can be safely used across multiple threads
123      * unlike a <code>Transformer</code> object.
124      * </p>
125      */

126     private Templates JavaDoc templates;
127     private NodeFactory factory;
128     
129     
130     // I could use one TransformerFactory field instead of local
131
// variables but then I'd have to synchronize it; and it would
132
// be hard to change the class used to transform
133

134     
135     /**
136      * <p>
137      * Creates a new <code>XSLTransform</code> by
138      * reading the stylesheet from the specified source.
139      * </p>
140      *
141      * @param source TrAX <code>Source</code> object from
142      * which the input document is read
143      *
144      * @throws XSLException when an IOException, format error, or
145      * something else prevents the stylesheet from being compiled
146      */

147      private XSLTransform(Source JavaDoc source) throws XSLException {
148          
149         try {
150             TransformerFactory JavaDoc factory
151               = TransformerFactory.newInstance();
152             this.templates = factory.newTemplates(source);
153         }
154         catch (TransformerFactoryConfigurationError JavaDoc error) {
155            throw new XSLException(
156              "Could not locate a TrAX TransformerFactory", error
157            );
158         }
159         catch (TransformerConfigurationException JavaDoc ex) {
160            throw new XSLException(
161              "Syntax error in stylesheet", ex
162            );
163         }
164         
165     }
166     
167     
168     /**
169      * <p>
170      * Creates a new <code>XSLTransform</code> by
171      * reading the stylesheet from the supplied document.
172      * </p>
173      *
174      * @param stylesheet document containing the stylesheet
175      *
176      * @throws XSLException when the supplied document
177      * is not syntactically correct XSLT
178      */

179     public XSLTransform(Document stylesheet) throws XSLException {
180         this(stylesheet, new NodeFactory());
181     }
182
183
184     /**
185      * <p>
186      * Creates a new <code>XSLTransform</code> by
187      * reading the stylesheet from the supplied document.
188      * The supplied factory will be used to create all nodes
189      * in the result tree, so that a transform can create
190      * instances of subclasses of the standard XOM classes.
191      * Because an XSL transformation generates a list of nodes rather
192      * than a document, the factory's <code>startMakingDocument</code>
193      * and <code>finishMakingDocument</code> methods are not called.
194      * </p>
195      *
196      * @param stylesheet document containing the stylesheet
197      * @param factory the factory used to build nodes in the result tree
198      *
199      * @throws XSLException when the supplied document
200      * is not syntactically correct XSLT
201      */

202     public XSLTransform(Document stylesheet, NodeFactory factory)
203       throws XSLException {
204         
205         this(new XOMSource(stylesheet));
206         if (factory == null) this.factory = new NodeFactory();
207         else this.factory = factory;
208         
209     }
210
211
212     /**
213      * <p>
214      * Creates a new <code>Nodes</code> from the
215      * input <code>Document</code> by applying this object's
216      * stylesheet. The original <code>Document</code> is not
217      * changed.
218      * </p>
219      *
220      * @param in document to transform
221      *
222      * @return a <code>Nodes</code> containing the result of the
223      * transformation
224      *
225      * @throws XSLException if the transformation fails, normally
226      * due to an XSLT error
227      */

228     public Nodes transform(Document in) throws XSLException {
229         return transform(new XOMSource(in));
230     }
231   
232     
233     /**
234      * <p>
235      * Creates a new <code>Nodes</code> object from the
236      * input <code>Nodes</code> object by applying this object's
237      * stylesheet. The original <code>Nodes</code> object is not
238      * changed.
239      * </p>
240      *
241      * @param in document to transform
242      *
243      * @return a <code>Nodes</code> containing the result of
244      * the transformation
245      *
246      * @throws XSLException if the transformation fails, normally
247      * due to an XSLT error
248      */

249     public Nodes transform(Nodes in) throws XSLException {
250         
251         if (in.size() == 0) return new Nodes();
252         XOMSource source = new XOMSource(in);
253         return transform(source);
254         
255     }
256
257     
258     /**
259      * <p>
260      * Creates a new <code>Nodes</code> object from the
261      * input <code>Source</code> object by applying this object's
262      * stylesheet.
263      * </p>
264      *
265      * @param in TrAX <code>Source</code> to transform
266      *
267      * @return a <code>Nodes</code> object containing the result of
268      * the transformation
269      *
270      * @throws XSLException if the transformation fails, normally
271      * due to an XSLT error
272      */

273     private Nodes transform(Source JavaDoc in) throws XSLException {
274         
275         try {
276             XOMResult out = new XOMResult(factory);
277             Transformer JavaDoc transformer = templates.newTransformer();
278             // work around Xalan bug
279
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
280             transformer.transform(in, out);
281             return out.getResult();
282         }
283         catch (Exception JavaDoc ex) {
284             // workaround bugs that wrap RuntimeExceptions
285
Throwable JavaDoc cause = ex;
286             if (cause instanceof TransformerException JavaDoc) {
287                 TransformerException JavaDoc tex = (TransformerException JavaDoc) cause;
288                 Throwable JavaDoc nested = tex.getException();
289                 if (nested != null) {
290                     cause = nested;
291                     if (cause instanceof SAXParseException JavaDoc) {
292                         nested = ((SAXParseException JavaDoc) cause).getException();
293                         if (nested != null) cause = nested;
294                     }
295                 }
296             }
297             throw new XSLException(ex.getMessage(), cause);
298         }
299         
300     }
301     
302     
303     /**
304      * <p>
305      * Builds a <code>Document</code> object from a
306      * <code>Nodes</code> object. This is useful when the stylesheet
307      * is known to produce a well-formed document with a single root
308      * element and no text or attribute nodes. If the stylesheet
309      * produces anything else, an <code>XMLException</code> is thrown.
310      * </p>
311      *
312      * @param nodes the nodes to be placed in the new document
313      *
314      * @return a document containing the nodes
315      *
316      * @throws XMLException if <code>nodes</code> does not contain
317      * exactly one element or if it contains any text nodes or
318      * attributes
319      */

320     public static Document toDocument(Nodes nodes) {
321         
322         Element root = null;
323         int rootPosition = 0;
324         for (int i = 0; i < nodes.size(); i++) {
325             if (nodes.get(i) instanceof Element) {
326                 rootPosition = i;
327                 root = (Element) nodes.get(i);
328                 break;
329             }
330         }
331         
332         if (root == null) {
333             throw new XMLException("No root element");
334         }
335         
336         Document result = new Document(root);
337         
338         for (int i = 0; i < rootPosition; i++) {
339             result.insertChild(nodes.get(i), i);
340         }
341         
342         for (int i = rootPosition+1; i < nodes.size(); i++) {
343             result.appendChild(nodes.get(i));
344         }
345         
346         return result;
347         
348     }
349   
350     
351     /**
352      * <p>
353      * Returns a string form of this <code>XSLTransform</code>,
354      * suitable for debugging.
355      * </p>
356      *
357      * @return debugging string
358      */

359     public String JavaDoc toString() {
360         return "[" + getClass().getName() + ": " + templates + "]";
361     }
362   
363     
364 }
Popular Tags