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