• 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.parsers.DocumentBuilder;
26 import javax.xml.parsers.DocumentBuilderFactory;
27 import javax.xml.transform.Source;
28 import javax.xml.transform.Templates;
29 import javax.xml.transform.Transformer;
30 import javax.xml.transform.TransformerConfigurationException;
31 import javax.xml.transform.TransformerFactory;
32 import javax.xml.transform.dom.DOMResult;
33 import javax.xml.transform.dom.DOMSource;
34 import javax.xml.transform.stream.StreamResult;
35 
36 import org.apache.qetest.QetestUtils;
37 import org.apache.xml.utils.DefaultErrorHandler;
38 import org.w3c.dom.Document;
39 import org.w3c.dom.DocumentFragment;
40 import org.w3c.dom.Node;
41 import org.xml.sax.InputSource;
42 
43 /**
44  * Implementation of TransformWrapper that uses the TrAX API and
45  * uses DOMs for it's sources.
46  *
47  * This implementation records separate times for xslRead (time to
48  * parse the xsl and build a DOM) and xslBuild (time to take the
49  * DOMSource object until it's built the templates); xmlRead (time
50  * to parse the xml and build a DOM).  Note xmlBuild is not timed
51  * since it's not easily measureable in TrAX.
52  * The transform time is just the time to create the DOMResult
53  * object; the resultsWrite is the separate time it takes to
54  * serialize that to disk.
55  *
56  * <b>Important!</b>  The underlying System property of
57  * javax.xml.transform.TransformerFactory will determine the actual
58  * TrAX implementation used.  This value will be reported out in
59  * our getProcessorInfo() method.
60  *
61  * //@todo add in checks for factory.getFeature(DOMSource.FEATURE)
62  *
63  * @author Shane Curcuru
64  * @version $Id$
65  */
66 public class TraxDOMWrapper extends TransformWrapperHelper
67 {
68 
69     /**
70      * TransformerFactory to use; constructed in newProcessor().
71      */
72     protected TransformerFactory factory = null;
73 
74 
75     /**
76      * Templates to use for buildStylesheet().
77      */
78     protected Templates builtTemplates = null;
79 
80 
81     /**
82      * Cached copy of newProcessor() Hashtable.
83      */
84     protected Hashtable newProcessorOpts = null;
85 
86 
87     /**
88      * Get a general description of this wrapper itself.
89      *
90      * @return Uses TrAX to perform transforms from DOMSource(node)
91      */
getDescription()92     public String getDescription()
93     {
94         return "Uses TrAX to perform transforms from DOMSource(node)";
95     }
96 
97 
98     /**
99      * Get a specific description of the wrappered processor.
100      *
101      * @return specific description of the underlying processor or
102      * transformer implementation: this should include both the
103      * general product name, as well as specific version info.  If
104      * possible, should be implemented without actively creating
105      * an underlying processor.
106      */
getProcessorInfo()107     public Properties getProcessorInfo()
108     {
109         Properties p = TraxWrapperUtils.getTraxInfo();
110         p.put("traxwrapper.method", "dom");
111         p.put("traxwrapper.desc", getDescription());
112         return p;
113     }
114 
115 
116     /**
117      * Actually create/initialize an underlying processor or factory.
118      *
119      * For TrAX/javax.xml.transform implementations, this creates
120      * a new TransformerFactory.  For Xalan-J 1.x this creates an
121      * XSLTProcessor.  Other implmentations may or may not actually
122      * do any work in this method.
123      *
124      * @param options Hashtable of options, unused.
125      *
126      * @return (Object)getProcessor() as a side-effect, this will
127      * be null if there was any problem creating the processor OR
128      * if the underlying implementation doesn't use this
129      *
130      * @throws Exception covers any underlying exceptions thrown
131      * by the actual implementation
132      */
newProcessor(Hashtable options)133     public Object newProcessor(Hashtable options) throws Exception
134     {
135         newProcessorOpts = options;
136         //@todo do we need to do any other cleanup?
137         reset(false);
138         factory = TransformerFactory.newInstance();
139         factory.setErrorListener(new DefaultErrorHandler());
140         // Verify the factory supports DOM!
141         if (!(factory.getFeature(DOMSource.FEATURE)
142               && factory.getFeature(DOMResult.FEATURE)))
143         {
144             throw new TransformerConfigurationException("TraxDOMWrapper.newProcessor: factory does not support DOM!");
145         }
146         // Set any of our options as Attributes on the factory
147         TraxWrapperUtils.setAttributes(factory, options);
148         return (Object)factory;
149     }
150 
151 
152     /**
153      * Transform supplied xmlName file with the stylesheet in the
154      * xslName file into a resultName file.
155      *
156      * Names are assumed to be local path\filename references, and
157      * will be converted to URLs as needed for any underlying
158      * processor implementation.
159      *
160      * @param xmlName local path\filename of XML file to transform
161      * @param xslName local path\filename of XSL stylesheet to use
162      * @param resultName local path\filename to put result in
163      *
164      * @return array of longs denoting timing of all parts of
165      * our operation
166      *
167      * @throws Exception any underlying exceptions from the
168      * wrappered processor are simply allowed to propagate; throws
169      * a RuntimeException if any other problems prevent us from
170      * actually completing the operation
171      */
transform(String xmlName, String xslName, String resultName)172     public long[] transform(String xmlName, String xslName, String resultName)
173         throws Exception
174     {
175         preventFootShooting();
176         long startTime = 0;
177         long xslRead = 0;
178         long xslBuild = 0;
179         long xmlRead = 0;
180         long transform = 0;
181         long resultWrite = 0;
182 
183         DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
184         dfactory.setNamespaceAware(true);
185         DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
186 
187         // Timed: read xsl into a DOM
188         startTime = System.currentTimeMillis();
189         Node xslNode = docBuilder.parse(new InputSource(QetestUtils.filenameToURL(xslName)));
190         xslRead = System.currentTimeMillis() - startTime;
191 
192         // Untimed: create DOMSource and setSystemId
193         DOMSource xslSource = new DOMSource(xslNode);
194         xslSource.setSystemId(QetestUtils.filenameToURL(xslName));
195 
196         // Timed: build Transformer from DOMSource
197         startTime = System.currentTimeMillis();
198         Transformer transformer = factory.newTransformer(xslSource);
199         transformer.setErrorListener(new DefaultErrorHandler());
200         xslBuild = System.currentTimeMillis() - startTime;
201 
202         // Timed: read xml into a DOM
203         startTime = System.currentTimeMillis();
204         Node xmlNode = docBuilder.parse(new InputSource(QetestUtils.filenameToURL(xmlName)));
205         xmlRead = System.currentTimeMillis() - startTime;
206 
207         // Untimed: create DOMSource and setSystemId
208         DOMSource xmlSource = new DOMSource(xmlNode);
209         xmlSource.setSystemId(QetestUtils.filenameToURL(xmlName));
210 
211         // Untimed: create DOMResult
212         Document outDoc = docBuilder.newDocument();
213         DocumentFragment outNode = outDoc.createDocumentFragment();
214         DOMResult domResult = new DOMResult(outNode);
215 
216         // Untimed: Set any of our options as Attributes on the transformer
217         TraxWrapperUtils.setAttributes(transformer, newProcessorOpts);
218 
219         // Untimed: Apply any parameters needed
220         applyParameters(transformer);
221 
222         // Timed: build xml (so to speak) and transform
223         startTime = System.currentTimeMillis();
224         transformer.transform(xmlSource, domResult);
225         transform = System.currentTimeMillis() - startTime;
226 
227         // Untimed: prepare serializer with outputProperties
228         //  from the stylesheet
229         Transformer resultSerializer = factory.newTransformer();
230         resultSerializer.setErrorListener(new DefaultErrorHandler());
231         Properties serializationProps = transformer.getOutputProperties();
232         resultSerializer.setOutputProperties(serializationProps);
233 
234         // Timed: writeResults from the DOMResult
235         startTime = System.currentTimeMillis();
236         resultSerializer.transform(new DOMSource(outNode),
237                              new StreamResult(resultName));
238         resultWrite = System.currentTimeMillis() - startTime;
239 
240         long[] times = getTimeArray();
241         times[IDX_OVERALL] = xslRead + xslBuild + xmlRead
242                              + transform + resultWrite;
243         times[IDX_XSLREAD] = xslRead;
244         times[IDX_XSLBUILD] = xslBuild;
245         times[IDX_XMLREAD] = xmlRead;
246         times[IDX_TRANSFORM] = transform;
247         times[IDX_RESULTWRITE] = resultWrite;
248         return times;
249     }
250 
251 
252     /**
253      * Pre-build/pre-compile a stylesheet.
254      *
255      * Although the actual mechanics are implementation-dependent,
256      * most processors have some method of pre-setting up the data
257      * needed by the stylesheet itself for later use in transforms.
258      * In TrAX/javax.xml.transform, this equates to creating a
259      * Templates object.
260      *
261      * Sets isStylesheetReady() to true if it succeeds.  Users can
262      * then call transformWithStylesheet(xmlName, resultName) to
263      * actually perform a transformation with this pre-built
264      * stylesheet.
265      *
266      * @param xslName local path\filename of XSL stylesheet to use
267      *
268      * @return array of longs denoting timing of only these parts of
269      * our operation: IDX_OVERALL, IDX_XSLREAD, IDX_XSLBUILD
270      *
271      * @throws Exception any underlying exceptions from the
272      * wrappered processor are simply allowed to propagate; throws
273      * a RuntimeException if any other problems prevent us from
274      * actually completing the operation
275      *
276      * @see #transformWithStylesheet(String xmlName, String resultName)
277      */
buildStylesheet(String xslName)278     public long[] buildStylesheet(String xslName) throws Exception
279     {
280         preventFootShooting();
281         long startTime = 0;
282         long xslRead = 0;
283         long xslBuild = 0;
284 
285         DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
286         dfactory.setNamespaceAware(true);
287         DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
288 
289         // Timed: read xsl into a DOM
290         startTime = System.currentTimeMillis();
291         Node xslNode = docBuilder.parse(new InputSource(QetestUtils.filenameToURL(xslName)));
292         xslRead = System.currentTimeMillis() - startTime;
293 
294         // Untimed: create DOMSource and setSystemId
295         DOMSource xslSource = new DOMSource(xslNode);
296         xslSource.setSystemId(QetestUtils.filenameToURL(xslName));
297 
298         // Timed: build Templates from DOMSource
299         startTime = System.currentTimeMillis();
300         builtTemplates = factory.newTemplates(xslSource);
301         xslBuild = System.currentTimeMillis() - startTime;
302 
303         m_stylesheetReady = true;
304 
305         long[] times = getTimeArray();
306         times[IDX_OVERALL] = xslRead + xslBuild;
307         times[IDX_XSLREAD] = xslRead;
308         times[IDX_XSLBUILD] = xslBuild;
309         return times;
310     }
311 
312 
313     /**
314      * Transform supplied xmlName file with a pre-built/pre-compiled
315      * stylesheet into a resultName file.
316      *
317      * User must have called buildStylesheet(xslName) beforehand,
318      * obviously.
319      * Names are assumed to be local path\filename references, and
320      * will be converted to URLs as needed.
321      *
322      * @param xmlName local path\filename of XML file to transform
323      * @param resultName local path\filename to put result in
324      *
325      * @return array of longs denoting timing of only these parts of
326      * our operation: IDX_OVERALL, IDX_XMLREAD,
327      * IDX_TRANSFORM, IDX_RESULTWRITE
328      *
329      * @throws Exception any underlying exceptions from the
330      * wrappered processor are simply allowed to propagate; throws
331      * a RuntimeException if any other problems prevent us from
332      * actually completing the operation; throws an
333      * IllegalStateException if isStylesheetReady() == false.
334      *
335      * @see #buildStylesheet(String xslName)
336      */
transformWithStylesheet(String xmlName, String resultName)337     public long[] transformWithStylesheet(String xmlName, String resultName)
338         throws Exception
339     {
340         if (!isStylesheetReady())
341             throw new IllegalStateException("transformWithStylesheet() when isStylesheetReady() == false");
342 
343         preventFootShooting();
344         long startTime = 0;
345         long xmlRead = 0;
346         long transform = 0;
347         long resultWrite = 0;
348 
349         // Untimed: get Transformer from Templates
350         Transformer transformer = builtTemplates.newTransformer();
351         transformer.setErrorListener(new DefaultErrorHandler());
352 
353         DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
354         dfactory.setNamespaceAware(true);
355         DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
356 
357          // Timed: read xml into a DOM
358         startTime = System.currentTimeMillis();
359         Node xmlNode = docBuilder.parse(new InputSource(QetestUtils.filenameToURL(xmlName)));
360         xmlRead = System.currentTimeMillis() - startTime;
361 
362         // Untimed: create DOMSource and setSystemId
363         DOMSource xmlSource = new DOMSource(xmlNode);
364         xmlSource.setSystemId(QetestUtils.filenameToURL(xmlName));
365 
366         // Untimed: create DOMResult
367         Document outNode = docBuilder.newDocument();
368         DOMResult domResult = new DOMResult(outNode);
369 
370         // Untimed: Set any of our options as Attributes on the transformer
371         TraxWrapperUtils.setAttributes(transformer, newProcessorOpts);
372 
373         // Untimed: Apply any parameters needed
374         applyParameters(transformer);
375 
376         // Timed: build xml (so to speak) and transform
377         startTime = System.currentTimeMillis();
378         transformer.transform(xmlSource, domResult);
379         transform = System.currentTimeMillis() - startTime;
380 
381         // Untimed: prepare serializer with outputProperties
382         //  from the stylesheet
383         Transformer resultSerializer = factory.newTransformer();
384         resultSerializer.setErrorListener(new DefaultErrorHandler());
385         Properties serializationProps = transformer.getOutputProperties();
386         resultSerializer.setOutputProperties(serializationProps);
387 
388         // Timed: writeResults from the DOMResult
389         startTime = System.currentTimeMillis();
390         resultSerializer.transform(new DOMSource(outNode),
391                              new StreamResult(resultName));
392         resultWrite = System.currentTimeMillis() - startTime;
393 
394         long[] times = getTimeArray();
395         times[IDX_OVERALL] = xmlRead + transform + resultWrite;
396         times[IDX_XMLREAD] = xmlRead;
397         times[IDX_TRANSFORM] = transform;
398         times[IDX_RESULTWRITE] = resultWrite;
399         return times;
400     }
401 
402 
403     /**
404      * Transform supplied xmlName file with a stylesheet found in an
405      * xml-stylesheet PI into a resultName file.
406      *
407      * Names are assumed to be local path\filename references, and
408      * will be converted to URLs as needed.  Implementations will
409      * use whatever facilities exist in their wrappered processor
410      * to fetch and build the stylesheet to use for the transform.
411      *
412      * @param xmlName local path\filename of XML file to transform
413      * @param resultName local path\filename to put result in
414      *
415      * @return array of longs denoting timing of only these parts of
416      * our operation: IDX_OVERALL, IDX_XSLREAD (time to find XSL
417      * reference from the xml-stylesheet PI), IDX_XSLBUILD, (time
418      * to then build the Transformer therefrom), IDX_TRANSFORM,
419      * and IDX_RESULTWRITE
420      *
421      * @throws Exception any underlying exceptions from the
422      * wrappered processor are simply allowed to propagate; throws
423      * a RuntimeException if any other problems prevent us from
424      * actually completing the operation
425      */
transformEmbedded(String xmlName, String resultName)426     public long[] transformEmbedded(String xmlName, String resultName)
427         throws Exception
428     {
429         long startTime = 0;
430         long xslRead = 0;
431         long xslBuild = 0;
432         long xmlRead = 0;
433         long transform = 0;
434         long resultWrite = 0;
435 
436         DocumentBuilderFactory dfactory = DocumentBuilderFactory.newInstance();
437         dfactory.setNamespaceAware(true);
438         DocumentBuilder docBuilder = dfactory.newDocumentBuilder();
439 
440         // Timed: read xml into a DOM
441         startTime = System.currentTimeMillis();
442         Node xmlNode = docBuilder.parse(new InputSource(QetestUtils.filenameToURL(xmlName)));
443         xmlRead = System.currentTimeMillis() - startTime;
444 
445         // Untimed: create DOMSource and setSystemId
446         DOMSource xmlSource = new DOMSource(xmlNode);
447         xmlSource.setSystemId(QetestUtils.filenameToURL(xmlName));
448 
449         // Timed: readxsl from the xml document
450         startTime = System.currentTimeMillis();
451         Source xslSource = factory.getAssociatedStylesheet(xmlSource,
452                                                            null, null, null);
453         xslRead = System.currentTimeMillis() - startTime;
454 
455         // Timed: build Transformer from Source
456         startTime = System.currentTimeMillis();
457         Transformer transformer = factory.newTransformer(xslSource);
458         transformer.setErrorListener(new DefaultErrorHandler());
459         xslBuild = System.currentTimeMillis() - startTime;
460 
461         // Untimed: create DOMResult
462         Document outNode = docBuilder.newDocument();
463         DOMResult domResult = new DOMResult(outNode);
464 
465         // Untimed: Set any of our options as Attributes on the transformer
466         TraxWrapperUtils.setAttributes(transformer, newProcessorOpts);
467 
468         // Untimed: Apply any parameters needed
469         applyParameters(transformer);
470 
471         // Timed: build xml (so to speak) and transform
472         startTime = System.currentTimeMillis();
473         transformer.transform(xmlSource, domResult);
474         transform = System.currentTimeMillis() - startTime;
475 
476         // Untimed: prepare serializer with outputProperties
477         //  from the stylesheet
478         Transformer resultSerializer = factory.newTransformer();
479         resultSerializer.setErrorListener(new DefaultErrorHandler());
480         Properties serializationProps = transformer.getOutputProperties();
481         resultSerializer.setOutputProperties(serializationProps);
482 
483         // Timed: writeResults from the DOMResult
484         startTime = System.currentTimeMillis();
485         resultSerializer.transform(new DOMSource(outNode),
486                              new StreamResult(resultName));
487         resultWrite = System.currentTimeMillis() - startTime;
488 
489         long[] times = getTimeArray();
490         times[IDX_OVERALL] = xslRead + xslBuild + xmlRead
491                              + transform + resultWrite;
492         times[IDX_XSLREAD] = xslRead;
493         times[IDX_XSLBUILD] = xslBuild;
494         times[IDX_XMLREAD] = xmlRead;
495         times[IDX_TRANSFORM] = transform;
496         times[IDX_RESULTWRITE] = resultWrite;
497         return times;
498     }
499 
500 
501     /**
502      * Reset our parameters and wrapper state, and optionally
503      * force creation of a new underlying processor implementation.
504      *
505      * This always clears our built stylesheet and any parameters
506      * that have been set.  If newProcessor is true, also forces a
507      * re-creation of our underlying processor as if by calling
508      * newProcessor().
509      *
510      * @param newProcessor if we should reset our underlying
511      * processor implementation as well
512      */
reset(boolean newProcessor)513     public void reset(boolean newProcessor)
514     {
515         super.reset(newProcessor); // clears indent and parameters
516         m_stylesheetReady = false;
517         builtTemplates = null;
518         if (newProcessor)
519         {
520             try
521             {
522                 newProcessor(newProcessorOpts);
523             }
524             catch (Exception e)
525             {
526                 //@todo Hmm: what should we do here?
527             }
528         }
529     }
530 
531 
532     /**
533      * Apply a single parameter to a Transformer.
534      *
535      * Overridden to take a Transformer and call setParameter().
536      *
537      * @param passThru to be passed to each applyParameter() method
538      * call - for TrAX, you might pass a Transformer object.
539      * @param namespace for the parameter, may be null
540      * @param name for the parameter, should not be null
541      * @param value for the parameter, may be null
542      */
applyParameter(Object passThru, String namespace, String name, Object value)543     protected void applyParameter(Object passThru, String namespace,
544                                   String name, Object value)
545     {
546         try
547         {
548             Transformer t = (Transformer)passThru;
549             // Munge the namespace into the name per
550             //  javax.xml.transform.Transformer.setParameter()
551             if (null != namespace)
552             {
553                 name = "{" + namespace + "}" + name;
554             }
555             t.setParameter(name, value);
556         }
557         catch (Exception e)
558         {
559             throw new IllegalArgumentException("applyParameter threw: " + e.toString());
560         }
561     }
562 
563 
564     /**
565      * Ensure newProcessor has been called when needed.
566      *
567      * Prevent users from shooting themselves in the foot by
568      * calling a transform* API before newProcessor().
569      *
570      * (Sorry, I couldn't resist)
571      */
preventFootShooting()572     public void preventFootShooting() throws Exception
573     {
574         if (null == factory)
575             newProcessor(newProcessorOpts);
576     }
577 }
578