1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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
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;
93
94 /***
95 * Create a new JDTraXLiaison.
96 */
97
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
143 public void setStylesheet(String systemid) {
144 if(this.systemid != null) {
145 transformer = null;
146
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
158 transformer = null;
159
160
161 if (!this.stylesheet.equals(stylesheet)
162 || (stylesheet.lastModified() != templatesModTime)) {
163 templates = null;
164 }
165 }
166 this.stylesheet = stylesheet;
167
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
183 res.setSystemId(JAXPUtils.getSystemId(outfile));
184 Source src = getSource(fis, infile);
185 transformer.transform(src, res);
186 } finally {
187
188
189
190 try {
191 if (fis != null) {
192 fis.close();
193 }
194 } catch (IOException ignored) {
195
196 }
197 try {
198 if (fos != null) {
199 fos.close();
200 }
201 } catch (IOException ignored) {
202
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
215 private Source getSource(InputStream is, File infile)
216 throws ParserConfigurationException, SAXException {
217 return getSource(is, JAXPUtils.getSystemId(infile));
218 }
219
220
221 private Source getSource(InputStream is, String systemid)
222 throws ParserConfigurationException, SAXException {
223
224
225
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
240
241 src = new StreamSource(is);
242 }
243
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
256
257
258
259 InputStream xslStream = null;
260 try {
261
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
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
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
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
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
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
484 if (xmlCatalog != null) {
485 setEntityResolver(xmlCatalog);
486 setURIResolver(xmlCatalog);
487 }
488
489
490
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
505 public void configure(JDXSLTProcess xsltTask) {
506 JDXSLTProcess.Factory factory = xsltTask.getFactory();
507 if (factory != null) {
508 setFactory(factory.getName());
509
510
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
521 if (xmlCatalog != null) {
522 setEntityResolver(xmlCatalog);
523 setURIResolver(xmlCatalog);
524 }
525
526
527
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 }