View Javadoc

1   /*
2    * org.osjava.jardiff.JDXSLTProcess
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.File;
42  import java.util.Enumeration;
43  import java.util.Vector;
44  import org.apache.tools.ant.AntClassLoader;
45  import org.apache.tools.ant.BuildException;
46  import org.apache.tools.ant.DirectoryScanner;
47  import org.apache.tools.ant.DynamicConfigurator;
48  import org.apache.tools.ant.Project;
49  import org.apache.tools.ant.types.Mapper;
50  import org.apache.tools.ant.types.Path;
51  import org.apache.tools.ant.types.Reference;
52  import org.apache.tools.ant.types.XMLCatalog;
53  import org.apache.tools.ant.util.FileNameMapper;
54  import org.apache.tools.ant.util.FileUtils;
55  
56  import org.apache.tools.ant.taskdefs.MatchingTask;
57  import org.apache.tools.ant.taskdefs.XSLTLiaison2;
58  import org.apache.tools.ant.taskdefs.XSLTLiaison;
59  import org.apache.tools.ant.taskdefs.XSLTLogger;
60  import org.apache.tools.ant.taskdefs.XSLTLoggerAware;
61  
62  public class JDXSLTProcess extends MatchingTask implements XSLTLogger {
63      
64      /*** destination directory */
65      private File destDir = null;
66  
67      /*** where to find the source XML file, default is the project's basedir */
68      private File baseDir = null;
69  
70      /*** XSL stylesheet */
71      private String xslFile = null;
72  
73      /*** extension of the files produced by XSL processing */
74      private String targetExtension = ".html";
75  
76      /*** additional parameters to be passed to the stylesheets */
77      private Vector params = new Vector();
78  
79      /*** Input XML document to be used */
80      private File inFile = null;
81  
82      /*** Output file */
83      private File outFile = null;
84  
85      /*** The name of the XSL processor to use */
86      private String processor;
87  
88      /*** Classpath to use when trying to load the XSL processor */
89      private Path classpath = null;
90  
91      /***
92       * Systemid of stylesheet (url).
93       */
94      // JARDIFF: Added
95      private String styleurl = null;
96  
97      /*** The Liason implementation to use to communicate with the XSL
98       *  processor */
99      private XSLTLiaison liaison;
100 
101     /*** Flag which indicates if the stylesheet has been loaded into
102      *  the processor */
103     private boolean stylesheetLoaded = false;
104 
105     /*** force output of target files even if they already exist */
106     private boolean force = false;
107 
108     /*** Utilities used for file operations */
109     private FileUtils fileUtils;
110 
111     /*** XSL output properties to be used */
112     private Vector outputProperties = new Vector();
113 
114     /*** for resolving entities such as dtds */
115     private XMLCatalog xmlCatalog = new XMLCatalog();
116 
117     // JARDIFF: Modified.
118     /*** Name of the TRAX Liaison class */
119     private static final String TRAX_LIAISON_CLASS =
120                         "org.osjava.jardiff.ant.JDTraXLiaison";
121 
122     /*** Name of the now-deprecated XSLP Liaison class */
123     private static final String XSLP_LIAISON_CLASS =
124                         "org.apache.tools.ant.taskdefs.optional.XslpLiaison";
125 
126     /*** Name of the now-deprecated Xalan liaison class */
127     private static final String XALAN_LIAISON_CLASS =
128                         "org.apache.tools.ant.taskdefs.optional.XalanLiaison";
129 
130     /***
131      * Whether to style all files in the included directories as well.
132      *
133      * @since Ant 1.5
134      */
135     private boolean performDirectoryScan = true;
136 
137     /***
138      * factory element for TraX processors only
139      * @since Ant 1.6
140      */
141     private Factory factory = null;
142 
143     /***
144      * whether to reuse Transformer if transforming multiple files.
145      * @since 1.5.2
146      */
147     private boolean reuseLoadedStylesheet = true;
148 
149     /***
150      * AntClassLoader for the nested <classpath> - if set.
151      *
152      * <p>We keep this here in order to reset the context classloader
153      * in execute.  We can't use liaison.getClass().getClassLoader()
154      * since the actual liaison class may have been loaded by a loader
155      * higher up (system classloader, for example).</p>
156      *
157      * @since Ant 1.6.2
158      */
159     private AntClassLoader loader = null;
160 
161     /***
162      * Mapper to use when a set of files gets processed.
163      *
164      * @since Ant 1.6.2
165      */
166     private Mapper mapperElement = null;
167 
168 
169     /***
170      * Creates a new JDXSLTProcess Task.
171      * @since JarDiff 0.2
172      */
173     // JARDIFF:Modified 
174     public JDXSLTProcess() {
175         fileUtils = FileUtils.newFileUtils();
176     } //-- JDXSLTProcess
177 
178     /***
179      * Whether to style all files in the included directories as well;
180      * optional, default is true.
181      *
182      * @param b true if files in included directories are processed.
183      * @since Ant 1.5
184      */
185     public void setScanIncludedDirectories(boolean b) {
186         performDirectoryScan = b;
187     }
188 
189     /***
190      * Controls whether the stylesheet is reloaded for every transform.
191      *
192      * <p>Setting this to true may get around a bug in certain
193      * Xalan-J versions, default is false.</p>
194      *
195      * @since Ant 1.5.2
196      */
197     public void setReloadStylesheet(boolean b) {
198         reuseLoadedStylesheet = !b;
199     }
200 
201     /***
202      * Defines the mapper to map source to destination files.
203      * @exception BuildException if more than one mapper is defined
204      * @since Ant 1.6.2
205      */
206     public void addMapper(Mapper mapper) {
207         if (mapperElement != null) {
208             throw new BuildException("Cannot define more than one mapper",
209                                      getLocation());
210         }
211         mapperElement = mapper;
212     }
213 
214     /***
215      * Executes the task.
216      *
217      * @exception BuildException if there is an execution problem.
218      * @todo validate that if either in or our is defined, then both are
219      */
220     public void execute() throws BuildException {
221         File savedBaseDir = baseDir;
222 
223         DirectoryScanner scanner;
224         String[]         list;
225         String[]         dirs;
226 
227         // JARDIFF: Added check
228         if(xslFile != null && styleurl != null) {
229             throw new BuildException(
230                     "cannot specify both styleurl and xslfile", getLocation()
231                     );
232         }
233         // JARDIFF: modified
234         if (xslFile == null && styleurl == null) {
235             throw new BuildException("no stylesheet specified", getLocation());
236         }
237 
238         if (inFile != null && !inFile.exists()) {
239             throw new BuildException("input file " + inFile.toString() + " does not exist", getLocation());
240         }
241 
242         try {
243             if (baseDir == null) {
244                 baseDir = getProject().resolveFile(".");
245             }
246 
247             liaison = getLiaison();
248 
249             // JARDIFF: Added
250             if(! (liaison instanceof JDXSLTLiaison3) && styleurl != null ) {
251                 throw new BuildException(
252                         "processor does not support style urls", getLocation()
253                         );
254             }
255 
256 
257             // check if liaison wants to log errors using us as logger
258             if (liaison instanceof XSLTLoggerAware) {
259                 ((XSLTLoggerAware) liaison).setLogger(this);
260             }
261 
262             log("Using " + liaison.getClass().toString(), Project.MSG_VERBOSE);
263 
264             // JARDIFF: Modified.
265             Object styleob;
266             if(styleurl != null) {
267                 styleob = styleurl;
268             } else {
269                 File stylesheet = getProject().resolveFile(xslFile);
270                 if (!stylesheet.exists()) {
271                     stylesheet = fileUtils.resolveFile(baseDir, xslFile);
272                     /*
273                      * shouldn't throw out deprecation warnings before we know,
274                      * the wrong version has been used.
275                      */
276                     if (stylesheet.exists()) {
277                         log("DEPRECATED - the style attribute should be relative "
278                                 + "to the project\'s");
279                         log("             basedir, not the tasks\'s basedir.");
280                     }
281                 }
282                 styleob = stylesheet;
283             }
284 
285             // if we have an in file and out then process them
286             if (inFile != null && outFile != null) {
287                 process(inFile, outFile, styleob);
288                 return;
289             }
290 
291             /*
292              * if we get here, in and out have not been specified, we are
293              * in batch processing mode.
294              */
295 
296             //-- make sure Source directory exists...
297             if (destDir == null) {
298                 String msg = "destdir attributes must be set!";
299                 throw new BuildException(msg);
300             }
301             scanner = getDirectoryScanner(baseDir);
302             log("Transforming into " + destDir, Project.MSG_INFO);
303 
304             // Process all the files marked for styling
305             list = scanner.getIncludedFiles();
306             for (int i = 0; i < list.length; ++i) {
307                 process(baseDir, list[i], destDir, styleob);
308             }
309             if (performDirectoryScan) {
310                 // Process all the directories marked for styling
311                 dirs = scanner.getIncludedDirectories();
312                 for (int j = 0; j < dirs.length; ++j) {
313                     list = new File(baseDir, dirs[j]).list();
314                     for (int i = 0; i < list.length; ++i) {
315                         process(baseDir, dirs[j] + File.separator + list[i],
316                                 destDir, styleob);
317                     }
318                 }
319             }
320         } finally {
321             if (loader != null) {
322                 loader.resetThreadContextLoader();
323                 loader = null;
324             }
325             liaison = null;
326             stylesheetLoaded = false;
327             baseDir = savedBaseDir;
328         }
329     }
330 
331     /***
332      * Set whether to check dependencies, or always generate;
333      * optional, default is false.
334      *
335      * @param force true if always generate.
336      */
337     public void setForce(boolean force) {
338         this.force = force;
339     }
340 
341     /***
342      * Set the base directory;
343      * optional, default is the project's basedir.
344      *
345      * @param dir the base directory
346      **/
347     public void setBasedir(File dir) {
348         baseDir = dir;
349     }
350 
351     /***
352      * Set the destination directory into which the XSL result
353      * files should be copied to;
354      * required, unless <tt>in</tt> and <tt>out</tt> are
355      * specified.
356      * @param dir the name of the destination directory
357      **/
358     public void setDestdir(File dir) {
359         destDir = dir;
360     }
361 
362     /***
363      * Set the desired file extension to be used for the target;
364      * optional, default is html.
365      * @param name the extension to use
366      **/
367     public void setExtension(String name) {
368         targetExtension = name;
369     }
370 
371     /***
372      * Name of the stylesheet to use - given either relative
373      * to the project's basedir or as an absolute path; required.
374      *
375      * @param xslFile the stylesheet to use
376      */
377     public void setStyle(String xslFile) {
378         this.xslFile = xslFile;
379     }
380 
381     /***
382      * Set the optional classpath to the XSL processor
383      *
384      * @param classpath the classpath to use when loading the XSL processor
385      */
386     public void setClasspath(Path classpath) {
387         createClasspath().append(classpath);
388     }
389 
390     /***
391      * Set the optional classpath to the XSL processor
392      *
393      * @return a path instance to be configured by the Ant core.
394      */
395     public Path createClasspath() {
396         if (classpath == null) {
397             classpath = new Path(getProject());
398         }
399         return classpath.createPath();
400     }
401 
402     /***
403      * Set the reference to an optional classpath to the XSL processor
404      *
405      * @param r the id of the Ant path instance to act as the classpath
406      *          for loading the XSL processor
407      */
408     public void setClasspathRef(Reference r) {
409         createClasspath().setRefid(r);
410     }
411 
412     /***
413      * Set the name of the XSL processor to use; optional, default trax.
414      * Other values are "xalan" for Xalan1 and "xslp" for XSL:P, though the
415      * later is strongly deprecated.
416      *
417      * @param processor the name of the XSL processor
418      */
419     public void setProcessor(String processor) {
420         this.processor = processor;
421     }
422 
423     /***
424      * Set the stylesheet url.
425      *
426      * @since JarDiff 0.2
427      */
428     public void setStyleurl(String styleurl) {
429         this.styleurl = styleurl;
430     }
431 
432     /***
433      * Add the catalog to our internal catalog
434      *
435      * @param xmlCatalog the XMLCatalog instance to use to look up DTDs
436      */
437     public void addConfiguredXMLCatalog(XMLCatalog xmlCatalog) {
438         this.xmlCatalog.addConfiguredXMLCatalog(xmlCatalog);
439     }
440 
441     /***
442      * Load processor here instead of in setProcessor - this will be
443      * called from within execute, so we have access to the latest
444      * classpath.
445      *
446      * @param proc the name of the processor to load.
447      * @exception Exception if the processor cannot be loaded.
448      */
449     private void resolveProcessor(String proc) throws Exception {
450         if (proc.equals("trax")) {
451             final Class clazz = loadClass(TRAX_LIAISON_CLASS);
452             liaison = (XSLTLiaison) clazz.newInstance();
453         } else if (proc.equals("xslp")) {
454             log("DEPRECATED - xslp processor is deprecated. Use trax "
455                 + "instead.");
456             final Class clazz = loadClass(XSLP_LIAISON_CLASS);
457             liaison = (XSLTLiaison) clazz.newInstance();
458         } else if (proc.equals("xalan")) {
459             log("DEPRECATED - xalan processor is deprecated. Use trax "
460                 + "instead.");
461             final Class clazz = loadClass(XALAN_LIAISON_CLASS);
462             liaison = (XSLTLiaison) clazz.newInstance();
463         } else {
464             liaison = (XSLTLiaison) loadClass(proc).newInstance();
465         }
466     }
467 
468     /***
469      * Load named class either via the system classloader or a given
470      * custom classloader.
471      *
472      * @param classname the name of the class to load.
473      * @return the requested class.
474      * @exception Exception if the class could not be loaded.
475      */
476     private Class loadClass(String classname) throws Exception {
477         if (classpath == null) {
478             return Class.forName(classname);
479         } else {
480             loader = getProject().createClassLoader(classpath);
481             loader.setThreadContextLoader();
482             Class c = Class.forName(classname, true, loader);
483             return c;
484         }
485     }
486 
487     /***
488      * Specifies the output name for the styled result from the
489      * <tt>in</tt> attribute; required if <tt>in</tt> is set
490      *
491      * @param outFile the output File instance.
492      */
493     public void setOut(File outFile) {
494         this.outFile = outFile;
495     }
496 
497     /***
498      * specifies a single XML document to be styled. Should be used
499      * with the <tt>out</tt> attribute; ; required if <tt>out</tt> is set
500      *
501      * @param inFile the input file
502      */
503     public void setIn(File inFile) {
504         this.inFile = inFile;
505     }
506 
507     /***
508      * Processes the given input XML file and stores the result
509      * in the given resultFile.
510      *
511      * @param baseDir the base directory for resolving files.
512      * @param xmlFile the input file
513      * @param destDir the destination directory
514      * @param stylesheet the stylesheet to use.
515      * @exception BuildException if the processing fails.
516      */
517     private void process(File baseDir, String xmlFile, File destDir,
518                          Object stylesheet)
519         throws BuildException {
520 
521         File   outFile = null;
522         File   inFile = null;
523 
524         try {
525             long styleSheetLastModified = 0L;
526             // JARDIFF: Added
527             if(stylesheet instanceof File) {
528                 styleSheetLastModified = ( (File)stylesheet ).lastModified();
529             }
530             inFile = new File(baseDir, xmlFile);
531 
532             if (inFile.isDirectory()) {
533                 log("Skipping " + inFile + " it is a directory.",
534                     Project.MSG_VERBOSE);
535                 return;
536             }
537 
538             FileNameMapper mapper = null;
539             if (mapperElement != null) {
540                 mapper = mapperElement.getImplementation();
541             } else {
542                 mapper = new StyleMapper();
543             }
544 
545             String[] outFileName = mapper.mapFileName(xmlFile);
546             if (outFileName == null || outFileName.length == 0) {
547                 log("Skipping " + inFile + " it cannot get mapped to output.",
548                     Project.MSG_VERBOSE);
549                 return;
550             } else if (outFileName == null || outFileName.length > 1) {
551                 log("Skipping " + inFile + " its mapping is ambiguos.",
552                     Project.MSG_VERBOSE);
553                 return;
554             }
555 
556             outFile = new File(destDir, outFileName[0]);
557 
558             if (force
559                 || inFile.lastModified() > outFile.lastModified()
560                 || styleSheetLastModified > outFile.lastModified()) {
561                 ensureDirectoryFor(outFile);
562                 log("Processing " + inFile + " to " + outFile);
563 
564                 configureLiaison(stylesheet);
565                 liaison.transform(inFile, outFile);
566             }
567         } catch (Exception ex) {
568             // If failed to process document, must delete target document,
569             // or it will not attempt to process it the second time
570             log("Failed to process " + inFile, Project.MSG_INFO);
571             if (outFile != null) {
572                 outFile.delete();
573             }
574 
575             throw new BuildException(ex);
576         }
577 
578     } //-- processXML
579 
580     /***
581      * Process the input file to the output file with the given stylesheet.
582      *
583      * @param inFile the input file to process.
584      * @param outFile the destination file.
585      * @param stylesheet the stylesheet to use.
586      * @exception BuildException if the processing fails.
587      */
588     private void process(File inFile, File outFile, Object stylesheet)
589          throws BuildException {
590         try {
591             long styleSheetLastModified = 0L;
592             // JARDIFF: Added
593             if(stylesheet instanceof File) {
594                 styleSheetLastModified = ( (File)stylesheet ).lastModified();
595             }
596             log("In file " + inFile + " time: " + inFile.lastModified(),
597                 Project.MSG_DEBUG);
598             log("Out file " + outFile + " time: " + outFile.lastModified(),
599                 Project.MSG_DEBUG);
600             log("Style file " + xslFile + " time: " + styleSheetLastModified,
601                 Project.MSG_DEBUG);
602             if (force || inFile.lastModified() >= outFile.lastModified()
603                 || styleSheetLastModified >= outFile.lastModified()) {
604                 ensureDirectoryFor(outFile);
605                 log("Processing " + inFile + " to " + outFile,
606                     Project.MSG_INFO);
607                 configureLiaison(stylesheet);
608                 liaison.transform(inFile, outFile);
609             } else {
610                 log("Skipping input file " + inFile
611                     + " because it is older than output file " + outFile
612                     + " and so is the stylesheet " + stylesheet, Project.MSG_DEBUG);
613             }
614         } catch (Exception ex) {
615             log("Failed to process " + inFile, Project.MSG_INFO);
616             if (outFile != null) {
617                 outFile.delete();
618             }
619             throw new BuildException(ex);
620         }
621     }
622 
623     /***
624      * Ensure the directory exists for a given file
625      *
626      * @param targetFile the file for which the directories are required.
627      * @exception BuildException if the directories cannot be created.
628      */
629     private void ensureDirectoryFor(File targetFile)
630          throws BuildException {
631         File directory = fileUtils.getParentFile(targetFile);
632         if (!directory.exists()) {
633             if (!directory.mkdirs()) {
634                 throw new BuildException("Unable to create directory: "
635                                          + directory.getAbsolutePath());
636             }
637         }
638     }
639 
640     /***
641      * Get the factory instance configured for this processor
642      *
643      * @return the factory instance in use
644      */
645     public Factory getFactory() {
646         return factory;
647     }
648 
649     /***
650      * Get the XML catalog containing entity definitions
651      *
652      * @return the XML catalog for the task.
653      */
654     public XMLCatalog getXMLCatalog() {
655         return xmlCatalog;
656     }
657 
658     public Enumeration getOutputProperties() {
659         return outputProperties.elements();
660     }
661 
662 
663     /***
664      * Get the Liason implementation to use in processing.
665      *
666      * @return an instance of the XSLTLiason interface.
667      */
668     protected XSLTLiaison getLiaison() {
669         // if processor wasn't specified, see if TraX is available.  If not,
670         // default it to xslp or xalan, depending on which is in the classpath
671         if (liaison == null) {
672             if (processor != null) {
673                 try {
674                     resolveProcessor(processor);
675                 } catch (Exception e) {
676                     throw new BuildException(e);
677                 }
678             } else {
679                 try {
680                     resolveProcessor("trax");
681                 } catch (Throwable e1) {
682                     try {
683                         resolveProcessor("xalan");
684                     } catch (Throwable e2) {
685                         try {
686                             resolveProcessor("xslp");
687                         } catch (Throwable e3) {
688                             e3.printStackTrace();
689                             e2.printStackTrace();
690                             throw new BuildException(e1);
691                         }
692                     }
693                 }
694             }
695         }
696         return liaison;
697     }
698 
699     /***
700      * Create an instance of an XSL parameter for configuration by Ant.
701      *
702      * @return an instance of the Param class to be configured.
703      */
704     public Param createParam() {
705         Param p = new Param();
706         params.addElement(p);
707         return p;
708     }
709 
710     /***
711      * The Param inner class used to store XSL parameters
712      */
713     public static class Param {
714         /*** The parameter name */
715         private String name = null;
716 
717         /*** The parameter's value */
718         private String expression = null;
719 
720         private String ifProperty;
721         private String unlessProperty;
722         private Project project;
723 
724         /***
725          * Set the current project
726          *
727          * @param project the current project
728          */
729         public void setProject(Project project) {
730             this.project = project;
731         }
732 
733         /***
734          * Set the parameter name.
735          *
736          * @param name the name of the parameter.
737          */
738         public void setName(String name) {
739             this.name = name;
740         }
741 
742         /***
743          * The parameter value
744          * NOTE : was intended to be an XSL expression.
745          * @param expression the parameter's value.
746          */
747         public void setExpression(String expression) {
748             this.expression = expression;
749         }
750 
751         /***
752          * Get the parameter name
753          *
754          * @return the parameter name
755          * @exception BuildException if the name is not set.
756          */
757         public String getName() throws BuildException {
758             if (name == null) {
759                 throw new BuildException("Name attribute is missing.");
760             }
761             return name;
762         }
763 
764         /***
765          * Get the parameter's value
766          *
767          * @return the parameter value
768          * @exception BuildException if the value is not set.
769          */
770         public String getExpression() throws BuildException {
771             if (expression == null) {
772                 throw new BuildException("Expression attribute is missing.");
773             }
774             return expression;
775         }
776 
777         /***
778          * Set whether this param should be used.  It will be
779          * used if the property has been set, otherwise it won't.
780          * @param ifProperty name of property
781          */
782         public void setIf(String ifProperty) {
783             this.ifProperty = ifProperty;
784         }
785 
786         /***
787          * Set whether this param should NOT be used. It
788          * will not be used if the property has been set, otherwise it
789          * will be used.
790          * @param unlessProperty name of property
791          */
792         public void setUnless(String unlessProperty) {
793             this.unlessProperty = unlessProperty;
794         }
795         /***
796          * Ensures that the param passes the conditions placed
797          * on it with <code>if</code> and <code>unless</code> properties.
798          */
799         public boolean shouldUse() {
800             if (ifProperty != null && project.getProperty(ifProperty) == null) {
801                 return false;
802             } else if (unlessProperty != null
803                     && project.getProperty(unlessProperty) != null) {
804                 return false;
805             }
806 
807             return true;
808         }
809     } // Param
810 
811 
812     /***
813      * Create an instance of an output property to be configured.
814      * @return the newly created output property.
815      * @since Ant 1.5
816      */
817     public OutputProperty createOutputProperty() {
818         OutputProperty p = new OutputProperty();
819         outputProperties.addElement(p);
820         return p;
821     }
822 
823 
824     /***
825      * Specify how the result tree should be output as specified
826      * in the <a href="http://www.w3.org/TR/xslt#output">
827      * specification</a>.
828      * @since Ant 1.5
829      */
830     public static class OutputProperty {
831         /*** output property name */
832         private String name;
833 
834         /*** output property value */
835         private String value;
836 
837         /***
838          * @return the output property name.
839          */
840         public String getName() {
841             return name;
842         }
843 
844         /***
845          * set the name for this property
846          * @param name A non-null String that specifies an
847          * output property name, which may be namespace qualified.
848          */
849         public void setName(String name) {
850             this.name = name;
851         }
852 
853         /***
854          * @return the output property value.
855          */
856         public String getValue() {
857             return value;
858         }
859 
860         /***
861          * set the value for this property
862          * @param value The non-null string value of the output property.
863          */
864         public void setValue(String value) {
865             this.value = value;
866         }
867     }
868 
869     /***
870      * Initialize internal instance of XMLCatalog
871      */
872     public void init() throws BuildException {
873         super.init();
874         xmlCatalog.setProject(getProject());
875     }
876 
877     /***
878      * Loads the stylesheet and set xsl:param parameters.
879      *
880      * @param stylesheet the file form which to load the stylesheet.
881      * @exception BuildException if the stylesheet cannot be loaded.
882      */
883     protected void configureLiaison(Object stylesheet) throws BuildException {
884         if (stylesheetLoaded && reuseLoadedStylesheet) {
885             return;
886         }
887         stylesheetLoaded = true;
888 
889         try {
890             log("Loading stylesheet " + stylesheet, Project.MSG_INFO);
891             // JARDIFF: ADDED
892             if(stylesheet instanceof File) {
893                 liaison.setStylesheet((File) stylesheet);
894             } else {
895                 if(liaison instanceof JDXSLTLiaison3) {
896                     ((JDXSLTLiaison3)liaison).
897                         setStylesheet((String) stylesheet);
898                 } else {
899                     String msg = "liaison does not support stylesheet urls";
900                     throw new BuildException(msg, getLocation());
901                 }
902             }
903             for (Enumeration e = params.elements(); e.hasMoreElements();) {
904                 Param p = (Param) e.nextElement();
905                 if (p.shouldUse()) {
906                     liaison.addParam(p.getName(), p.getExpression());
907                 }
908             }
909             // JARDIFF: Modified (nasty workaround)
910             if (liaison instanceof JDXSLTLiaison3) {
911                 ((JDXSLTLiaison3) liaison).configure(this);
912             }
913         } catch (Exception ex) {
914             log("Failed to transform using stylesheet " + stylesheet,
915                  Project.MSG_INFO);
916             throw new BuildException(ex);
917         }
918     }
919 
920     /***
921      * Create the factory element to configure a trax liaison.
922      * @return the newly created factory element.
923      * @throws BuildException if the element is created more than one time.
924      */
925     public Factory createFactory() throws BuildException {
926         if (factory != null) {
927             throw new BuildException("'factory' element must be unique");
928         }
929         factory = new Factory();
930         return factory;
931     }
932 
933     /***
934      * The factory element to configure a transformer factory
935      * @since Ant 1.6
936      */
937     public static class Factory {
938 
939         /*** the factory class name to use for TraXLiaison */
940         private String name;
941 
942         /***
943          * the list of factory attributes to use for TraXLiaison
944          */
945         private Vector attributes = new Vector();
946 
947         /***
948          * @return the name of the factory.
949          */
950         public String getName() {
951             return name;
952         }
953 
954         /***
955          * Set the name of the factory
956          * @param name the name of the factory.
957          */
958         public void setName(String name) {
959             this.name = name;
960         }
961 
962         /***
963          * Create an instance of a factory attribute.
964          * the newly created factory attribute
965          */
966         public void addAttribute(Attribute attr) {
967             attributes.addElement(attr);
968         }
969 
970         /***
971          * return the attribute elements.
972          * @return the enumeration of attributes
973          */
974         public Enumeration getAttributes() {
975             return attributes.elements();
976         }
977 
978         /***
979          * A JAXP factory attribute. This is mostly processor specific, for
980          * example for Xalan 2.3+, the following attributes could be set:
981          * <ul>
982          *  <li>http://xml.apache.org/xalan/features/optimize (true|false) </li>
983          *  <li>http://xml.apache.org/xalan/features/incremental (true|false) </li>
984          * </ul>
985          */
986         public static class Attribute implements DynamicConfigurator {
987 
988             /*** attribute name, mostly processor specific */
989             private String name;
990 
991             /*** attribute value, often a boolean string */
992             private Object value;
993 
994             /***
995              * @return the attribute name.
996              */
997             public String getName() {
998                 return name;
999             }
1000 
1001             /***
1002              * @return the output property value.
1003              */
1004             public Object getValue() {
1005                 return value;
1006             }
1007 
1008             public Object createDynamicElement(String name) throws BuildException {
1009                 return null;
1010             }
1011 
1012             public void setDynamicAttribute(String name, String value)
1013                     throws BuildException {
1014                 // only 'name' and 'value' exist.
1015                 if ("name".equalsIgnoreCase(name)) {
1016                     this.name = value;
1017                 } else if ("value".equalsIgnoreCase(name)) {
1018                     // a value must be of a given type
1019                     // say boolean|integer|string that are mostly used.
1020                     if ("true".equalsIgnoreCase(value)
1021                             || "false".equalsIgnoreCase(value)) {
1022                         this.value = new Boolean(value);
1023                     } else {
1024                         try {
1025                             this.value = new Integer(value);
1026                         } catch (NumberFormatException e) {
1027                             this.value = value;
1028                         }
1029                     }
1030                 } else {
1031                     throw new BuildException("Unsupported attribute: " + name);
1032                 }
1033             }
1034         } // -- class Attribute
1035 
1036     } // -- class Factory
1037 
1038     /***
1039      * Mapper implementation of the "traditional" way &lt;xslt&gt;
1040      * mapped filenames.
1041      *
1042      * <p>If the file has an extension, chop it off.  Append whatever
1043      * the user has specified as extension or ".html".</p>
1044      *
1045      * @since Ant 1.6.2
1046      */
1047     private class StyleMapper implements FileNameMapper {
1048         public void setFrom(String from) {}
1049         public void setTo(String to) {}
1050         public String[] mapFileName(String xmlFile) {
1051             int dotPos = xmlFile.lastIndexOf('.');
1052             if (dotPos > 0) {
1053                 xmlFile = xmlFile.substring(0, dotPos);
1054             }
1055             return new String[] {xmlFile + targetExtension};
1056         }
1057     }
1058 }