1 /* 2 * Copyright (c) 2014, 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 import static jaxp.library.JAXPTestUtilities.filenameToURL; 31 32 /** 33 * Implementation of TransformWrapper that uses the TrAX API and uses systemId 34 * URL's for it's sources. 35 * 36 * This is the most common usage: transformer = factory.newTransformer(new 37 * StreamSource(xslURL)); transformer.transform(new StreamSource(xmlURL), new 38 * StreamResult(resultFileName)); 39 * 40 * <b>Important!</b> The underlying System property of 41 * javax.xml.transform.TransformerFactory will determine the actual TrAX 42 * implementation used. This value will be reported out in our 43 * getProcessorInfo() method. 44 */ 45 public class TraxSystemIdWrapper extends TransformWrapperHelper { 46 47 /** 48 * TransformerFactory to use; constructed in newProcessor(). 49 */ 50 protected TransformerFactory factory; 51 52 /** 53 * Templates to use for buildStylesheet(). 54 */ 55 protected Templates builtTemplates; 56 57 /** 58 * Cached copy of newProcessor() properties. 59 */ 60 protected Properties newProcessorOpts; 61 62 /** 63 * Get a general description of this wrapper itself. 64 * 65 * @return Uses TrAX to perform transforms from StreamSource(systemId) 66 */ 67 public String getDescription() { 68 return "Uses TrAX to perform transforms from StreamSource(systemId)"; 69 } 70 71 /** 72 * Get a specific description of the wrapped processor. 73 * 74 * @return specific description of the underlying processor or transformer 75 * implementation: this should include both the general product name, as 76 * well as specific version info. If possible, should be implemented without 77 * actively creating an underlying processor. 78 */ 79 @Override 80 public Properties getProcessorInfo() { 81 Properties p = TraxWrapperUtils.getTraxInfo(); 82 p.put("traxwrapper.method", "systemId"); 83 p.put("traxwrapper.desc", getDescription()); 84 return p; 85 } 86 87 /** 88 * Actually create/initialize an underlying processor or factory. 89 * 90 * For TrAX/javax.xml.transform implementations, this creates a new 91 * TransformerFactory. For Xalan-J 1.x this creates an XSLTProcessor. Other 92 * implmentations may or may not actually do any work in this method. 93 * 94 * @param options Properties of options, unused. 95 * 96 * @return (Object)getProcessor() as a side-effect, this will be null if 97 * there was any problem creating the processor OR if the underlying 98 * implementation doesn't use this 99 * @throws javax.xml.transform.TransformerConfigurationException when 100 * actual implementation doesn't support StreamSource or StreamResult. 101 */ 102 @Override 103 public TransformerFactory newProcessor(Properties options) 104 throws TransformerConfigurationException { 105 newProcessorOpts = options; 106 reset(false); 107 factory = TransformerFactory.newInstance(); 108 // Verify the factory supports Streams! 109 if (!(factory.getFeature(StreamSource.FEATURE) 110 && factory.getFeature(StreamResult.FEATURE))) { 111 throw new TransformerConfigurationException("TraxSystemIdWrapper.newProcessor: factory does not support Streams!"); 112 } 113 // Set any of our options as Attributes on the factory 114 TraxWrapperUtils.setAttributes(factory, newProcessorOpts); 115 return factory; 116 } 117 118 /** 119 * Transform supplied xmlName file with the stylesheet in the xslName file 120 * into a resultName file. 121 * 122 * Names are assumed to be local path\filename references, and will be 123 * converted to URLs as needed for any underlying processor implementation. 124 * 125 * @param xmlName local path\filename of XML file to transform 126 * @param xslName local path\filename of XSL stylesheet to use 127 * @param resultName local path\filename to put result in 128 * 129 * @throws Exception any underlying exceptions from the wrapped processor 130 * are simply allowed to propagate; throws a RuntimeException if any other 131 * problems prevent us from actually completing the operation 132 */ 133 @Override 134 public void transform(String xmlName, String xslName, String resultName) 135 throws Exception { 136 preventFootShooting(); 137 138 // Read/build xsl from a URL 139 Transformer transformer = factory.newTransformer( 140 new StreamSource(filenameToURL(xslName))); 141 142 // Set any of our options as Attributes on the transformer 143 TraxWrapperUtils.setAttributes(transformer, newProcessorOpts); 144 145 // Apply any parameters needed 146 applyParameters(transformer); 147 148 // Read/build xml, transform, and write results 149 transformer.transform(new StreamSource(filenameToURL(xmlName)), 150 new StreamResult(resultName)); 151 } 152 153 /** 154 * Pre-build/pre-compile a stylesheet. 155 * 156 * Although the actual mechanics are implementation-dependent, most 157 * processors have some method of pre-setting up the data needed by the 158 * stylesheet itself for later use in transforms. In 159 * TrAX/javax.xml.transform, this equates to creating a Templates object. 160 * 161 * Sets isStylesheetReady() to true if it succeeds. Users can then call 162 * transformWithStylesheet(xmlName, resultName) to actually perform a 163 * transformation with this pre-built stylesheet. 164 * 165 * @param xslName local path\filename of XSL stylesheet to use 166 * 167 * @throws Exception any underlying exceptions from the wrapped processor 168 * are simply allowed to propagate; throws a RuntimeException if any other 169 * problems prevent us from actually completing the operation 170 * 171 * @see #transformWithStylesheet(String xmlName, String resultName) 172 */ 173 @Override 174 public void buildStylesheet(String xslName) throws Exception { 175 preventFootShooting(); 176 177 // Read/build xsl from a URL 178 builtTemplates = factory.newTemplates( 179 new StreamSource(filenameToURL(xslName))); 180 m_stylesheetReady = true; 181 } 182 183 /** 184 * Transform supplied xmlName file with a pre-built/pre-compiled stylesheet 185 * into a resultName file. 186 * 187 * User must have called buildStylesheet(xslName) beforehand, obviously. 188 * Names are assumed to be local path\filename references, and will be 189 * converted to URLs as needed. 190 * 191 * @param xmlName local path\filename of XML file to transform 192 * @param resultName local path\filename to put result in 193 * 194 * @throws Exception any underlying exceptions from the wrapped processor 195 * are simply allowed to propagate; throws a RuntimeException if any other 196 * problems prevent us from actually completing the operation; throws an 197 * IllegalStateException if isStylesheetReady() == false. 198 * 199 * @see #buildStylesheet(String xslName) 200 */ 201 @Override 202 public void transformWithStylesheet(String xmlName, String resultName) 203 throws Exception { 204 if (!isStylesheetReady()) { 205 throw new IllegalStateException("transformWithStylesheet() when isStylesheetReady() == false"); 206 } 207 208 preventFootShooting(); 209 210 // Get Transformer from Templates 211 Transformer transformer = builtTemplates.newTransformer(); 212 213 // Set any of our options as Attributes on the transformer 214 TraxWrapperUtils.setAttributes(transformer, newProcessorOpts); 215 216 // Apply any parameters needed 217 applyParameters(transformer); 218 219 // Read/build xml, transform, and write results 220 transformer.transform(new StreamSource(filenameToURL(xmlName)), 221 new StreamResult(resultName)); 222 } 223 224 /** 225 * Transform supplied xmlName file with a stylesheet found in an 226 * xml-stylesheet PI into a resultName file. 227 * 228 * Names are assumed to be local path\filename references, and will be 229 * converted to URLs as needed. Implementations will use whatever facilities 230 * exist in their wrapped processor to fetch and build the stylesheet to 231 * use for the transform. 232 * 233 * @param xmlName local path\filename of XML file to transform 234 * @param resultName local path\filename to put result in 235 * 236 * @throws Exception any underlying exceptions from the wrapped processor 237 * are simply allowed to propagate; throws a RuntimeException if any other 238 * problems prevent us from actually completing the operation 239 */ 240 @Override 241 public void transformEmbedded(String xmlName, String resultName) 242 throws Exception { 243 preventFootShooting(); 244 // Read xsl from the xml document 245 Source xslSource = factory.getAssociatedStylesheet(new StreamSource(filenameToURL(xmlName)), 246 null, null, null); 247 248 // Build xsl from a URL 249 Transformer transformer = factory.newTransformer(xslSource); 250 251 // Set any of our options as Attributes on the transformer 252 TraxWrapperUtils.setAttributes(transformer, newProcessorOpts); 253 254 // Apply any parameters needed 255 applyParameters(transformer); 256 257 // Read/build xml, transform, and write results 258 transformer.transform(new StreamSource(filenameToURL(xmlName)), 259 new StreamResult(resultName)); 260 } 261 262 /** 263 * Reset our parameters and wrapper state, and optionally force creation of 264 * a new underlying processor implementation. 265 * 266 * This always clears our built stylesheet and any parameters that have been 267 * set. If newProcessor is true, also forces a re-creation of our underlying 268 * processor as if by calling newProcessor(). 269 * 270 * @param newProcessor if we should reset our underlying processor 271 * implementation as well 272 */ 273 @Override 274 public void reset(boolean newProcessor) { 275 super.reset(newProcessor); // clears indent and parameters 276 m_stylesheetReady = false; 277 builtTemplates = null; 278 if (newProcessor) { 279 try { 280 newProcessor(newProcessorOpts); 281 } catch (TransformerConfigurationException ignore) { 282 } 283 } 284 } 285 286 /** 287 * Apply a single parameter to a Transformer. 288 * 289 * Overridden to take a Transformer and call setParameter(). 290 * 291 * @param transformer a Transformer object. 292 * @param namespace for the parameter, may be null 293 * @param name for the parameter, should not be null 294 * @param value for the parameter, may be null 295 */ 296 @Override 297 protected void applyParameter(Transformer transformer, String namespace, 298 String name, String value) { 299 try { 300 // Munge the namespace into the name per 301 // javax.xml.transform.Transformer.setParameter() 302 if (null != namespace) { 303 name = "{" + namespace + "}" + name; 304 } 305 transformer.setParameter(name, value); 306 } catch (Exception e) { 307 throw new IllegalArgumentException("applyParameter threw: " + e.toString()); 308 } 309 } 310 311 /** 312 * Ensure newProcessor has been called when needed. 313 * 314 * Prevent users from shooting themselves in the foot by calling a 315 * transform* API before newProcessor(). 316 * @throws java.lang.Exception 317 */ 318 public void preventFootShooting() throws Exception { 319 if (null == factory) { 320 newProcessor(newProcessorOpts); 321 } 322 } 323 }