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.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
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
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
174 public JDXSLTProcess() {
175 fileUtils = FileUtils.newFileUtils();
176 }
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
228 if(xslFile != null && styleurl != null) {
229 throw new BuildException(
230 "cannot specify both styleurl and xslfile", getLocation()
231 );
232 }
233
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
250 if(! (liaison instanceof JDXSLTLiaison3) && styleurl != null ) {
251 throw new BuildException(
252 "processor does not support style urls", getLocation()
253 );
254 }
255
256
257
258 if (liaison instanceof XSLTLoggerAware) {
259 ((XSLTLoggerAware) liaison).setLogger(this);
260 }
261
262 log("Using " + liaison.getClass().toString(), Project.MSG_VERBOSE);
263
264
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
274
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
286 if (inFile != null && outFile != null) {
287 process(inFile, outFile, styleob);
288 return;
289 }
290
291
292
293
294
295
296
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
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
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
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
569
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 }
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
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
670
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 }
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
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
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
1015 if ("name".equalsIgnoreCase(name)) {
1016 this.name = value;
1017 } else if ("value".equalsIgnoreCase(name)) {
1018
1019
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 }
1035
1036 }
1037
1038 /***
1039 * Mapper implementation of the "traditional" way <xslt>
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 }