View Javadoc

1   /*
2    * org.osjava.jardiff.ant.JDTraXLiaison
3    *
4    * $Id: IOThread.java 1952 2005-08-28 18:03:41Z cybertiger $
5    * $URL: https://svn.osjava.org/svn/osjava/trunk/osjava-nio/src/java/org/osjava/nio/IOThread.java $
6    * $Rev: 1952 $
7    * $Date: 2005-08-28 18:03:41 +0000 (Sun, 28 Aug 2005) $
8    * $Author: cybertiger $
9    *
10   * Copyright (c) 2005, Antony Riley
11   * All rights reserved.
12   *
13   * Redistribution and use in source and binary forms, with or without
14   * modification, are permitted provided that the following conditions are met:
15   *
16   * + Redistributions of source code must retain the above copyright notice,
17   *   this list of conditions and the following disclaimer.
18   *
19   * + Redistributions in binary form must reproduce the above copyright notice,
20   *   this list of conditions and the following disclaimer in the documentation
21   *   and/or other materials provided with the distribution.
22   *
23   * + Neither the name JarDiff nor the names of its contributors may
24   *   be used to endorse or promote products derived from this software without
25   *   specific prior written permission.
26   *
27   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
28   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30   * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
31   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37   * POSSIBILITY OF SUCH DAMAGE.
38   */
39  package org.osjava.jardiff.ant;
40  
41  import java.io.BufferedInputStream;
42  import java.io.BufferedOutputStream;
43  import java.io.File;
44  import java.io.FileInputStream;
45  import java.io.FileOutputStream;
46  import java.io.IOException;
47  import java.io.InputStream;
48  import java.io.OutputStream;
49  import java.net.URL;
50  import java.util.Vector;
51  import java.util.Enumeration;
52  import javax.xml.parsers.ParserConfigurationException;
53  import javax.xml.parsers.SAXParserFactory;
54  import javax.xml.transform.ErrorListener;
55  import javax.xml.transform.Source;
56  import javax.xml.transform.SourceLocator;
57  import javax.xml.transform.Templates;
58  import javax.xml.transform.Transformer;
59  import javax.xml.transform.TransformerException;
60  import javax.xml.transform.TransformerFactory;
61  import javax.xml.transform.URIResolver;
62  import javax.xml.transform.sax.SAXSource;
63  import javax.xml.transform.stream.StreamResult;
64  import javax.xml.transform.stream.StreamSource;
65  import javax.xml.transform.TransformerConfigurationException;
66  import org.apache.tools.ant.BuildException;
67  import org.apache.tools.ant.taskdefs.XSLTLiaison2;
68  import org.apache.tools.ant.taskdefs.XSLTProcess;
69  import org.apache.tools.ant.taskdefs.XSLTLogger;
70  import org.apache.tools.ant.taskdefs.XSLTLoggerAware;
71  import org.apache.tools.ant.types.XMLCatalog;
72  import org.apache.tools.ant.util.JAXPUtils;
73  import org.xml.sax.EntityResolver;
74  import org.xml.sax.InputSource;
75  import org.xml.sax.SAXException;
76  import org.xml.sax.XMLReader;
77  
78  /* JarDiff modifications of Ant class anotated with // JARDIFF */
79  
80  /***
81   * Custom TraX liaison class.
82   *
83   * This allows loading a stylesheet from a systemid (URL).
84   */
85  public class JDTraXLiaison implements JDXSLTLiaison3, ErrorListener, 
86         XSLTLoggerAware 
87  {
88  
89      /***
90       * The systemid of the stylesheet (a URL).
91       */
92      private String systemid; // JARDIFF: Added.
93  
94      /***
95       * Create a new JDTraXLiaison.
96       */
97      // JARDIFF: Modified constructor name.
98      public JDTraXLiaison() throws Exception {
99      }
100 
101     /***
102      * the name of the factory implementation class to use
103      * or null for default JAXP lookup.
104      */
105     private String factoryName = null;
106 
107     /*** The trax TransformerFactory */
108     private TransformerFactory tfactory = null;
109 
110     /*** stylesheet to use for transformation */
111     private File stylesheet;
112 
113     private XSLTLogger logger;
114 
115     /*** possible resolver for publicIds */
116     private EntityResolver entityResolver;
117 
118     /*** transformer to use for processing files */
119     private Transformer transformer;
120 
121     /*** The In memory version of the stylesheet */
122     private Templates templates;
123 
124     /***
125      * The modification time of the stylesheet from which the templates
126      * are read
127      */
128     private long templatesModTime;
129 
130     /*** possible resolver for URIs */
131     private URIResolver uriResolver;
132 
133     /*** transformer output properties */
134     private Vector outputProperties = new Vector();
135 
136     /*** stylesheet parameters */
137     private Vector params = new Vector();
138 
139     /*** factory attributes */
140     private Vector attributes = new Vector();
141 
142     // JARDIFF: Added Method.
143     public void setStylesheet(String systemid) {
144         if(this.systemid != null) {
145             transformer = null;
146             // Can't check mod time on URLs
147             if(this.systemid.equals(systemid)) {
148                 templates = null;
149             }
150         }
151         this.systemid = systemid;
152         this.stylesheet = null;
153     }
154 
155     public void setStylesheet(File stylesheet) throws Exception {
156         if (this.stylesheet != null) {
157             // resetting the stylesheet - reset transformer
158             transformer = null;
159 
160             // do we need to reset templates as well
161             if (!this.stylesheet.equals(stylesheet)
162                 || (stylesheet.lastModified() != templatesModTime)) {
163                 templates = null;
164             }
165         }
166         this.stylesheet = stylesheet;
167         // JARDIFF: Added next line.
168         this.systemid = stylesheet.toURL().toString();
169     }
170 
171     public void transform(File infile, File outfile) throws Exception {
172         if (transformer == null) {
173             createTransformer();
174         }
175 
176         InputStream fis = null;
177         OutputStream fos = null;
178         try {
179             fis = new BufferedInputStream(new FileInputStream(infile));
180             fos = new BufferedOutputStream(new FileOutputStream(outfile));
181             StreamResult res = new StreamResult(fos);
182             // not sure what could be the need of this...
183             res.setSystemId(JAXPUtils.getSystemId(outfile));
184             Source src = getSource(fis, infile);
185             transformer.transform(src, res);
186         } finally {
187             // make sure to close all handles, otherwise the garbage
188             // collector will close them...whenever possible and
189             // Windows may complain about not being able to delete files.
190             try {
191                 if (fis != null) {
192                     fis.close();
193                 }
194             } catch (IOException ignored) {
195                 // ignore
196             }
197             try {
198                 if (fos != null) {
199                     fos.close();
200                 }
201             } catch (IOException ignored) {
202                 // ignore
203             }
204         }
205     }
206 
207     /***
208      * Get the source instance from the stream and id of the file.
209      * @param is the stream containing the stylesheet data.
210      * @param infile the file that will be used for the systemid.
211      * @return the configured source instance matching the stylesheet.
212      * @throws Exception if there is a problem creating the source.
213      */
214     // JARDIFF: Added for compatability.
215     private Source getSource(InputStream is, File infile)
216         throws ParserConfigurationException, SAXException {
217         return getSource(is, JAXPUtils.getSystemId(infile));
218     }
219 
220     // JARDIFF: Modified method parameters
221     private Source getSource(InputStream is, String systemid)
222         throws ParserConfigurationException, SAXException {
223         // todo: is this comment still relevant ??
224         // FIXME: need to use a SAXSource as the source for the transform
225         // so we can plug in our own entity resolver
226         Source src = null;
227         if (entityResolver != null) {
228             if (getFactory().getFeature(SAXSource.FEATURE)) {
229                 SAXParserFactory spFactory = SAXParserFactory.newInstance();
230                 spFactory.setNamespaceAware(true);
231                 XMLReader reader = spFactory.newSAXParser().getXMLReader();
232                 reader.setEntityResolver(entityResolver);
233                 src = new SAXSource(reader, new InputSource(is));
234             } else {
235                 throw new IllegalStateException("xcatalog specified, but "
236                     + "parser doesn't support SAX");
237             }
238         } else {
239             // WARN: Don't use the StreamSource(File) ctor. It won't work with
240             // xalan prior to 2.2 because of systemid bugs.
241             src = new StreamSource(is);
242         }
243         // JARDIFF: Modified (moved to overloaded method variant).
244         src.setSystemId(systemid);
245         return src;
246     }
247 
248     /***
249      * Read in templates from the stylesheet
250      */
251     private void readTemplates()
252         throws IOException, TransformerConfigurationException,
253                ParserConfigurationException, SAXException {
254 
255         // Use a stream so that you can close it yourself quickly
256         // and avoid keeping the handle until the object is garbaged.
257         // (always keep control), otherwise you won't be able to delete
258         // the file quickly on windows.
259         InputStream xslStream = null;
260         try {
261             // JARDIFF: Largely modified.
262             if(stylesheet != null) {
263                 xslStream
264                     = new BufferedInputStream(new FileInputStream(stylesheet));
265                 templatesModTime = stylesheet.lastModified();
266             } else {
267                 xslStream = 
268                     new URL(systemid).openStream();
269                 templatesModTime = 0L;
270             }
271             Source src = getSource(xslStream, systemid);
272             templates = getFactory().newTemplates(src);
273         } finally {
274             if(xslStream != null) {
275                 xslStream.close();
276             }
277         }
278     }
279 
280     /***
281      * Create a new transformer based on the liaison settings
282      * @return the newly created and configured transformer.
283      * @throws Exception thrown if there is an error during creation.
284      * @see #setStylesheet(java.io.File)
285      * @see #addParam(java.lang.String, java.lang.String)
286      * @see #setOutputProperty(java.lang.String, java.lang.String)
287      */
288     private void createTransformer() throws Exception {
289         if (templates == null) {
290             readTemplates();
291         }
292 
293         transformer = templates.newTransformer();
294 
295         // configure the transformer...
296         transformer.setErrorListener(this);
297         if (uriResolver != null) {
298             transformer.setURIResolver(uriResolver);
299         }
300         for (int i = 0; i < params.size(); i++) {
301             final String[] pair = (String[]) params.elementAt(i);
302             transformer.setParameter(pair[0], pair[1]);
303         }
304         for (int i = 0; i < outputProperties.size(); i++) {
305             final String[] pair = (String[]) outputProperties.elementAt(i);
306             transformer.setOutputProperty(pair[0], pair[1]);
307         }
308     }
309 
310     /***
311      * return the Transformer factory associated to this liaison.
312      * @return the Transformer factory associated to this liaison.
313      * @throws BuildException thrown if there is a problem creating
314      * the factory.
315      * @see #setFactory(String)
316      * @since Ant 1.5.2
317      */
318     private TransformerFactory getFactory() throws BuildException {
319         if (tfactory != null) {
320             return tfactory;
321         }
322         // not initialized yet, so create the factory
323         if (factoryName == null) {
324             tfactory = TransformerFactory.newInstance();
325         } else {
326             try {
327                 Class clazz = Class.forName(factoryName);
328                 tfactory = (TransformerFactory) clazz.newInstance();
329             } catch (Exception e) {
330                 throw new BuildException(e);
331             }
332         }
333         tfactory.setErrorListener(this);
334 
335         // specific attributes for the transformer
336         for (int i = 0; i < attributes.size(); i++) {
337             final Object[] pair = (Object[]) attributes.elementAt(i);
338             tfactory.setAttribute((String) pair[0], pair[1]);
339         }
340 
341         if (uriResolver != null) {
342             tfactory.setURIResolver(uriResolver);
343         }
344         return tfactory;
345     }
346 
347 
348     /***
349      * Set the factory name to use instead of JAXP default lookup.
350      * @param name the fully qualified class name of the factory to use
351      * or null for the default JAXP look up mechanism.
352      * @since Ant 1.6
353      */
354     public void setFactory(String name) {
355         factoryName = name;
356     }
357 
358     /***
359      * Set a custom attribute for the JAXP factory implementation.
360      * @param name the attribute name.
361      * @param value the value of the attribute, usually a boolean
362      * string or object.
363      * @since Ant 1.6
364      */
365     public void setAttribute(String name, Object value) {
366         final Object[] pair = new Object[]{name, value};
367         attributes.addElement(pair);
368     }
369 
370     /***
371      * Set the output property for the current transformer.
372      * Note that the stylesheet must be set prior to calling
373      * this method.
374      * @param name the output property name.
375      * @param value the output property value.
376      * @since Ant 1.5
377      * @since Ant 1.5
378      */
379     public void setOutputProperty(String name, String value) {
380         final String[] pair = new String[]{name, value};
381         outputProperties.addElement(pair);
382     }
383 
384     /*** Set the class to resolve entities during the transformation
385      */
386     public void setEntityResolver(EntityResolver aResolver) {
387         entityResolver = aResolver;
388     }
389 
390     /*** Set the class to resolve URIs during the transformation
391      */
392     public void setURIResolver(URIResolver aResolver) {
393         uriResolver = aResolver;
394     }
395 
396     public void addParam(String name, String value) {
397         final String[] pair = new String[]{name, value};
398         params.addElement(pair);
399     }
400 
401     public void setLogger(XSLTLogger l) {
402         logger = l;
403     }
404 
405     public void error(TransformerException e) {
406         logError(e, "Error");
407     }
408 
409     public void fatalError(TransformerException e) {
410         logError(e, "Fatal Error");
411         throw new BuildException("Fatal error during transformation", e);
412     }
413 
414     public void warning(TransformerException e) {
415         logError(e, "Warning");
416     }
417 
418     private void logError(TransformerException e, String type) {
419         if (logger == null) {
420             return;
421         }
422 
423         StringBuffer msg = new StringBuffer();
424         SourceLocator locator = e.getLocator();
425         if (locator != null) {
426             String systemid = locator.getSystemId();
427             if (systemid != null) {
428                 String url = systemid;
429                 if (url.startsWith("file:///")) {
430                     url = url.substring(8);
431                 }
432                 msg.append(url);
433             } else {
434                 msg.append("Unknown file");
435             }
436             int line = locator.getLineNumber();
437             if (line != -1) {
438                 msg.append(":" + line);
439                 int column = locator.getColumnNumber();
440                 if (column != -1) {
441                     msg.append(":" + column);
442                 }
443             }
444         }
445         msg.append(": " + type + "! ");
446         msg.append(e.getMessage());
447         if (e.getCause() != null) {
448             msg.append(" Cause: " + e.getCause());
449         }
450 
451         logger.log(msg.toString());
452     }
453 
454     // kept for backwards compatibility
455     /***
456      * @deprecated use org.apache.tools.ant.util.JAXPUtils#getSystemId instead
457      */
458     protected String getSystemId(File file) {
459         return JAXPUtils.getSystemId(file);
460     }
461 
462 
463     /***
464      * Specific configuration for the TRaX liaison.
465      * @param xsltTask the XSLTProcess task instance from which this liasion
466      *        is to be configured.
467      */
468     public void configure(XSLTProcess xsltTask) {
469         XSLTProcess.Factory factory = xsltTask.getFactory();
470         if (factory != null) {
471             setFactory(factory.getName());
472 
473             // configure factory attributes
474             for (Enumeration attrs = factory.getAttributes();
475                     attrs.hasMoreElements();) {
476                 XSLTProcess.Factory.Attribute attr =
477                         (XSLTProcess.Factory.Attribute) attrs.nextElement();
478                 setAttribute(attr.getName(), attr.getValue());
479             }
480         }
481 
482         XMLCatalog xmlCatalog = xsltTask.getXMLCatalog();
483         // use XMLCatalog as the entity resolver and URI resolver
484         if (xmlCatalog != null) {
485             setEntityResolver(xmlCatalog);
486             setURIResolver(xmlCatalog);
487         }
488 
489 
490         // configure output properties
491         for (Enumeration props = xsltTask.getOutputProperties();
492                 props.hasMoreElements();) {
493             XSLTProcess.OutputProperty prop
494                 = (XSLTProcess.OutputProperty) props.nextElement();
495             setOutputProperty(prop.getName(), prop.getValue());
496         }
497     }
498 
499     /***
500      * Specific configuration for the TRaX liaison.
501      * @param xsltTask the XSLTProcess task instance from which this liasion
502      *        is to be configured.
503      */
504     // JARDIFF: Added
505     public void configure(JDXSLTProcess xsltTask) {
506         JDXSLTProcess.Factory factory = xsltTask.getFactory();
507         if (factory != null) {
508             setFactory(factory.getName());
509 
510             // configure factory attributes
511             for (Enumeration attrs = factory.getAttributes();
512                     attrs.hasMoreElements();) {
513                 JDXSLTProcess.Factory.Attribute attr =
514                         (JDXSLTProcess.Factory.Attribute) attrs.nextElement();
515                 setAttribute(attr.getName(), attr.getValue());
516             }
517         }
518 
519         XMLCatalog xmlCatalog = xsltTask.getXMLCatalog();
520         // use XMLCatalog as the entity resolver and URI resolver
521         if (xmlCatalog != null) {
522             setEntityResolver(xmlCatalog);
523             setURIResolver(xmlCatalog);
524         }
525 
526 
527         // configure output properties
528         for (Enumeration props = xsltTask.getOutputProperties();
529                 props.hasMoreElements();) {
530             JDXSLTProcess.OutputProperty prop
531                 = (JDXSLTProcess.OutputProperty) props.nextElement();
532             setOutputProperty(prop.getName(), prop.getValue());
533         }
534     }
535 }