001: /*
002: ******************************************************************
003: Copyright (c) 2001-2007, Jeff Martin, Tim Bacon
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009:
010: * Redistributions of source code must retain the above copyright
011: notice, this list of conditions and the following disclaimer.
012: * Redistributions in binary form must reproduce the above
013: copyright notice, this list of conditions and the following
014: disclaimer in the documentation and/or other materials provided
015: with the distribution.
016: * Neither the name of the xmlunit.sourceforge.net nor the names
017: of its contributors may be used to endorse or promote products
018: derived from this software without specific prior written
019: permission.
020:
021: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
022: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
023: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
024: FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
025: COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
026: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
027: BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
028: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
029: CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
030: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
031: ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
032: POSSIBILITY OF SUCH DAMAGE.
033:
034: ******************************************************************
035: */
036:
037: package org.custommonkey.xmlunit;
038:
039: import org.custommonkey.xmlunit.exceptions.ConfigurationException;
040: import org.custommonkey.xmlunit.exceptions.XMLUnitRuntimeException;
041:
042: import java.io.File;
043: import java.io.StringReader;
044: import java.io.StringWriter;
045: import java.net.MalformedURLException;
046: import java.util.Properties;
047:
048: import javax.xml.transform.ErrorListener;
049: import javax.xml.transform.Result;
050: import javax.xml.transform.Source;
051: import javax.xml.transform.Transformer;
052: import javax.xml.transform.TransformerException;
053: import javax.xml.transform.TransformerFactory;
054: import javax.xml.transform.URIResolver;
055: import javax.xml.transform.dom.DOMSource;
056: import javax.xml.transform.dom.DOMResult;
057: import javax.xml.transform.sax.SAXSource;
058: import javax.xml.transform.stream.StreamResult;
059: import javax.xml.transform.stream.StreamSource;
060: import org.xml.sax.InputSource;
061: import org.w3c.dom.Document;
062: import org.w3c.dom.Node;
063:
064: /**
065: * Handy wrapper for an XSLT transformation performed using JAXP/Trax.
066: * Note that transformation is not actually performed until a call to
067: * <code>getResultXXX</code> method, and Templates are not used.
068: * <br />Examples and more at <a href="http://xmlunit.sourceforge.net"/>xmlunit.sourceforge.net</a>
069: */
070: public class Transform {
071: private static final File PWD = new File(".");
072:
073: private final Source inputSource;
074: private final Transformer transformer;
075:
076: /**
077: * Create a transformation using String input XML and String stylesheet
078: * @param input
079: * @param stylesheet
080: */
081: public Transform(String input, String stylesheet) {
082: this (new StreamSource(new StringReader(input)),
083: new StreamSource(new StringReader(stylesheet)));
084: }
085:
086: /**
087: * Create a transformation using String input XML and stylesheet in a File
088: * @param input
089: * @param stylesheet
090: */
091: public Transform(String input, File stylesheet) {
092: this (new StreamSource(new StringReader(input)),
093: new StreamSource(stylesheet));
094: }
095:
096: /**
097: * Create a transformation using InputSource input XML and
098: * InputSource stylesheet
099: * @param input
100: * @param stylesheet
101: */
102: public Transform(InputSource input, InputSource stylesheet) {
103: this (new SAXSource(input), new SAXSource(stylesheet));
104: }
105:
106: /**
107: * Create a transformation using InputSource input XML and
108: * stylesheet in a File
109: * @param input
110: * @param stylesheet
111: */
112: public Transform(InputSource input, File stylesheet) {
113: this (new SAXSource(input), new StreamSource(stylesheet));
114: }
115:
116: /**
117: * Create a transformation that allows us to serialize a DOM Node
118: * @param source
119: */
120: public Transform(Node sourceNode) {
121: this (sourceNode, (Source) null);
122: }
123:
124: /**
125: * Create a transformation from an input Node and stylesheet in a String
126: * @param sourceNode
127: * @param stylesheet
128: */
129: public Transform(Node sourceNode, String stylesheet) {
130: this (sourceNode, new StreamSource(new StringReader(stylesheet)));
131: }
132:
133: /**
134: * Create a transformation from an input Node and stylesheet in a File
135: * @param sourceNode
136: * @param stylesheet
137: */
138: public Transform(Node sourceNode, File stylesheet) {
139: this (sourceNode, new StreamSource(stylesheet));
140: }
141:
142: /**
143: * Create a transformation from an input Node and stylesheet in a Source
144: * @param sourceNode
145: * @param stylesheetSource
146: */
147: private Transform(Node sourceNode, Source stylesheetSource) {
148: this (new DOMSource(sourceNode), stylesheetSource);
149: }
150:
151: /**
152: * Create a transformation using Source input XML and Source stylesheet
153: * @param inputReader
154: * @param stylesheetReader
155: */
156: public Transform(Source inputSource, Source stylesheetSource) {
157: this .inputSource = inputSource;
158: provideSystemIdIfRequired(inputSource);
159:
160: provideSystemIdIfRequired(stylesheetSource);
161: this .transformer = getTransformer(stylesheetSource);
162: }
163:
164: /**
165: * Ensure that the source has a systemId
166: * @param source
167: */
168: private void provideSystemIdIfRequired(Source source) {
169: if (source != null
170: && (source.getSystemId() == null || source
171: .getSystemId().length() == 0)) {
172: source.setSystemId(getDefaultSystemId());
173: }
174: }
175:
176: /**
177: * @return the current working directory as an URL-form string
178: */
179: private String getDefaultSystemId() {
180: try {
181: return PWD.toURL().toExternalForm();
182: } catch (MalformedURLException e) {
183: throw new XMLUnitRuntimeException(
184: "Unable to determine current "
185: + "working directory!", e);
186: }
187: }
188:
189: /**
190: * Factory method
191: * @param stylesheetSource
192: * @throws ConfigurationException
193: * @return
194: */
195: private Transformer getTransformer(Source stylesheetSource)
196: throws ConfigurationException {
197: try {
198: TransformerFactory factory = XMLUnit
199: .getTransformerFactory();
200: Transformer t = stylesheetSource == null ? factory
201: .newTransformer() : factory
202: .newTransformer(stylesheetSource);
203: return t;
204: } catch (javax.xml.transform.TransformerConfigurationException ex) {
205: throw new ConfigurationException(ex);
206: }
207: }
208:
209: /**
210: * Perform the actual transformation
211: * @param result
212: * @throws TransformerException
213: */
214: protected void transformTo(Result result)
215: throws TransformerException {
216: transformer.transform(inputSource, result);
217: }
218:
219: /**
220: * Perform the XSLT transformation specified in the constructor
221: * @return the result as a String
222: * @throws TransformerException
223: */
224: public String getResultString() throws TransformerException {
225: StringWriter outputWriter = new StringWriter();
226: transformTo(new StreamResult(outputWriter));
227:
228: return outputWriter.toString();
229: }
230:
231: /**
232: * Perform the XSLT transformation specified in the constructor
233: * @return the result as a DOM Document
234: * @throws TransformerException
235: */
236: public Document getResultDocument() throws TransformerException {
237: DOMResult result = new DOMResult();
238: transformTo(result);
239:
240: return (Document) result.getNode();
241: }
242:
243: /**
244: * Override an output property specified in the transformation stylesheet
245: * @param name
246: * @param value
247: */
248: public void setOutputProperty(String name, String value) {
249: Properties properties = new Properties();
250: properties.setProperty(name, value);
251: setOutputProperties(properties);
252: }
253:
254: /**
255: * Override output properties specified in the transformation stylesheet
256: * @param outputProperties
257: * @see Transformer#setOutputProperties(java.util.Properties)
258: */
259: public void setOutputProperties(Properties outputProperties) {
260: transformer.setOutputProperties(outputProperties);
261: }
262:
263: /**
264: * Add a parameter for the transformation
265: * @param name
266: * @param value
267: * @see Transformer#setParameter(java.lang.String, java.lang.Object)
268: */
269: public void setParameter(String name, Object value) {
270: transformer.setParameter(name, value);
271: }
272:
273: /**
274: * See a parameter used for the transformation
275: * @param name
276: * @return the parameter value
277: * @see Transformer#getParameter(java.lang.String)
278: */
279: public Object getParameter(String name) {
280: return transformer.getParameter(name);
281: }
282:
283: /**
284: * Clear parameters used for the transformation
285: * @see Transformer#clearParameters()
286: */
287: public void clearParameters() {
288: transformer.clearParameters();
289: }
290:
291: /**
292: * Set the URIResolver for the transformation
293: * @see Transformer#setURIResolver(javax.xml.transform.URIResolver)
294: */
295: public void setURIResolver(URIResolver uriResolver) {
296: transformer.setURIResolver(uriResolver);
297: }
298:
299: /**
300: * Set the ErrorListener for the transformation
301: * @see Transformer#setErrorListener(javax.xml.transform.ErrorListener)
302: */
303: public void setErrorListener(ErrorListener errorListener) {
304: transformer.setErrorListener(errorListener);
305: }
306: }
|