• 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.util.Hashtable;
23 import java.util.Properties;
24 
25 import javax.xml.transform.Source;
26 import javax.xml.transform.Templates;
27 import javax.xml.transform.Transformer;
28 import javax.xml.transform.TransformerConfigurationException;
29 import javax.xml.transform.TransformerFactory;
30 import javax.xml.transform.stream.StreamResult;
31 import javax.xml.transform.stream.StreamSource;
32 
33 import org.apache.qetest.QetestUtils;
34 import org.apache.xml.utils.DefaultErrorHandler;
35 
36 /**
37  * Implementation of TransformWrapper that uses the TrAX API and
38  * uses systemId URL's for it's sources.
39  *
40  * This is the most common usage:
41  * transformer = factory.newTransformer(new StreamSource(xslURL));
42  * transformer.transform(new StreamSource(xmlURL), new StreamResult(resultFileName));
43  *
44  * <b>Important!</b>  The underlying System property of
45  * javax.xml.transform.TransformerFactory will determine the actual
46  * TrAX implementation used.  This value will be reported out in
47  * our getProcessorInfo() method.
48  *
49  * @author Shane Curcuru
50  * @version $Id$
51  */
52 public class TraxSystemIdWrapper extends TransformWrapperHelper
53 {
54 
55     /**
56      * TransformerFactory to use; constructed in newProcessor().
57      */
58     protected TransformerFactory factory = null;
59 
60 
61     /**
62      * Templates to use for buildStylesheet().
63      */
64     protected Templates builtTemplates = null;
65 
66 
67     /**
68      * Cached copy of newProcessor() Hashtable.
69      */
70     protected Hashtable newProcessorOpts = null;
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(systemId)";
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", "systemId");
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("TraxSystemIdWrapper.newProcessor: factory does not support Streams!");
130         }
131         // Set any of our options as Attributes on the factory
132         TraxWrapperUtils.setAttributes(factory, newProcessorOpts);
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 URL
166         startTime = System.currentTimeMillis();
167 
168         Transformer transformer = factory.newTransformer(
169                                    new StreamSource(QetestUtils.filenameToURL(xslName)));
170         transformer.setErrorListener(new DefaultErrorHandler());
171 
172         xslBuild = System.currentTimeMillis() - startTime;
173 
174         // Untimed: Set any of our options as Attributes on the transformer
175         TraxWrapperUtils.setAttributes(transformer, newProcessorOpts);
176 
177         // Untimed: Apply any parameters needed
178         applyParameters(transformer);
179 
180         // Timed: read/build xml, transform, and write results
181         startTime = System.currentTimeMillis();
182         transformer.transform(new StreamSource(QetestUtils.filenameToURL(xmlName)),
183                               new StreamResult(resultName));
184         transform = System.currentTimeMillis() - startTime;
185 
186         long[] times = getTimeArray();
187         times[IDX_OVERALL] = xslBuild + transform;
188         times[IDX_XSLBUILD] = xslBuild;
189         times[IDX_TRANSFORM] = transform;
190         return times;
191     }
192 
193 
194     /**
195      * Pre-build/pre-compile a stylesheet.
196      *
197      * Although the actual mechanics are implementation-dependent,
198      * most processors have some method of pre-setting up the data
199      * needed by the stylesheet itself for later use in transforms.
200      * In TrAX/javax.xml.transform, this equates to creating a
201      * Templates object.
202      *
203      * Sets isStylesheetReady() to true if it succeeds.  Users can
204      * then call transformWithStylesheet(xmlName, resultName) to
205      * actually perform a transformation with this pre-built
206      * stylesheet.
207      *
208      * @param xslName local path\filename of XSL stylesheet to use
209      *
210      * @return array of longs denoting timing of only these parts of
211      * our operation: IDX_OVERALL, IDX_XSLBUILD
212      *
213      * @throws Exception any underlying exceptions from the
214      * wrappered processor are simply allowed to propagate; throws
215      * a RuntimeException if any other problems prevent us from
216      * actually completing the operation
217      *
218      * @see #transformWithStylesheet(String xmlName, String resultName)
219      */
buildStylesheet(String xslName)220     public long[] buildStylesheet(String xslName) throws Exception
221     {
222         preventFootShooting();
223         long startTime = 0;
224         long xslBuild = 0;
225 
226         // Timed: read/build xsl from a URL
227         startTime = System.currentTimeMillis();
228         builtTemplates = factory.newTemplates(
229                 new StreamSource(QetestUtils.filenameToURL(xslName)));
230         xslBuild = System.currentTimeMillis() - startTime;
231         m_stylesheetReady = true;
232 
233         long[] times = getTimeArray();
234         times[IDX_OVERALL] = xslBuild;
235         times[IDX_XSLBUILD] = xslBuild;
236         return times;
237     }
238 
239 
240     /**
241      * Transform supplied xmlName file with a pre-built/pre-compiled
242      * stylesheet into a resultName file.
243      *
244      * User must have called buildStylesheet(xslName) beforehand,
245      * obviously.
246      * Names are assumed to be local path\filename references, and
247      * will be converted to URLs as needed.
248      *
249      * @param xmlName local path\filename of XML file to transform
250      * @param resultName local path\filename to put result in
251      *
252      * @return array of longs denoting timing of only these parts of
253      * our operation: IDX_OVERALL, IDX_XSLBUILD, IDX_TRANSFORM
254      *
255      * @throws Exception any underlying exceptions from the
256      * wrappered processor are simply allowed to propagate; throws
257      * a RuntimeException if any other problems prevent us from
258      * actually completing the operation; throws an
259      * IllegalStateException if isStylesheetReady() == false.
260      *
261      * @see #buildStylesheet(String xslName)
262      */
transformWithStylesheet(String xmlName, String resultName)263     public long[] transformWithStylesheet(String xmlName, String resultName)
264         throws Exception
265     {
266         if (!isStylesheetReady())
267             throw new IllegalStateException("transformWithStylesheet() when isStylesheetReady() == false");
268 
269         preventFootShooting();
270         long startTime = 0;
271         long transform = 0;
272 
273         // UNTimed: get Transformer from Templates
274         Transformer transformer = builtTemplates.newTransformer();
275         transformer.setErrorListener(new DefaultErrorHandler());
276 
277         // Untimed: Set any of our options as Attributes on the transformer
278         TraxWrapperUtils.setAttributes(transformer, newProcessorOpts);
279 
280         // Untimed: Apply any parameters needed
281         applyParameters(transformer);
282 
283         // Timed: read/build xml, transform, and write results
284         startTime = System.currentTimeMillis();
285         transformer.transform(new StreamSource(QetestUtils.filenameToURL(xmlName)),
286                               new StreamResult(resultName));
287         transform = System.currentTimeMillis() - startTime;
288 
289         long[] times = getTimeArray();
290         times[IDX_OVERALL] = transform;
291         times[IDX_TRANSFORM] = transform;
292         return times;
293     }
294 
295 
296     /**
297      * Transform supplied xmlName file with a stylesheet found in an
298      * xml-stylesheet PI into a resultName file.
299      *
300      * Names are assumed to be local path\filename references, and
301      * will be converted to URLs as needed.  Implementations will
302      * use whatever facilities exist in their wrappered processor
303      * to fetch and build the stylesheet to use for the transform.
304      *
305      * @param xmlName local path\filename of XML file to transform
306      * @param resultName local path\filename to put result in
307      *
308      * @return array of longs denoting timing of only these parts of
309      * our operation: IDX_OVERALL, IDX_XSLREAD (time to find XSL
310      * reference from the xml-stylesheet PI), IDX_XSLBUILD, (time
311      * to then build the Transformer therefrom), IDX_TRANSFORM
312      *
313      * @throws Exception any underlying exceptions from the
314      * wrappered processor are simply allowed to propagate; throws
315      * a RuntimeException if any other problems prevent us from
316      * actually completing the operation
317      */
transformEmbedded(String xmlName, String resultName)318     public long[] transformEmbedded(String xmlName, String resultName)
319         throws Exception
320     {
321         preventFootShooting();
322         long startTime = 0;
323         long xslRead = 0;
324         long xslBuild = 0;
325         long transform = 0;
326 
327         // Timed: readxsl from the xml document
328         startTime = System.currentTimeMillis();
329         Source xslSource = factory.getAssociatedStylesheet(new StreamSource(QetestUtils.filenameToURL(xmlName)),
330                                                               null, null, null);
331         xslRead = System.currentTimeMillis() - startTime;
332 
333         // Timed: build xsl from a URL
334         startTime = System.currentTimeMillis();
335         Transformer transformer = factory.newTransformer(xslSource);
336         transformer.setErrorListener(new DefaultErrorHandler());
337         xslBuild = System.currentTimeMillis() - startTime;
338 
339         // Untimed: Set any of our options as Attributes on the transformer
340         TraxWrapperUtils.setAttributes(transformer, newProcessorOpts);
341 
342         // Untimed: Apply any parameters needed
343         applyParameters(transformer);
344 
345         // Timed: read/build xml, transform, and write results
346         startTime = System.currentTimeMillis();
347         transformer.transform(new StreamSource(QetestUtils.filenameToURL(xmlName)),
348                               new StreamResult(resultName));
349         transform = System.currentTimeMillis() - startTime;
350 
351         long[] times = getTimeArray();
352         times[IDX_OVERALL] = xslRead + xslBuild + transform;
353         times[IDX_XSLREAD] = xslRead;
354         times[IDX_XSLBUILD] = xslBuild;
355         times[IDX_TRANSFORM] = transform;
356         return times;
357     }
358 
359 
360     /**
361      * Reset our parameters and wrapper state, and optionally
362      * force creation of a new underlying processor implementation.
363      *
364      * This always clears our built stylesheet and any parameters
365      * that have been set.  If newProcessor is true, also forces a
366      * re-creation of our underlying processor as if by calling
367      * newProcessor().
368      *
369      * @param newProcessor if we should reset our underlying
370      * processor implementation as well
371      */
reset(boolean newProcessor)372     public void reset(boolean newProcessor)
373     {
374         super.reset(newProcessor); // clears indent and parameters
375         m_stylesheetReady = false;
376         builtTemplates = null;
377         if (newProcessor)
378         {
379             try
380             {
381                 newProcessor(newProcessorOpts);
382             }
383             catch (Exception e)
384             {
385                 //@todo Hmm: what should we do here?
386             }
387         }
388     }
389 
390 
391     /**
392      * Apply a single parameter to a Transformer.
393      *
394      * Overridden to take a Transformer and call setParameter().
395      *
396      * @param passThru to be passed to each applyParameter() method
397      * call - for TrAX, you might pass a Transformer object.
398      * @param namespace for the parameter, may be null
399      * @param name for the parameter, should not be null
400      * @param value for the parameter, may be null
401      */
applyParameter(Object passThru, String namespace, String name, Object value)402     protected void applyParameter(Object passThru, String namespace,
403                                   String name, Object value)
404     {
405         try
406         {
407             Transformer t = (Transformer)passThru;
408             // Munge the namespace into the name per
409             //  javax.xml.transform.Transformer.setParameter()
410             if (null != namespace)
411             {
412                 name = "{" + namespace + "}" + name;
413             }
414             t.setParameter(name, value);
415         }
416         catch (Exception e)
417         {
418             throw new IllegalArgumentException("applyParameter threw: " + e.toString());
419         }
420     }
421 
422 
423     /**
424      * Ensure newProcessor has been called when needed.
425      *
426      * Prevent users from shooting themselves in the foot by
427      * calling a transform* API before newProcessor().
428      *
429      * (Sorry, I couldn't resist)
430      */
preventFootShooting()431     public void preventFootShooting() throws Exception
432     {
433         if (null == factory)
434             newProcessor(newProcessorOpts);
435     }
436 }
437