View Javadoc

1   package org.apache.dvsl;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.Map;
23  import java.util.Properties;
24  import java.util.Vector;
25  import java.util.HashMap;
26  import java.util.Enumeration;
27  
28  import java.io.File;
29  import java.io.FileInputStream;
30  import java.io.BufferedWriter;
31  import java.io.OutputStreamWriter;
32  import java.io.FileOutputStream;
33  
34  import org.apache.tools.ant.AntClassLoader;
35  import org.apache.tools.ant.BuildException;
36  import org.apache.tools.ant.DirectoryScanner;
37  import org.apache.tools.ant.Project;
38  import org.apache.tools.ant.taskdefs.MatchingTask;
39  import org.apache.tools.ant.types.Path;
40  import org.apache.tools.ant.types.Reference;
41  
42  
43  /**
44   * A Task to process via DVSL a set of XML documents. This is
45   * useful for building views of XML based documentation.
46   * arguments:
47   * <ul>
48   * <li>basedir
49   * <li>destdir
50   * <li>style
51   * <li>in
52   * <li>out
53   * <li>logfile
54   * <li>includes
55   * <li>excludes
56   * <li>force
57   * <li>extension
58   * <li>outputencoding
59   * <li>classpath
60   * <li>classpathref
61   * <li>toolboxfile
62   * <li>velocityconfigclass
63   * <li>
64   * </ul>
65   * <p>Of these arguments, the <b>sourcedir</b> and <b>destdir</b> are required, or,
66   * <b>in</b> and <b>out</b> are required.</p>
67   * <p>Following are the supported nested elements:</p>
68   * <ul>
69   * <li>&lt;include&gt;
70   * <li>&lt;exclude&gt;
71   * <li>&lt;classpath&gt;
72   * <li>&lt;tool name="toolbox-property" value="value-or-object" /&gt;
73   * <li>&lt;velconfig name="velocity-config-name" value="config-value" /&gt;
74   * </ul>
75   * <p>This task will recursively scan the sourcedir and destdir
76   * looking for XML documents to process via DVSL. </p>
77   *
78   * <p>This task was adapted from Ant's &lt;style&gt; task (XSLTProcess class) from the
79   * 1.4.1 release.</p>
80   *
81   * @author <a href="mailto:kvisco@exoffice.com">Keith Visco</a>
82   * @author <a href="mailto:rubys@us.ibm.com">Sam Ruby</a>
83   * @author <a href="mailto:russgold@acm.org">Russell Gold</a>
84   * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
85   * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
86   * @author <a href="mailto:billb@progress.com">Bill Burton.</a>
87   */
88  
89  public class DVSLTask extends MatchingTask
90  {
91      /**
92       *  Supported app values
93       */
94      public static final String INFILENAME = "infilename";
95      public static final String OUTFILENAME = "outfilename";
96  
97      private DVSL dvsl;
98  
99      private File destDir = null;
100     private File baseDir = null;
101     private File stylesheet = null;
102     private String stylesheetEncoding = null;
103     private File inFile = null;
104     private File outFile = null;
105     private File logFile = null;
106 
107     private String targetExtension = ".html";
108     private String outputEncoding = "UTF-8";
109 
110     private Path classpath = null;
111     private ClassLoader classLoader = null;
112 
113     private boolean force = false;
114 
115     private Vector toolAttr = new Vector();
116     private File toolboxFile = null;
117     private Properties toolboxProps = null;
118 
119     private String velConfigClass = null;
120     private Map velConfigMap = null;
121     private Vector velConfigAttr = new Vector();
122 
123     private boolean validatingParser = false;
124 
125     //private String outputtype = null;  // later when output type is supported
126 
127     /**
128      * Creates a new DVSLTask Task.
129      **/
130     public DVSLTask()
131     {
132         classLoader = DVSLTask.class.getClassLoader();
133     }
134 
135     /**
136      * Executes the task.
137      */
138     public void execute()
139             throws BuildException
140     {
141         DirectoryScanner scanner;
142         String[]         list;
143         String[]         dirs;
144 
145         if (stylesheet == null)
146         {
147             throw new BuildException("no stylesheet specified", getLocation());
148         }
149 
150         if (baseDir == null)
151         {
152             baseDir = getProject().resolveFile(".");
153         }
154 
155         /*
156          * make a DVSL and set validation
157          */
158         dvsl = new DVSL();
159 
160         dvsl.setValidatingParser(validatingParser);
161 
162         /*
163          *  Create a new Classloader for loading the Toolbox and the Velocity
164          *  properties class.
165          */
166         if (classpath != null)
167         {
168             classLoader = new AntClassLoader(getProject(), classpath);
169             dvsl.setClassLoader(classLoader);
170         }
171 
172         /*
173          * If the user gave us a velPropClass, we create an instance
174          * of that to use to config the VelocityEngine inside te DVSL
175          */
176         Object velConfigObj = null;
177 
178         if (velConfigClass != null)
179         {
180             try
181             {
182                 velConfigObj = Class.forName(velConfigClass, true, classLoader).newInstance();
183 
184                 if (velConfigObj instanceof Map)
185                 {
186                     velConfigMap = (Map) velConfigObj;
187                 }
188                 else
189                 {
190                     throw new BuildException("VelocityPropClass is not instanceof java.util.Map");
191                 }
192             }
193             catch(Exception ex)
194             {
195                 throw new BuildException("Error instantiating VelocityPropClass : "
196                         + ex);
197             }
198         }
199 
200         /*
201          * If any nested Velocity Config elements have been specified, overlay any settings
202          * in the Velocity Config object.
203          */
204         if (!velConfigAttr.isEmpty())
205         {
206             if (velConfigMap == null)
207             {
208                velConfigMap = new HashMap();
209             }
210 
211             /*
212              * Now copy velocity config attributes into the Map
213              */
214             for (Enumeration e = velConfigAttr.elements(); e.hasMoreElements();)
215             {
216                 VelocityConfig p = (VelocityConfig)e.nextElement();
217                 velConfigMap.put(p.getName(), p.getValue());
218             }
219         }
220 
221         /*
222          * Finally, set the Velocity Config object in DVSL if it's valid.
223          */
224         if (velConfigMap != null)
225         {
226             dvsl.setVelocityConfig(velConfigMap);
227         }
228 
229         /*
230          * If a logfile attribute was specified, use that for the log file name,
231          * otherwise use a Velocity to Ant logging adapter.
232          */
233         if (logFile != null)
234         {
235             dvsl.setLogFile(logFile);
236         }
237         else
238         {
239             dvsl.setLogChute(new AntLogChute(this));
240         }
241 
242         /*
243          * now the stylesheet
244          */
245         try
246         {
247             log("Loading stylesheet " + stylesheet, Project.MSG_INFO);
248             dvsl.setStylesheet(stylesheet, stylesheetEncoding);
249         }
250         catch (Exception ex)
251         {
252             log("Failed to read stylesheet " + stylesheet, Project.MSG_INFO);
253             throw new BuildException(ex);
254         }
255 
256         /*
257          *  now, if we were given a toolbox, set that up too
258          */
259         toolboxProps = new Properties();
260 
261         try
262         {
263             if (toolboxFile != null)
264             {
265                 toolboxProps.load(new FileInputStream(toolboxFile));
266             }
267 
268             /*
269              *  Overlay any parameters
270              */
271             for (Enumeration e = toolAttr.elements(); e.hasMoreElements();)
272             {
273                 Tool p = (Tool)e.nextElement();
274                 toolboxProps.setProperty(p.getName(), p.getValue());
275             }
276 
277             dvsl.setToolbox(toolboxProps);
278         }
279         catch(Exception ee)
280         {
281             throw new BuildException("Error loading the toolbox : " + ee);
282         }
283 
284         /*
285          * if we have an in file and out then process them
286          */
287 
288         if (inFile != null && outFile != null)
289         {
290             process(inFile, outFile, stylesheet);
291             return;
292         }
293 
294         /*
295          * if we get here, in and out have not been specified, we are
296          * in batch processing mode.
297          */
298 
299         /*
300          *   make sure Source directory exists...
301          */
302         if (destDir == null)
303         {
304             String msg = "destdir attributes must be set!";
305             throw new BuildException(msg);
306         }
307 
308         scanner = getDirectoryScanner(baseDir);
309         log("Transforming into "+destDir, Project.MSG_INFO);
310 
311         /*
312          *  Process all the files marked for styling
313          */
314         list = scanner.getIncludedFiles();
315 
316         for (int i = 0;i < list.length; ++i)
317         {
318             process(baseDir, list[i], destDir, stylesheet);
319         }
320 
321         /*
322          *  Process all the directoried marked for styling
323          */
324         dirs = scanner.getIncludedDirectories();
325 
326         for (int j = 0;j < dirs.length;++j)
327         {
328             list=new File(baseDir,dirs[j]).list();
329 
330             for (int i = 0;i < list.length;++i)
331             {
332                 process(baseDir, list[i], destDir, stylesheet);
333             }
334         }
335     } //-- execute
336 
337     /**
338      * Set whether to check dependencies, or always generate.
339      * @param force false to check dependencies, true to always generate
340      */
341     public void setForce(boolean force)
342     {
343         this.force = force;
344     }
345 
346     /**
347      * Set the base directory.
348      * @param dir name of the base directory
349      */
350     public void setBasedir(File dir)
351     {
352         baseDir = dir;
353     }
354 
355     /**
356      * Set the destination directory where the generated
357      * files should be directed.
358      * @param dir name of the destination directory
359      */
360     public void setDestdir(File dir)
361     {
362         destDir = dir;
363     }
364 
365     /**
366      * Set the desired file extension to be used for the target files.
367      * If not specified, &quot;<code>.html</code>&quot; is used.
368      * @param name the extension to use
369      */
370     public void setExtension(String name)
371     {
372         targetExtension = name;
373     }
374 
375     /**
376      * Sets the file to use for stylesheet.
377      * @param dvslFile stylesheet filename
378      */
379     public void setStyle(File dvslFile)
380     {
381         this.stylesheet = dvslFile;
382     }
383 
384     /**
385      * Sets the encoding of stylesheet file.
386      * @param dvslFileEncoding encoding of stylesheet file
387      */
388     public void setStyleEncoding(String dvslFileEncoding)
389     {
390         this.stylesheetEncoding = dvslFileEncoding;
391     }
392 
393     /**
394      * Sets the file to use for logging.  If not specified, all logging
395      * is directed through Ant's logging system.
396      * @param logFile logging filename
397      */
398     public void setLogFile(File logFile)
399     {
400         this.logFile = logFile;
401     }
402 
403     /**
404      * Sets the Toolbox properties file to use.
405      * @param toolboxFile properties file of tools
406      * @deprecated use setToolboxFile instead
407      */
408     public void setToolbox(String toolboxFile)
409     {
410         log("DEPRECATED - use the toolboxfile attribute instead");
411         this.toolboxFile = new File(toolboxFile);
412     }
413 
414     /**
415      * Sets the Toolbox properties file to use.
416      * @param toolboxFile properties file of tools
417      */
418     public void setToolboxFile(File toolboxFile)
419     {
420         this.toolboxFile = toolboxFile;
421     }
422 
423     /**
424      * Allows the user to specify a class that implements
425      * {@link java.util.Properties} that will have user properties
426      * to be used when setting up DVSL.
427      * @param classname Velocity configuration class to load
428      */
429     public void setVelocityConfigClass(String classname)
430     {
431         velConfigClass = classname;
432     }
433 
434     /**
435      * Sets an output file
436      * @param outFile output file
437      */
438     public void setOut(File outFile)
439     {
440         this.outFile = outFile;
441     }
442 
443     /**
444      * Sets an input xml file to be styled
445      * @param inFile input file
446      */
447     public void setIn(File inFile)
448     {
449         this.inFile = inFile;
450     }
451 
452     /**
453      * Sets the character encoding for output files.  If not specified,
454      * output is written with UTF-8 encodin6g.
455      * @param encoding Output encoding
456      */
457     public void setOutputEncoding(String encoding)
458     {
459         if (encoding != null)
460             this.outputEncoding = encoding;
461     }
462 
463     /**
464      * Set the classpath to load the Processor through (attribute).
465      * @param classpath classpath to set
466      */
467     public void setClasspath(Path classpath)
468     {
469         createClasspath().append(classpath);
470     }
471 
472     /**
473      * Set the classpath to load the Processor through (nested element).
474      */
475     public Path createClasspath()
476     {
477         if (classpath == null)
478         {
479             classpath = new Path(getProject());
480         }
481         return classpath.createPath();
482     }
483 
484     /**
485      * Set the classpath to load the Processor through via reference
486      * (attribute).
487      * @param r reference to classpath
488      */
489     public void setClasspathRef(Reference r)
490     {
491         createClasspath().setRefid(r);
492     }
493 
494     /**
495      *  Sets the flag to have DVSL use a validating parser for the
496      *  input documents
497      */
498     public void setValidatingParser(boolean validating)
499     {
500         if (validating == true)
501         {
502             log("Parser is validating.");
503         }
504 
505         validatingParser = validating;
506     }
507 
508     /**
509      *  Sets an application value from outside of the DVSL task
510      */
511     public void putAppValue(String name, Object o)
512     {
513         dvsl.putAppValue(name,o);
514     }
515 
516     /**
517      * Processes the given input XML file and stores the result
518      * in the given resultFile.
519      */
520     private void process(File baseDir, String xmlFile, File destDir,
521                          File stylesheet)
522         throws BuildException
523     {
524 
525         String fileExt=targetExtension;
526         File   outFile=null;
527         File   inFile=null;
528 
529         try
530         {
531             long styleSheetLastModified = stylesheet.lastModified();
532             inFile = new File(baseDir,xmlFile);
533             int dotPos = xmlFile.lastIndexOf('.');
534 
535             dvsl.putAppValue(INFILENAME, xmlFile);
536 
537             String outfilename;
538 
539             if (dotPos > 0)
540             {
541                 outfilename = xmlFile.substring(0, xmlFile.lastIndexOf('.'))
542                          + fileExt;
543                 outFile = new File(destDir, outfilename);
544             }
545             else
546             {
547                 outfilename = xmlFile + fileExt;
548                 outFile = new File(destDir, outfilename);
549             }
550 
551             dvsl.putAppValue(OUTFILENAME, outfilename);
552 
553             if (force ||
554                 inFile.lastModified() > outFile.lastModified() ||
555                 styleSheetLastModified > outFile.lastModified())
556             {
557                 ensureDirectoryFor(outFile);
558                 log("Processing "+inFile+" to "+outFile, Project.MSG_INFO);
559                 long time = transform(inFile, outFile);
560                 log("Processed "+inFile+" in "+time+" ms.", Project.MSG_VERBOSE);
561             }
562         }
563         catch (Exception ex)
564         {
565             /*
566              * If failed to process document, must delete target document,
567              * or it will not attempt to process it the second time
568              */
569 
570             log("Failed to process " + inFile, Project.MSG_INFO);
571 
572             if (outFile != null)
573             {
574                 outFile.delete();
575             }
576 
577             throw new BuildException(ex);
578         }
579     }
580 
581     private void process(File inFile, File outFile, File stylesheet)
582             throws BuildException
583     {
584         try
585         {
586             long styleSheetLastModified = stylesheet.lastModified();
587 
588             log("In file "+inFile+" time: " + inFile.lastModified() ,
589                     Project.MSG_DEBUG);
590             log("Out file "+outFile+" time: " + outFile.lastModified() ,
591                     Project.MSG_DEBUG);
592             log("Style file "+stylesheet+" time: " + styleSheetLastModified ,
593                     Project.MSG_DEBUG);
594 
595             if (force ||
596                 inFile.lastModified() > outFile.lastModified() ||
597                 styleSheetLastModified > outFile.lastModified())
598             {
599                 ensureDirectoryFor(outFile);
600                 log("Processing " + inFile + " to " + outFile, Project.MSG_INFO);
601                 long time = transform(inFile, outFile);
602                 log("Processed "+inFile+" in "+time+" ms.", Project.MSG_VERBOSE);
603             }
604         }
605         catch (Exception ex)
606         {
607             log("Failed to process " + inFile, Project.MSG_INFO);
608 
609             if(outFile!=null)
610             {
611                 outFile.delete();
612             }
613             throw new BuildException(ex);
614         }
615     }
616 
617     /**
618      * <p>
619      * Does the actual transform
620      * </p>
621      *
622      * @param inFile  XML document source
623      * @param outFile File for transformed input
624      * @return elapsed time in ms. for transformation
625      */
626     private long transform(File inFile, File outFile)
627         throws Exception
628     {
629         BufferedWriter writer =
630                 new BufferedWriter(new OutputStreamWriter(
631                                        new FileOutputStream(outFile),
632                                            outputEncoding));
633 
634         long time = dvsl.transform(inFile, writer);
635         writer.close();
636         return time;
637     }
638 
639     private void ensureDirectoryFor(File targetFile) throws BuildException
640     {
641         File directory = new File(targetFile.getParent());
642 
643         if (!directory.exists())
644         {
645             if (!directory.mkdirs())
646             {
647                 throw new BuildException("Unable to create directory: "
648                                          + directory.getAbsolutePath());
649             }
650         }
651     }
652 
653     /**
654      * support for &lt;tool&gt; nested element
655      */
656     public Tool createTool()
657     {
658         Tool p = new Tool();
659         toolAttr.addElement(p);
660         return p;
661     }
662 
663     public class Tool
664     {
665         private String name = null;
666         private String value = null;
667 
668         public void setName(String name)
669         {
670             this.name = name;
671         }
672 
673         public String getName() throws BuildException
674         {
675             if (name == null)
676             {
677                 throw new BuildException("Name attribute is missing.");
678             }
679             return name;
680         }
681 
682         public void setValue(String value)
683         {
684             this.value = value;
685         }
686 
687         public String getValue() throws BuildException
688         {
689             if (value == null)
690             {
691                 throw new BuildException("value for attribute for " + getName() + " is missing.");
692             }
693             return value;
694         }
695     }
696 
697     /**
698      *  support for &lt;velconfig&gt; nested element
699      */
700     public VelocityConfig createVelConfig()
701     {
702         VelocityConfig p = new VelocityConfig();
703         velConfigAttr.addElement(p);
704         return p;
705     }
706 
707     public class VelocityConfig
708     {
709         private String name = null;
710         private String value = null;
711 
712         public void setName(String name)
713         {
714             this.name = name;
715         }
716 
717         public String getName() throws BuildException
718         {
719             if (name == null)
720             {
721                 throw new BuildException("Name attribute is missing.");
722             }
723             return name;
724         }
725 
726         public void setValue(String value)
727         {
728             this.value = value;
729         }
730 
731         public String getValue() throws BuildException
732         {
733             if (value == null)
734             {
735                 throw new BuildException("Value attribute is missing.");
736             }
737             return value;
738         }
739     }
740 
741     /**
742      * Set the output type to use for the transformation.  Only "xml" (the
743      * default) is guaranteed to work for all parsers.  Xalan2 also
744      * supports "html" and "text".<br>
745      * <i>Not currently implemented.</i>
746      * @param type the output method to use
747      */
748     /*
749     public void setOutputtype(String type) {
750         this.outputtype = type;
751     }
752     */
753 
754 }