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