1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 */ 4 /* 5 * Licensed to the Apache Software Foundation (ASF) under one or more 6 * contributor license agreements. See the NOTICE file distributed with 7 * this work for additional information regarding copyright ownership. 8 * The ASF licenses this file to You under the Apache License, Version 2.0 9 * (the "License"); you may not use this file except in compliance with 10 * the License. You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 package org.apache.qetest.xslwrapper; 21 22 import java.util.Properties; 23 import javax.xml.transform.Source; 24 import javax.xml.transform.Templates; 25 import javax.xml.transform.Transformer; 26 import javax.xml.transform.TransformerConfigurationException; 27 import javax.xml.transform.TransformerFactory; 28 import javax.xml.transform.stream.StreamResult; 29 import javax.xml.transform.stream.StreamSource; 30 31 /** 32 * Implementation of TransformWrapper that uses the TrAX API and uses supplied 33 * local paths for it's sources. 34 * 35 * This is the most common usage: transformer = factory.newTransformer(new 36 * StreamSource(xslPath)); transformer.transform(new StreamSource(xmlPath), new 37 * StreamResult(resultFileName)); 38 * 39 * Note that URLs are theoretically required by the TrAX API's, so this is not 40 * necessarily a good test... It is essentially the same as TraxLocalPathWrapper 41 * without calling filenameToURL(). 42 * 43 * <b>Important!</b> The underlying System property of 44 * javax.xml.transform.TransformerFactory will determine the actual TrAX 45 * implementation used. This value will be reported out in our 46 * getProcessorInfo() method. 47 * 48 */ 49 public class TraxLocalPathWrapper extends TransformWrapperHelper { 50 /** 51 * Templates to use for buildStylesheet(). 52 */ 53 private Templates builtTemplates; 54 55 /** 56 * Get a general description of this wrapper itself. 57 * 58 * @return Uses TrAX to perform transforms from StreamSource(localPath) 59 */ 60 public String getDescription() { 61 return "Uses TrAX to perform transforms from StreamSource(localPath)"; 62 } 63 64 /** 65 * Get a specific description of the wrappered processor. 66 * 67 * @return specific description of the underlying processor or transformer 68 * implementation: this should include both the general product name, as 69 * well as specific version info. If possible, should be implemented without 70 * actively creating an underlying processor. 71 */ 72 @Override 73 public Properties getProcessorInfo() { 74 Properties p = TraxWrapperUtils.getTraxInfo(); 75 p.put("traxwrapper.method", "localPath"); 76 p.put("traxwrapper.desc", getDescription()); 77 return p; 78 } 79 80 /** 81 * Actually create/initialize an underlying processor or factory. 82 * 83 * For TrAX/javax.xml.transform implementations, this creates a new 84 * TransformerFactory. For Xalan-J 1.x this creates an XSLTProcessor. Other 85 * implmentations may or may not actually do any work in this method. 86 * 87 * @param options Hashtable of options, unused. 88 * 89 * @return (Object)getProcessor() as a side-effect, this will be null if 90 * there was any problem creating the processor OR if the underlying 91 * implementation doesn't use this 92 * @throws javax.xml.transform.TransformerConfigurationException when 93 * actual implementation doesn't support StreamSource or StreamResult. 94 */ 95 @Override 96 public TransformerFactory newProcessor(Properties options) throws TransformerConfigurationException { 97 newProcessorOpts = options; 98 reset(false); 99 factory = TransformerFactory.newInstance(); 100 // Verify the factory supports Streams! 101 if (!(factory.getFeature(StreamSource.FEATURE) 102 && factory.getFeature(StreamResult.FEATURE))) { 103 throw new TransformerConfigurationException("TraxLocalPathWrapper.newProcessor: factory does not support Streams!"); 104 } 105 // Set any of our options as Attributes on the factory 106 TraxWrapperUtils.setAttributes(factory, options); 107 return factory; 108 } 109 110 /** 111 * Transform supplied xmlName file with the stylesheet in the xslName file 112 * into a resultName file. 113 * 114 * Names are assumed to be local path\filename references, and will be 115 * converted to URLs as needed for any underlying processor implementation. 116 * 117 * @param xmlName local path\filename of XML file to transform 118 * @param xslName local path\filename of XSL stylesheet to use 119 * @param resultName local path\filename to put result in 120 * 121 * @throws Exception any underlying exceptions from the wrapped processor 122 * are simply allowed to propagate; throws a RuntimeException if any other 123 * problems prevent us from actually completing the operation 124 */ 125 @Override 126 public void transform(String xmlName, String xslName, String resultName) 127 throws Exception { 128 preventFootShooting(); 129 // read/build xsl from a URL 130 Transformer transformer = factory.newTransformer(new StreamSource(xslName)); 131 132 // Set any of our options as Attributes on the transformer 133 TraxWrapperUtils.setAttributes(transformer, newProcessorOpts); 134 135 // Apply any parameters needed 136 applyParameters(transformer); 137 138 // read/build xml, transform, and write results. 139 transformer.transform(new StreamSource(xmlName), 140 new StreamResult(resultName)); 141 } 142 143 /** 144 * Pre-build/pre-compile a stylesheet. 145 * 146 * Although the actual mechanics are implementation-dependent, most 147 * processors have some method of pre-setting up the data needed by the 148 * stylesheet itself for later use in transforms. In 149 * TrAX/javax.xml.transform, this equates to creating a Templates object. 150 * 151 * Sets isStylesheetReady() to true if it succeeds. Users can then call 152 * transformWithStylesheet(xmlName, resultName) to actually perform a 153 * transformation with this pre-built stylesheet. 154 * 155 * @param xslName local path\filename of XSL stylesheet to use 156 * 157 * @throws Exception any underlying exceptions from the wrapped processor 158 * are simply allowed to propagate; throws a RuntimeException if any other 159 * problems prevent us from actually completing the operation 160 * 161 * @see #transformWithStylesheet(String xmlName, String resultName) 162 */ 163 @Override 164 public void buildStylesheet(String xslName) throws Exception { 165 preventFootShooting();// Timed: read/build xsl from a URL 166 builtTemplates = factory.newTemplates(new StreamSource(xslName)); 167 m_stylesheetReady = true; 168 } 169 170 /** 171 * Transform supplied xmlName file with a pre-built/pre-compiled stylesheet 172 * into a resultName file. 173 * 174 * User must have called buildStylesheet(xslName) beforehand, obviously. 175 * Names are assumed to be local path\filename references, and will be 176 * converted to URLs as needed. 177 * 178 * @param xmlName local path\filename of XML file to transform 179 * @param resultName local path\filename to put result in 180 * 181 * @throws Exception any underlying exceptions from the wrapped processor 182 * are simply allowed to propagate; throws a RuntimeException if any other 183 * problems prevent us from actually completing the operation; throws an 184 * IllegalStateException if isStylesheetReady() == false. 185 * 186 * @see #buildStylesheet(String xslName) 187 */ 188 @Override 189 public void transformWithStylesheet(String xmlName, String resultName) 190 throws Exception { 191 if (!isStylesheetReady()) { 192 throw new IllegalStateException("transformWithStylesheet() when isStylesheetReady() == false"); 193 } 194 195 preventFootShooting(); 196 197 // Get Transformer from Templates 198 Transformer transformer = builtTemplates.newTransformer(); 199 // Untimed: Set any of our options as Attributes on the transformer 200 TraxWrapperUtils.setAttributes(transformer, newProcessorOpts); 201 202 // Apply any parameters needed 203 applyParameters(transformer); 204 205 // read/build xml, transform, and write results 206 transformer.transform(new StreamSource(xmlName), 207 new StreamResult(resultName)); 208 } 209 210 /** 211 * Transform supplied xmlName file with a stylesheet found in an 212 * xml-stylesheet PI into a resultName file. 213 * 214 * Names are assumed to be local path\filename references, and will be 215 * converted to URLs as needed. Implementations will use whatever facilities 216 * exist in their wrapped processor to fetch and build the stylesheet to 217 * use for the transform. 218 * 219 * @param xmlName local path\filename of XML file to transform 220 * @param resultName local path\filename to put result in 221 * 222 * @throws Exception any underlying exceptions from the wrapped processor 223 * are simply allowed to propagate; throws a RuntimeException if any other 224 * problems prevent us from actually completing the operation 225 */ 226 @Override 227 public void transformEmbedded(String xmlName, String resultName) 228 throws Exception { 229 preventFootShooting(); 230 // Read xsl from the xml document 231 Source xslSource = factory.getAssociatedStylesheet( 232 new StreamSource(xmlName), null, null, null); 233 234 // Build xsl from a URL 235 Transformer transformer = factory.newTransformer(xslSource); 236 237 // Set any of our options as Attributes on the transformer 238 TraxWrapperUtils.setAttributes(transformer, newProcessorOpts); 239 240 // Apply any parameters needed 241 applyParameters(transformer); 242 243 // read/build xml, transform, and write results 244 transformer.transform(new StreamSource(xmlName), 245 new StreamResult(resultName)); 246 } 247 248 /** 249 * Reset our parameters and wrapper state, and optionally force creation of 250 * a new underlying processor implementation. 251 * 252 * This always clears our built stylesheet and any parameters that have been 253 * set. If newProcessor is true, also forces a re-creation of our underlying 254 * processor as if by calling newProcessor(). 255 * 256 * @param newProcessor if we should reset our underlying processor 257 * implementation as well 258 */ 259 @Override 260 public void reset(boolean newProcessor) { 261 super.reset(newProcessor); // clears indent and parameters 262 m_stylesheetReady = false; 263 builtTemplates = null; 264 if (newProcessor) { 265 try { 266 newProcessor(newProcessorOpts); 267 } catch (TransformerConfigurationException ignore) {} 268 } 269 } 270 271 /** 272 * Apply a single parameter to a Transformer. 273 * 274 * Overridden to take a Transformer and call setParameter(). 275 * 276 * @param transformer a Transformer object. 277 * @param namespace for the parameter, may be null 278 * @param name for the parameter, should not be null 279 * @param value for the parameter, may be null 280 */ 281 @Override 282 protected void applyParameter(Transformer transformer, String namespace, 283 String name, String value) { 284 try { 285 // Munge the namespace into the name per 286 // javax.xml.transform.Transformer.setParameter() 287 if (null != namespace) { 288 name = "{" + namespace + "}" + name; 289 } 290 transformer.setParameter(name, value); 291 } catch (Exception e) { 292 throw new IllegalArgumentException("applyParameter threw: " + e.toString()); 293 } 294 } 295 }