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