• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 /*
19  * $Id$
20  */
21 package org.apache.qetest.xslwrapper;
22 import java.io.File;
23 import java.util.Hashtable;
24 import java.util.Properties;
25 
26 import javax.xml.transform.Source;
27 import javax.xml.transform.Templates;
28 import javax.xml.transform.Transformer;
29 import javax.xml.transform.TransformerConfigurationException;
30 import javax.xml.transform.TransformerFactory;
31 import javax.xml.transform.stream.StreamResult;
32 import javax.xml.transform.stream.StreamSource;
33 import org.apache.xml.utils.DefaultErrorHandler;
34 
35 /**
36  * Implementation of TransformWrapper that uses the TrAX API and
37  * uses files for it's sources.
38  *
39  * This is the second most common usage:
40  * transformer = factory.newTransformer(new StreamSource(new File(xslName)));
41  * transformer.transform(new StreamSource(new File(xmlName)), new StreamResult(resultFileName));
42  *
43  * <b>Important!</b>  The underlying System property of
44  * javax.xml.transform.TransformerFactory will determine the actual
45  * TrAX implementation used.  This value will be reported out in
46  * our getProcessorInfo() method.
47  *
48  * @author Shane Curcuru
49  * @version $Id$
50  */
51 public class TraxFileWrapper extends TransformWrapperHelper
52 {
53 
54     /**
55      * TransformerFactory to use; constructed in newProcessor().
56      */
57     protected TransformerFactory factory = null;
58 
59 
60     /**
61      * Templates to use for buildStylesheet().
62      */
63     protected Templates builtTemplates = null;
64 
65 
66     /**
67      * Cached copy of newProcessor() Hashtable.
68      */
69     protected Hashtable newProcessorOpts = null;
70 
71 
72     /**
73      * Get a general description of this wrapper itself.
74      *
75      * @return Uses TrAX to perform transforms from StreamSource(systemId)
76      */
getDescription()77     public String getDescription()
78     {
79         return "Uses TrAX to perform transforms from StreamSource(new File(filename))";
80     }
81 
82 
83     /**
84      * Get a specific description of the wrappered processor.
85      *
86      * @return specific description of the underlying processor or
87      * transformer implementation: this should include both the
88      * general product name, as well as specific version info.  If
89      * possible, should be implemented without actively creating
90      * an underlying processor.
91      */
getProcessorInfo()92     public Properties getProcessorInfo()
93     {
94         Properties p = TraxWrapperUtils.getTraxInfo();
95         p.put("traxwrapper.method", "file");
96         p.put("traxwrapper.desc", getDescription());
97         return p;
98     }
99 
100 
101     /**
102      * Actually create/initialize an underlying processor or factory.
103      *
104      * For TrAX/javax.xml.transform implementations, this creates
105      * a new TransformerFactory.  For Xalan-J 1.x this creates an
106      * XSLTProcessor.  Other implmentations may or may not actually
107      * do any work in this method.
108      *
109      * @param options Hashtable of options, unused.
110      *
111      * @return (Object)getProcessor() as a side-effect, this will
112      * be null if there was any problem creating the processor OR
113      * if the underlying implementation doesn't use this
114      *
115      * @throws Exception covers any underlying exceptions thrown
116      * by the actual implementation
117      */
newProcessor(Hashtable options)118     public Object newProcessor(Hashtable options) throws Exception
119     {
120         newProcessorOpts = options;
121         //@todo do we need to do any other cleanup?
122         reset(false);
123         factory = TransformerFactory.newInstance();
124         factory.setErrorListener(new DefaultErrorHandler());
125         // Verify the factory supports Streams!
126         if (!(factory.getFeature(StreamSource.FEATURE)
127               && factory.getFeature(StreamResult.FEATURE)))
128         {
129             throw new TransformerConfigurationException("TraxFileWrapper.newProcessor: factory does not support Streams!");
130         }
131         // Set any of our options as Attributes on the factory
132         TraxWrapperUtils.setAttributes(factory, options);
133         return (Object)factory;
134     }
135 
136 
137     /**
138      * Transform supplied xmlName file with the stylesheet in the
139      * xslName file into a resultName file.
140      *
141      * Names are assumed to be local path\filename references, and
142      * will be converted to URLs as needed for any underlying
143      * processor implementation.
144      *
145      * @param xmlName local path\filename of XML file to transform
146      * @param xslName local path\filename of XSL stylesheet to use
147      * @param resultName local path\filename to put result in
148      *
149      * @return array of longs denoting timing of only these parts of
150      * our operation: IDX_OVERALL, IDX_XSLBUILD, IDX_TRANSFORM
151      *
152      * @throws Exception any underlying exceptions from the
153      * wrappered processor are simply allowed to propagate; throws
154      * a RuntimeException if any other problems prevent us from
155      * actually completing the operation
156      */
transform(String xmlName, String xslName, String resultName)157     public long[] transform(String xmlName, String xslName, String resultName)
158         throws Exception
159     {
160         preventFootShooting();
161         long startTime = 0;
162         long xslBuild = 0;
163         long transform = 0;
164 
165         // Timed: read/build xsl from a File
166         startTime = System.currentTimeMillis();
167         Transformer transformer = factory.newTransformer(new StreamSource(new File(xslName)));
168         transformer.setErrorListener(new DefaultErrorHandler());
169         xslBuild = System.currentTimeMillis() - startTime;
170 
171         // Untimed: Set any of our options as Attributes on the transformer
172         TraxWrapperUtils.setAttributes(transformer, newProcessorOpts);
173 
174         // Untimed: Apply any parameters needed
175         applyParameters(transformer);
176 
177         // Timed: read/build xml, transform, and write results
178         startTime = System.currentTimeMillis();
179         transformer.transform(new StreamSource(new File(xmlName)), new StreamResult(resultName));
180         transform = System.currentTimeMillis() - startTime;
181 
182         long[] times = getTimeArray();
183         times[IDX_OVERALL] = xslBuild + transform;
184         times[IDX_XSLBUILD] = xslBuild;
185         times[IDX_TRANSFORM] = transform;
186         return times;
187     }
188 
189 
190     /**
191      * Pre-build/pre-compile a stylesheet.
192      *
193      * Although the actual mechanics are implementation-dependent,
194      * most processors have some method of pre-setting up the data
195      * needed by the stylesheet itself for later use in transforms.
196      * In TrAX/javax.xml.transform, this equates to creating a
197      * Templates object.
198      *
199      * Sets isStylesheetReady() to true if it succeeds.  Users can
200      * then call transformWithStylesheet(xmlName, resultName) to
201      * actually perform a transformation with this pre-built
202      * stylesheet.
203      *
204      * @param xslName local path\filename of XSL stylesheet to use
205      *
206      * @return array of longs denoting timing of only these parts of
207      * our operation: IDX_OVERALL, IDX_XSLBUILD
208      *
209      * @throws Exception any underlying exceptions from the
210      * wrappered processor are simply allowed to propagate; throws
211      * a RuntimeException if any other problems prevent us from
212      * actually completing the operation
213      *
214      * @see #transformWithStylesheet(String xmlName, String resultName)
215      */
buildStylesheet(String xslName)216     public long[] buildStylesheet(String xslName) throws Exception
217     {
218         preventFootShooting();
219         long startTime = 0;
220         long xslBuild = 0;
221 
222         // Timed: read/build xsl from a URL
223         startTime = System.currentTimeMillis();
224         builtTemplates = factory.newTemplates(new StreamSource(new File(xslName)));
225         xslBuild = System.currentTimeMillis() - startTime;
226         m_stylesheetReady = true;
227 
228         long[] times = getTimeArray();
229         times[IDX_OVERALL] = xslBuild;
230         times[IDX_XSLBUILD] = xslBuild;
231         return times;
232     }
233 
234 
235     /**
236      * Transform supplied xmlName file with a pre-built/pre-compiled
237      * stylesheet into a resultName file.
238      *
239      * User must have called buildStylesheet(xslName) beforehand,
240      * obviously.
241      * Names are assumed to be local path\filename references, and
242      * will be converted to URLs as needed.
243      *
244      * @param xmlName local path\filename of XML file to transform
245      * @param resultName local path\filename to put result in
246      *
247      * @return array of longs denoting timing of only these parts of
248      * our operation: IDX_OVERALL, IDX_XSLBUILD, IDX_TRANSFORM
249      *
250      * @throws Exception any underlying exceptions from the
251      * wrappered processor are simply allowed to propagate; throws
252      * a RuntimeException if any other problems prevent us from
253      * actually completing the operation; throws an
254      * IllegalStateException if isStylesheetReady() == false.
255      *
256      * @see #buildStylesheet(String xslName)
257      */
transformWithStylesheet(String xmlName, String resultName)258     public long[] transformWithStylesheet(String xmlName, String resultName)
259         throws Exception
260     {
261         if (!isStylesheetReady())
262             throw new IllegalStateException("transformWithStylesheet() when isStylesheetReady() == false");
263 
264         preventFootShooting();
265         long startTime = 0;
266         long transform = 0;
267 
268         // UNTimed: get Transformer from Templates
269         Transformer transformer = builtTemplates.newTransformer();
270         transformer.setErrorListener(new DefaultErrorHandler());
271 
272         // Untimed: Set any of our options as Attributes on the transformer
273         TraxWrapperUtils.setAttributes(transformer, newProcessorOpts);
274 
275         // Untimed: Apply any parameters needed
276         applyParameters(transformer);
277 
278         // Timed: read/build xml, transform, and write results
279         startTime = System.currentTimeMillis();
280         transformer.transform(new StreamSource(new File(xmlName)),
281                               new StreamResult(resultName));
282         transform = System.currentTimeMillis() - startTime;
283 
284         long[] times = getTimeArray();
285         times[IDX_OVERALL] = transform;
286         times[IDX_TRANSFORM] = transform;
287         return times;
288     }
289 
290 
291     /**
292      * Transform supplied xmlName file with a stylesheet found in an
293      * xml-stylesheet PI into a resultName file.
294      *
295      * Names are assumed to be local path\filename references, and
296      * will be converted to URLs as needed.  Implementations will
297      * use whatever facilities exist in their wrappered processor
298      * to fetch and build the stylesheet to use for the transform.
299      *
300      * @param xmlName local path\filename of XML file to transform
301      * @param resultName local path\filename to put result in
302      *
303      * @return array of longs denoting timing of only these parts of
304      * our operation: IDX_OVERALL, IDX_XSLREAD (time to find XSL
305      * reference from the xml-stylesheet PI), IDX_XSLBUILD, (time
306      * to then build the Transformer therefrom), IDX_TRANSFORM
307      *
308      * @throws Exception any underlying exceptions from the
309      * wrappered processor are simply allowed to propagate; throws
310      * a RuntimeException if any other problems prevent us from
311      * actually completing the operation
312      */
transformEmbedded(String xmlName, String resultName)313     public long[] transformEmbedded(String xmlName, String resultName)
314         throws Exception
315     {
316         preventFootShooting();
317         long startTime = 0;
318         long xslRead = 0;
319         long xslBuild = 0;
320         long transform = 0;
321 
322         // Timed: readxsl from the xml document
323         startTime = System.currentTimeMillis();
324         Source xslSource = factory.getAssociatedStylesheet(new StreamSource(new File(xmlName)),
325                                                               null, null, null);
326         xslRead = System.currentTimeMillis() - startTime;
327 
328         // Timed: build xsl from a URL
329         startTime = System.currentTimeMillis();
330         Transformer transformer = factory.newTransformer(xslSource);
331         transformer.setErrorListener(new DefaultErrorHandler());
332         xslBuild = System.currentTimeMillis() - startTime;
333 
334         // Untimed: Set any of our options as Attributes on the transformer
335         TraxWrapperUtils.setAttributes(transformer, newProcessorOpts);
336 
337         // Untimed: Apply any parameters needed
338         applyParameters(transformer);
339 
340         // Timed: read/build xml, transform, and write results
341         startTime = System.currentTimeMillis();
342         transformer.transform(new StreamSource(new File(xmlName)),
343                               new StreamResult(resultName));
344         transform = System.currentTimeMillis() - startTime;
345 
346         long[] times = getTimeArray();
347         times[IDX_OVERALL] = xslRead + xslBuild + transform;
348         times[IDX_XSLREAD] = xslRead;
349         times[IDX_XSLBUILD] = xslBuild;
350         times[IDX_TRANSFORM] = transform;
351         return times;
352     }
353 
354 
355     /**
356      * Reset our parameters and wrapper state, and optionally
357      * force creation of a new underlying processor implementation.
358      *
359      * This always clears our built stylesheet and any parameters
360      * that have been set.  If newProcessor is true, also forces a
361      * re-creation of our underlying processor as if by calling
362      * newProcessor().
363      *
364      * @param newProcessor if we should reset our underlying
365      * processor implementation as well
366      */
reset(boolean newProcessor)367     public void reset(boolean newProcessor)
368     {
369         super.reset(newProcessor); // clears indent and parameters
370         m_stylesheetReady = false;
371         builtTemplates = null;
372         if (newProcessor)
373         {
374             try
375             {
376                 newProcessor(newProcessorOpts);
377             }
378             catch (Exception e)
379             {
380                 //@todo Hmm: what should we do here?
381             }
382         }
383     }
384 
385 
386     /**
387      * Apply a single parameter to a Transformer.
388      *
389      * Overridden to take a Transformer and call setParameter().
390      *
391      * @param passThru to be passed to each applyParameter() method
392      * call - for TrAX, you might pass a Transformer object.
393      * @param namespace for the parameter, may be null
394      * @param name for the parameter, should not be null
395      * @param value for the parameter, may be null
396      */
applyParameter(Object passThru, String namespace, String name, Object value)397     protected void applyParameter(Object passThru, String namespace,
398                                   String name, Object value)
399     {
400         try
401         {
402             Transformer t = (Transformer)passThru;
403             // Munge the namespace into the name per
404             //  javax.xml.transform.Transformer.setParameter()
405             if (null != namespace)
406             {
407                 name = "{" + namespace + "}" + name;
408             }
409             t.setParameter(name, value);
410         }
411         catch (Exception e)
412         {
413             throw new IllegalArgumentException("applyParameter threw: " + e.toString());
414         }
415     }
416 
417 
418     /**
419      * Ensure newProcessor has been called when needed.
420      *
421      * Prevent users from shooting themselves in the foot by
422      * calling a transform* API before newProcessor().
423      *
424      * (Sorry, I couldn't resist)
425      */
preventFootShooting()426     public void preventFootShooting() throws Exception
427     {
428         if (null == factory)
429             newProcessor(newProcessorOpts);
430     }
431 }
432