EMMA Coverage Report (generated Tue May 18 22:13:27 CDT 2004)
[all classes][org.apache.velocity.texen.ant]

COVERAGE SUMMARY FOR SOURCE FILE [TexenTask.java]

nameclass, %method, %block, %line, %
TexenTask.java100% (1/1)61%  (11/18)72%  (368/510)73%  (91.3/125)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class TexenTask100% (1/1)61%  (11/18)72%  (368/510)73%  (91.3/125)
getContextProperties (): ExtendedProperties 0%   (0/1)0%   (0/3)0%   (0/1)
getControlTemplate (): String 0%   (0/1)0%   (0/3)0%   (0/1)
getOutputDirectory (): String 0%   (0/1)0%   (0/3)0%   (0/1)
getOutputFile (): String 0%   (0/1)0%   (0/3)0%   (0/1)
getTemplatePath (): String 0%   (0/1)0%   (0/3)0%   (0/1)
setInputEncoding (String): void 0%   (0/1)0%   (0/4)0%   (0/2)
setOutputEncoding (String): void 0%   (0/1)0%   (0/4)0%   (0/2)
setOutputDirectory (File): void 100% (1/1)50%  (6/12)60%  (3/5)
execute (): void 100% (1/1)70%  (210/300)71%  (47.1/66)
setContextProperties (String): void 100% (1/1)81%  (83/102)87%  (20/23)
setTemplatePath (String): void 100% (1/1)90%  (36/40)92%  (9.2/10)
TexenTask (): void 100% (1/1)100% (7/7)100% (2/2)
cleanup (): void 100% (1/1)100% (1/1)100% (1/1)
initControlContext (): Context 100% (1/1)100% (4/4)100% (1/1)
populateInitialContext (Context): void 100% (1/1)100% (9/9)100% (2/2)
setControlTemplate (String): void 100% (1/1)100% (4/4)100% (2/2)
setOutputFile (String): void 100% (1/1)100% (4/4)100% (2/2)
setUseClasspath (boolean): void 100% (1/1)100% (4/4)100% (2/2)

1package 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 
19import java.util.StringTokenizer;
20import java.util.Date;
21import java.util.Hashtable;
22import java.util.Iterator;
23import java.util.Map;
24 
25import java.io.File;
26import java.io.Writer;
27import java.io.FileInputStream;
28import java.io.InputStream;
29import java.io.IOException;
30 
31import org.apache.tools.ant.BuildException;
32import org.apache.tools.ant.Task;
33import org.apache.velocity.VelocityContext;
34import org.apache.velocity.app.VelocityEngine;
35import org.apache.velocity.context.Context;
36import org.apache.velocity.texen.Generator;
37import org.apache.velocity.util.StringUtils;
38import org.apache.velocity.exception.MethodInvocationException;
39import org.apache.velocity.exception.ParseErrorException;
40import org.apache.velocity.exception.ResourceNotFoundException;
41import 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 */
50public 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 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 controlTemplate;
68    
69    /**
70     * This is where Velocity will look for templates
71     * using the file template loader.
72     */
73    protected String 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 outputDirectory;
80    
81    /**
82     * This is the file where the generated text
83     * will be placed.
84     */
85    protected String outputFile;
86    
87    /**
88     * This is the encoding for the output file(s).
89     */
90    protected String outputEncoding;
91 
92    /**
93     * This is the encoding for the input file(s)
94     * (templates).
95     */
96    protected String 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 fileSeparator = System.getProperty("file.separator");
142 
143    /**
144     * [REQUIRED] Set the control template for the
145     * generating process.
146     */
147    public void setControlTemplate (String controlTemplate)
148    {
149        this.controlTemplate = controlTemplate;
150    }
151 
152    /**
153     * Get the control template for the
154     * generating process.
155     */
156    public String 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 templatePath) throws Exception
168    {
169        StringBuffer resolvedPath = new StringBuffer();
170        StringTokenizer st = new StringTokenizer(templatePath, ",");
171        while ( st.hasMoreTokens() )
172        {
173            // resolve relative path from basedir and leave
174            // absolute path untouched.
175            File 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 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 outputDirectory)
202    {
203        try
204        {
205            this.outputDirectory = outputDirectory.getCanonicalPath();
206        }
207        catch (java.io.IOException ioe)
208        {
209            throw new BuildException(ioe);
210        }
211    }
212      
213    /**
214     * Get the output directory.
215     */
216    public String 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 outputFile)
226    {
227        this.outputFile = outputFile;
228    }
229    
230    /**
231     * Set the output encoding.
232     */
233    public void setOutputEncoding(String outputEncoding)
234    {
235        this.outputEncoding = outputEncoding;
236    }
237 
238    /**
239     * Set the input (template) encoding.
240     */
241    public void setInputEncoding(String inputEncoding)
242    {
243        this.inputEncoding = inputEncoding;
244    }
245 
246    /**
247     * Get the output file for the
248     * generation process.
249     */
250    public String 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 file )
261    {
262        String[] 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 fullPath = project.resolveFile(sources[i]);
280                log("Using contextProperties file: " + fullPath);
281                source.load(new FileInputStream(fullPath));
282            }
283            catch (Exception e)
284            {
285                ClassLoader classLoader = this.getClass().getClassLoader();
286            
287                try
288                {
289                    InputStream 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 ioe)
302                {
303                    source = null;
304                }
305            }
306        
307            Iterator j = source.getKeys();
308            
309            while (j.hasNext())
310            {
311                String name = (String) j.next();
312                String 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
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 file = new File(outputDirectory);
435            if (! file.exists())
436            {
437                file.mkdirs();
438            }
439            
440            String path = outputDirectory + File.separator + outputFile;
441            log("Generating to file " + path, project.MSG_INFO);
442            Writer 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 i = contextProperties.getKeys();
462        
463                while (i.hasNext())
464                {
465                    String property = (String) i.next();
466                    String 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(value)); 
474                    }
475                    catch (NumberFormatException 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 booleanString = 
481                            contextProperties.testBoolean(value);
482                        
483                        if (booleanString != null)
484                        {    
485                            c.put(property, new Boolean(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 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
569    {
570        context.put("now", new Date().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
583    {
584    }
585}

[all classes][org.apache.velocity.texen.ant]
EMMA 2.0.4015 (stable) (C) Vladimir Roubtsov