KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > velocity > texen > ant > TexenTask


1 package org.apache.velocity.texen.ant;
2
3 /*
4  * Copyright 2001,2004 The Apache Software Foundation.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19 import java.util.StringTokenizer JavaDoc;
20 import java.util.Date JavaDoc;
21 import java.util.Hashtable JavaDoc;
22 import java.util.Iterator JavaDoc;
23 import java.util.Map JavaDoc;
24
25 import java.io.File JavaDoc;
26 import java.io.Writer JavaDoc;
27 import java.io.FileInputStream JavaDoc;
28 import java.io.InputStream JavaDoc;
29 import java.io.IOException JavaDoc;
30
31 import org.apache.tools.ant.BuildException;
32 import org.apache.tools.ant.Task;
33 import org.apache.velocity.VelocityContext;
34 import org.apache.velocity.app.VelocityEngine;
35 import org.apache.velocity.context.Context;
36 import org.apache.velocity.texen.Generator;
37 import org.apache.velocity.util.StringUtils;
38 import org.apache.velocity.exception.MethodInvocationException;
39 import org.apache.velocity.exception.ParseErrorException;
40 import org.apache.velocity.exception.ResourceNotFoundException;
41 import org.apache.commons.collections.ExtendedProperties;
42
43 /**
44  * An ant task for generating output by using Velocity
45  *
46  * @author <a HREF="mailto:jvanzyl@apache.org">Jason van Zyl</a>
47  * @author <a HREF="robertdonkin@mac.com">Robert Burrell Donkin</a>
48  * @version $Id: TexenTask.java,v 1.39.4.1 2004/03/03 23:23:07 geirm Exp $
49  */

50 public class TexenTask
51     extends Task
52 {
53     /**
54      * This message fragment (telling users to consult the log or
55      * invoke ant with the -debug flag) is appended to rethrown
56      * exception messages.
57      */

58     private final static String JavaDoc ERR_MSG_FRAGMENT =
59         ". For more information consult the velocity log, or invoke ant " +
60         "with the -debug flag.";
61     
62     /**
63      * This is the control template that governs the output.
64      * It may or may not invoke the services of worker
65      * templates.
66      */

67     protected String JavaDoc controlTemplate;
68     
69     /**
70      * This is where Velocity will look for templates
71      * using the file template loader.
72      */

73     protected String JavaDoc templatePath;
74     
75     /**
76      * This is where texen will place all the output
77      * that is a product of the generation process.
78      */

79     protected String JavaDoc outputDirectory;
80     
81     /**
82      * This is the file where the generated text
83      * will be placed.
84      */

85     protected String JavaDoc outputFile;
86     
87     /**
88      * This is the encoding for the output file(s).
89      */

90     protected String JavaDoc outputEncoding;
91
92     /**
93      * This is the encoding for the input file(s)
94      * (templates).
95      */

96     protected String JavaDoc inputEncoding;
97
98     /**
99      * <p>
100      * These are properties that are fed into the
101      * initial context from a properties file. This
102      * is simply a convenient way to set some values
103      * that you wish to make available in the context.
104      * </p>
105      * <p>
106      * These values are not critical, like the template path
107      * or output path, but allow a convenient way to
108      * set a value that may be specific to a particular
109      * generation task.
110      * </p>
111      * <p>
112      * For example, if you are generating scripts to allow
113      * user to automatically create a database, then
114      * you might want the <code>$databaseName</code>
115      * to be placed
116      * in the initial context so that it is available
117      * in a script that might look something like the
118      * following:
119      * <code><pre>
120      * #!bin/sh
121      *
122      * echo y | mysqladmin create $databaseName
123      * </pre></code>
124      * The value of <code>$databaseName</code> isn't critical to
125      * output, and you obviously don't want to change
126      * the ant task to simply take a database name.
127      * So initial context values can be set with
128      * properties file.
129      */

130     protected ExtendedProperties contextProperties;
131
132     /**
133      * Property which controls whether the classpath
134      * will be used when trying to locate templates.
135      */

136     protected boolean useClasspath;
137
138     /**
139      * Path separator.
140      */

141     private String JavaDoc fileSeparator = System.getProperty("file.separator");
142
143     /**
144      * [REQUIRED] Set the control template for the
145      * generating process.
146      */

147     public void setControlTemplate (String JavaDoc controlTemplate)
148     {
149         this.controlTemplate = controlTemplate;
150     }
151
152     /**
153      * Get the control template for the
154      * generating process.
155      */

156     public String JavaDoc getControlTemplate()
157     {
158         return controlTemplate;
159     }
160
161     /**
162      * [REQUIRED] Set the path where Velocity will look
163      * for templates using the file template
164      * loader.
165      */

166     
167     public void setTemplatePath(String JavaDoc templatePath) throws Exception JavaDoc
168     {
169         StringBuffer JavaDoc resolvedPath = new StringBuffer JavaDoc();
170         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(templatePath, ",");
171         while ( st.hasMoreTokens() )
172         {
173             // resolve relative path from basedir and leave
174
// absolute path untouched.
175
File JavaDoc fullPath = project.resolveFile(st.nextToken());
176             resolvedPath.append(fullPath.getCanonicalPath());
177             if ( st.hasMoreTokens() )
178             {
179                 resolvedPath.append(",");
180             }
181         }
182         this.templatePath = resolvedPath.toString();
183         
184         System.out.println(templatePath);
185      }
186
187     /**
188      * Get the path where Velocity will look
189      * for templates using the file template
190      * loader.
191      */

192     public String JavaDoc getTemplatePath()
193     {
194         return templatePath;
195     }
196
197     /**
198      * [REQUIRED] Set the output directory. It will be
199      * created if it doesn't exist.
200      */

201     public void setOutputDirectory(File JavaDoc outputDirectory)
202     {
203         try
204         {
205             this.outputDirectory = outputDirectory.getCanonicalPath();
206         }
207         catch (java.io.IOException JavaDoc ioe)
208         {
209             throw new BuildException(ioe);
210         }
211     }
212       
213     /**
214      * Get the output directory.
215      */

216     public String JavaDoc getOutputDirectory()
217     {
218         return outputDirectory;
219     }
220
221     /**
222      * [REQUIRED] Set the output file for the
223      * generation process.
224      */

225     public void setOutputFile(String JavaDoc outputFile)
226     {
227         this.outputFile = outputFile;
228     }
229     
230     /**
231      * Set the output encoding.
232      */

233     public void setOutputEncoding(String JavaDoc outputEncoding)
234     {
235         this.outputEncoding = outputEncoding;
236     }
237
238     /**
239      * Set the input (template) encoding.
240      */

241     public void setInputEncoding(String JavaDoc inputEncoding)
242     {
243         this.inputEncoding = inputEncoding;
244     }
245
246     /**
247      * Get the output file for the
248      * generation process.
249      */

250     public String JavaDoc getOutputFile()
251     {
252         return outputFile;
253     }
254
255     /**
256      * Set the context properties that will be
257      * fed into the initial context be the
258      * generating process starts.
259      */

260     public void setContextProperties( String JavaDoc file )
261     {
262         String JavaDoc[] sources = StringUtils.split(file,",");
263         contextProperties = new ExtendedProperties();
264         
265         // Always try to get the context properties resource
266
// from a file first. Templates may be taken from a JAR
267
// file but the context properties resource may be a
268
// resource in the filesystem. If this fails than attempt
269
// to get the context properties resource from the
270
// classpath.
271
for (int i = 0; i < sources.length; i++)
272         {
273             ExtendedProperties source = new ExtendedProperties();
274             
275             try
276             {
277                 // resolve relative path from basedir and leave
278
// absolute path untouched.
279
File JavaDoc fullPath = project.resolveFile(sources[i]);
280                 log("Using contextProperties file: " + fullPath);
281                 source.load(new FileInputStream JavaDoc(fullPath));
282             }
283             catch (Exception JavaDoc e)
284             {
285                 ClassLoader JavaDoc classLoader = this.getClass().getClassLoader();
286             
287                 try
288                 {
289                     InputStream JavaDoc inputStream = classLoader.getResourceAsStream(sources[i]);
290                 
291                     if (inputStream == null)
292                     {
293                         throw new BuildException("Context properties file " + sources[i] +
294                             " could not be found in the file system or on the classpath!");
295                     }
296                     else
297                     {
298                         source.load(inputStream);
299                     }
300                 }
301                 catch (IOException JavaDoc ioe)
302                 {
303                     source = null;
304                 }
305             }
306         
307             Iterator JavaDoc j = source.getKeys();
308             
309             while (j.hasNext())
310             {
311                 String JavaDoc name = (String JavaDoc) j.next();
312                 String JavaDoc value = source.getString(name);
313                 contextProperties.setProperty(name,value);
314             }
315         }
316     }
317
318     /**
319      * Get the context properties that will be
320      * fed into the initial context be the
321      * generating process starts.
322      */

323     public ExtendedProperties getContextProperties()
324     {
325         return contextProperties;
326     }
327     
328     /**
329      * Set the use of the classpath in locating templates
330      *
331      * @param boolean true means the classpath will be used.
332      */

333     public void setUseClasspath(boolean useClasspath)
334     {
335         this.useClasspath = useClasspath;
336     }
337     
338     /**
339      * Creates a VelocityContext.
340      *
341      * @return new Context
342      * @throws Exception the execute method will catch
343      * and rethrow as a <code>BuildException</code>
344      */

345     public Context initControlContext()
346         throws Exception JavaDoc
347     {
348         return new VelocityContext();
349     }
350     
351     /**
352      * Execute the input script with Velocity
353      *
354      * @throws BuildException
355      * BuildExceptions are thrown when required attributes are missing.
356      * Exceptions thrown by Velocity are rethrown as BuildExceptions.
357      */

358     public void execute ()
359         throws BuildException
360     {
361         // Make sure the template path is set.
362
if (templatePath == null && useClasspath == false)
363         {
364             throw new BuildException(
365                 "The template path needs to be defined if you are not using " +
366                 "the classpath for locating templates!");
367         }
368     
369         // Make sure the control template is set.
370
if (controlTemplate == null)
371         {
372             throw new BuildException("The control template needs to be defined!");
373         }
374
375         // Make sure the output directory is set.
376
if (outputDirectory == null)
377         {
378             throw new BuildException("The output directory needs to be defined!");
379         }
380         
381         // Make sure there is an output file.
382
if (outputFile == null)
383         {
384             throw new BuildException("The output file needs to be defined!");
385         }
386         
387         VelocityEngine ve = new VelocityEngine();
388         
389         try
390         {
391             // Setup the Velocity Runtime.
392
if (templatePath != null)
393             {
394                 log("Using templatePath: " + templatePath, project.MSG_VERBOSE);
395                 ve.setProperty(
396                     ve.FILE_RESOURCE_LOADER_PATH, templatePath);
397             }
398             
399             if (useClasspath)
400             {
401                 log("Using classpath");
402                 ve.addProperty(
403                     VelocityEngine.RESOURCE_LOADER, "classpath");
404             
405                 ve.setProperty(
406                     "classpath." + VelocityEngine.RESOURCE_LOADER + ".class",
407                         "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
408
409                 ve.setProperty(
410                     "classpath." + VelocityEngine.RESOURCE_LOADER +
411                         ".cache", "false");
412
413                 ve.setProperty(
414                     "classpath." + VelocityEngine.RESOURCE_LOADER +
415                         ".modificationCheckInterval", "2");
416             }
417             
418             ve.init();
419
420             // Create the text generator.
421
Generator generator = Generator.getInstance();
422             generator.setVelocityEngine(ve);
423             generator.setOutputPath(outputDirectory);
424             generator.setInputEncoding(inputEncoding);
425             generator.setOutputEncoding(outputEncoding);
426
427             if (templatePath != null)
428             {
429                 generator.setTemplatePath(templatePath);
430             }
431             
432             // Make sure the output directory exists, if it doesn't
433
// then create it.
434
File JavaDoc file = new File JavaDoc(outputDirectory);
435             if (! file.exists())
436             {
437                 file.mkdirs();
438             }
439             
440             String JavaDoc path = outputDirectory + File.separator + outputFile;
441             log("Generating to file " + path, project.MSG_INFO);
442             Writer JavaDoc writer = generator.getWriter(path, outputEncoding);
443             
444             // The generator and the output path should
445
// be placed in the init context here and
446
// not in the generator class itself.
447
Context c = initControlContext();
448             
449             // Everything in the generator class should be
450
// pulled out and placed in here. What the generator
451
// class does can probably be added to the Velocity
452
// class and the generator class can probably
453
// be removed all together.
454
populateInitialContext(c);
455             
456             // Feed all the options into the initial
457
// control context so they are available
458
// in the control/worker templates.
459
if (contextProperties != null)
460             {
461                 Iterator JavaDoc i = contextProperties.getKeys();
462         
463                 while (i.hasNext())
464                 {
465                     String JavaDoc property = (String JavaDoc) i.next();
466                     String JavaDoc value = contextProperties.getString(property);
467                     
468                     // Now lets quickly check to see if what
469
// we have is numeric and try to put it
470
// into the context as an Integer.
471
try
472                     {
473                         c.put(property, new Integer JavaDoc(value));
474                     }
475                     catch (NumberFormatException JavaDoc nfe)
476                     {
477                         // Now we will try to place the value into
478
// the context as a boolean value if it
479
// maps to a valid boolean value.
480
String JavaDoc booleanString =
481                             contextProperties.testBoolean(value);
482                         
483                         if (booleanString != null)
484                         {
485                             c.put(property, new Boolean JavaDoc(booleanString));
486                         }
487                         else
488                         {
489                             // We are going to do something special
490
// for properties that have a "file.contents"
491
// suffix: for these properties will pull
492
// in the contents of the file and make
493
// them available in the context. So for
494
// a line like the following in a properties file:
495
//
496
// license.file.contents = license.txt
497
//
498
// We will pull in the contents of license.txt
499
// and make it available in the context as
500
// $license. This should make texen a little
501
// more flexible.
502
if (property.endsWith("file.contents"))
503                             {
504                                 // We need to turn the license file from relative to
505
// absolute, and let Ant help :)
506
value = StringUtils.fileContentsToString(
507                                     project.resolveFile(value).getCanonicalPath());
508                             
509                                 property = property.substring(
510                                     0, property.indexOf("file.contents") - 1);
511                             }
512                         
513                             c.put(property, value);
514                         }
515                     }
516                 }
517             }
518             
519             writer.write(generator.parse(controlTemplate, c));
520             writer.flush();
521             writer.close();
522             generator.shutdown();
523             cleanup();
524         }
525         catch( BuildException e)
526         {
527             throw e;
528         }
529         catch( MethodInvocationException e )
530         {
531             throw new BuildException(
532                 "Exception thrown by '" + e.getReferenceName() + "." +
533                     e.getMethodName() +"'" + ERR_MSG_FRAGMENT,
534                         e.getWrappedThrowable());
535         }
536         catch( ParseErrorException e )
537         {
538             throw new BuildException("Velocity syntax error" + ERR_MSG_FRAGMENT ,e);
539         }
540         catch( ResourceNotFoundException e )
541         {
542             throw new BuildException("Resource not found" + ERR_MSG_FRAGMENT,e);
543         }
544         catch( Exception JavaDoc e )
545         {
546             throw new BuildException("Generation failed" + ERR_MSG_FRAGMENT ,e);
547         }
548     }
549
550     /**
551      * <p>Place useful objects into the initial context.</p>
552      *
553      * <p>TexenTask places <code>Date().toString()</code> into the
554      * context as <code>$now</code>. Subclasses who want to vary the
555      * objects in the context should override this method.</p>
556      *
557      * <p><code>$generator</code> is not put into the context in this
558      * method.</p>
559      *
560      * @param context The context to populate, as retrieved from
561      * {@link #initControlContext()}.
562      *
563      * @throws Exception Error while populating context. The {@link
564      * #execute()} method will catch and rethrow as a
565      * <code>BuildException</code>.
566      */

567     protected void populateInitialContext(Context context)
568         throws Exception JavaDoc
569     {
570         context.put("now", new Date JavaDoc().toString());
571     }
572
573     /**
574      * A hook method called at the end of {@link #execute()} which can
575      * be overridden to perform any necessary cleanup activities (such
576      * as the release of database connections, etc.). By default,
577      * does nothing.
578      *
579      * @exception Exception Problem cleaning up.
580      */

581     protected void cleanup()
582         throws Exception JavaDoc
583     {
584     }
585 }
586
Popular Tags