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