All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.velocity.texen.ant.TexenTask Maven / Gradle / Ivy

There is a newer version: 1.7
Show newest version
package org.apache.velocity.texen.ant;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.    
 */

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.Date;
import java.util.Iterator;
import java.util.StringTokenizer;

import org.apache.commons.collections.ExtendedProperties;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.context.Context;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.texen.Generator;
import org.apache.velocity.util.StringUtils;

/**
 * An ant task for generating output by using Velocity
 *
 * @author Jason van Zyl
 * @author Robert Burrell Donkin
 * @version $Id: TexenTask.java 463298 2006-10-12 16:10:32Z henning $
 */
public class TexenTask
    extends Task
{
    /**
     * This message fragment (telling users to consult the log or
     * invoke ant with the -debug flag) is appended to rethrown
     * exception messages.
     */
    private final static String ERR_MSG_FRAGMENT =
        ". For more information consult the velocity log, or invoke ant " +
        "with the -debug flag.";

    /**
     * This is the control template that governs the output.
     * It may or may not invoke the services of worker
     * templates.
     */
    protected String controlTemplate;

    /**
     * This is where Velocity will look for templates
     * using the file template loader.
     */
    protected String templatePath;

    /**
     * This is where texen will place all the output
     * that is a product of the generation process.
     */
    protected String outputDirectory;

    /**
     * This is the file where the generated text
     * will be placed.
     */
    protected String outputFile;

    /**
     * This is the encoding for the output file(s).
     */
    protected String outputEncoding;

    /**
     * This is the encoding for the input file(s)
     * (templates).
     */
    protected String inputEncoding;

    /**
     * 

* These are properties that are fed into the * initial context from a properties file. This * is simply a convenient way to set some values * that you wish to make available in the context. *

*

* These values are not critical, like the template path * or output path, but allow a convenient way to * set a value that may be specific to a particular * generation task. *

*

* For example, if you are generating scripts to allow * user to automatically create a database, then * you might want the $databaseName * to be placed * in the initial context so that it is available * in a script that might look something like the * following: *

     * #!bin/sh
     *
     * echo y | mysqladmin create $databaseName
     * 
* The value of $databaseName isn't critical to * output, and you obviously don't want to change * the ant task to simply take a database name. * So initial context values can be set with * properties file. */ protected ExtendedProperties contextProperties; /** * Property which controls whether the classpath * will be used when trying to locate templates. */ protected boolean useClasspath; /** * The LogFile (incl. path) to log to. */ protected String logFile; /** * Property which controls whether the resource * loader will be told to cache. Default false */ protected String useResourceLoaderCache = "false"; /** * */ protected String resourceLoaderModificationCheckInterval = "2"; /** * [REQUIRED] Set the control template for the * generating process. * @param controlTemplate */ public void setControlTemplate (String controlTemplate) { this.controlTemplate = controlTemplate; } /** * Get the control template for the * generating process. * @return The current control template. */ public String getControlTemplate() { return controlTemplate; } /** * [REQUIRED] Set the path where Velocity will look * for templates using the file template * loader. * @param templatePath * @throws Exception */ public void setTemplatePath(String templatePath) throws Exception { StringBuffer resolvedPath = new StringBuffer(); StringTokenizer st = new StringTokenizer(templatePath, ","); while ( st.hasMoreTokens() ) { // resolve relative path from basedir and leave // absolute path untouched. File fullPath = project.resolveFile(st.nextToken()); resolvedPath.append(fullPath.getCanonicalPath()); if ( st.hasMoreTokens() ) { resolvedPath.append(","); } } this.templatePath = resolvedPath.toString(); System.out.println(templatePath); } /** * Get the path where Velocity will look * for templates using the file template * loader. * @return The template path. */ public String getTemplatePath() { return templatePath; } /** * [REQUIRED] Set the output directory. It will be * created if it doesn't exist. * @param outputDirectory */ public void setOutputDirectory(File outputDirectory) { try { this.outputDirectory = outputDirectory.getCanonicalPath(); } catch (java.io.IOException ioe) { throw new BuildException(ioe); } } /** * Get the output directory. * @return The output directory. */ public String getOutputDirectory() { return outputDirectory; } /** * [REQUIRED] Set the output file for the * generation process. * @param outputFile */ public void setOutputFile(String outputFile) { this.outputFile = outputFile; } /** * Set the output encoding. * @param outputEncoding */ public void setOutputEncoding(String outputEncoding) { this.outputEncoding = outputEncoding; } /** * Set the input (template) encoding. * @param inputEncoding */ public void setInputEncoding(String inputEncoding) { this.inputEncoding = inputEncoding; } /** * Get the output file for the * generation process. * @return The output file. */ public String getOutputFile() { return outputFile; } /** * Sets the log file. * @param log */ public void setLogFile(String log) { this.logFile = log; } /** * Gets the log file. * @return The log file. */ public String getLogFile() { return this.logFile; } /** * Set the context properties that will be * fed into the initial context be the * generating process starts. * @param file */ public void setContextProperties( String file ) { String[] sources = StringUtils.split(file,","); contextProperties = new ExtendedProperties(); // Always try to get the context properties resource // from a file first. Templates may be taken from a JAR // file but the context properties resource may be a // resource in the filesystem. If this fails than attempt // to get the context properties resource from the // classpath. for (int i = 0; i < sources.length; i++) { ExtendedProperties source = new ExtendedProperties(); try { // resolve relative path from basedir and leave // absolute path untouched. File fullPath = project.resolveFile(sources[i]); log("Using contextProperties file: " + fullPath); source.load(new FileInputStream(fullPath)); } catch (IOException e) { ClassLoader classLoader = this.getClass().getClassLoader(); try { InputStream inputStream = classLoader.getResourceAsStream(sources[i]); if (inputStream == null) { throw new BuildException("Context properties file " + sources[i] + " could not be found in the file system or on the classpath!"); } else { source.load(inputStream); } } catch (IOException ioe) { source = null; } } if (source != null) { for (Iterator j = source.getKeys(); j.hasNext(); ) { String name = (String) j.next(); String value = StringUtils.nullTrim(source.getString(name)); contextProperties.setProperty(name,value); } } } } /** * Get the context properties that will be * fed into the initial context be the * generating process starts. * @return The current context properties. */ public ExtendedProperties getContextProperties() { return contextProperties; } /** * Set the use of the classpath in locating templates * * @param useClasspath true means the classpath will be used. */ public void setUseClasspath(boolean useClasspath) { this.useClasspath = useClasspath; } /** * @param useResourceLoaderCache */ public void setUseResourceLoaderCache(String useResourceLoaderCache) { this.useResourceLoaderCache = useResourceLoaderCache; } /** * @param resourceLoaderModificationCheckInterval */ public void setResourceLoaderModificationCheckInterval(String resourceLoaderModificationCheckInterval) { this.resourceLoaderModificationCheckInterval = resourceLoaderModificationCheckInterval; } /** * Creates a VelocityContext. * * @return new Context * @throws Exception the execute method will catch * and rethrow as a BuildException */ public Context initControlContext() throws Exception { return new VelocityContext(); } /** * Execute the input script with Velocity * * @throws BuildException * BuildExceptions are thrown when required attributes are missing. * Exceptions thrown by Velocity are rethrown as BuildExceptions. */ public void execute () throws BuildException { // Make sure the template path is set. if (templatePath == null && useClasspath == false) { throw new BuildException( "The template path needs to be defined if you are not using " + "the classpath for locating templates!"); } // Make sure the control template is set. if (controlTemplate == null) { throw new BuildException("The control template needs to be defined!"); } // Make sure the output directory is set. if (outputDirectory == null) { throw new BuildException("The output directory needs to be defined!"); } // Make sure there is an output file. if (outputFile == null) { throw new BuildException("The output file needs to be defined!"); } VelocityEngine ve = new VelocityEngine(); try { // Setup the Velocity Runtime. if (templatePath != null) { log("Using templatePath: " + templatePath, Project.MSG_VERBOSE); ve.setProperty( RuntimeConstants.FILE_RESOURCE_LOADER_PATH, templatePath); } if (useClasspath) { log("Using classpath"); ve.addProperty( VelocityEngine.RESOURCE_LOADER, "classpath"); ve.setProperty( "classpath." + VelocityEngine.RESOURCE_LOADER + ".class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); ve.setProperty( "classpath." + VelocityEngine.RESOURCE_LOADER + ".cache", useResourceLoaderCache); ve.setProperty( "classpath." + VelocityEngine.RESOURCE_LOADER + ".modificationCheckInterval", resourceLoaderModificationCheckInterval); } if (this.logFile != null) { ve.setProperty(RuntimeConstants.RUNTIME_LOG, this.logFile); } ve.init(); // Create the text generator. Generator generator = Generator.getInstance(); generator.setVelocityEngine(ve); generator.setOutputPath(outputDirectory); generator.setInputEncoding(inputEncoding); generator.setOutputEncoding(outputEncoding); if (templatePath != null) { generator.setTemplatePath(templatePath); } // Make sure the output directory exists, if it doesn't // then create it. File file = new File(outputDirectory); if (! file.exists()) { file.mkdirs(); } String path = outputDirectory + File.separator + outputFile; log("Generating to file " + path, Project.MSG_INFO); Writer writer = generator.getWriter(path, outputEncoding); // The generator and the output path should // be placed in the init context here and // not in the generator class itself. Context c = initControlContext(); // Everything in the generator class should be // pulled out and placed in here. What the generator // class does can probably be added to the Velocity // class and the generator class can probably // be removed all together. populateInitialContext(c); // Feed all the options into the initial // control context so they are available // in the control/worker templates. if (contextProperties != null) { Iterator i = contextProperties.getKeys(); while (i.hasNext()) { String property = (String) i.next(); String value = StringUtils.nullTrim(contextProperties.getString(property)); // Now lets quickly check to see if what // we have is numeric and try to put it // into the context as an Integer. try { c.put(property, new Integer(value)); } catch (NumberFormatException nfe) { // Now we will try to place the value into // the context as a boolean value if it // maps to a valid boolean value. String booleanString = contextProperties.testBoolean(value); if (booleanString != null) { c.put(property, Boolean.valueOf(booleanString)); } else { // We are going to do something special // for properties that have a "file.contents" // suffix: for these properties will pull // in the contents of the file and make // them available in the context. So for // a line like the following in a properties file: // // license.file.contents = license.txt // // We will pull in the contents of license.txt // and make it available in the context as // $license. This should make texen a little // more flexible. if (property.endsWith("file.contents")) { // We need to turn the license file from relative to // absolute, and let Ant help :) value = StringUtils.fileContentsToString( project.resolveFile(value).getCanonicalPath()); property = property.substring( 0, property.indexOf("file.contents") - 1); } c.put(property, value); } } } } writer.write(generator.parse(controlTemplate, c)); writer.flush(); writer.close(); generator.shutdown(); cleanup(); } catch( BuildException e) { throw e; } catch( MethodInvocationException e ) { throw new BuildException( "Exception thrown by '" + e.getReferenceName() + "." + e.getMethodName() +"'" + ERR_MSG_FRAGMENT, e.getWrappedThrowable()); } catch( ParseErrorException e ) { throw new BuildException("Velocity syntax error" + ERR_MSG_FRAGMENT ,e); } catch( ResourceNotFoundException e ) { throw new BuildException("Resource not found" + ERR_MSG_FRAGMENT,e); } catch( Exception e ) { throw new BuildException("Generation failed" + ERR_MSG_FRAGMENT ,e); } } /** *

Place useful objects into the initial context.

* *

TexenTask places Date().toString() into the * context as $now. Subclasses who want to vary the * objects in the context should override this method.

* *

$generator is not put into the context in this * method.

* * @param context The context to populate, as retrieved from * {@link #initControlContext()}. * * @throws Exception Error while populating context. The {@link * #execute()} method will catch and rethrow as a * BuildException. */ protected void populateInitialContext(Context context) throws Exception { context.put("now", new Date().toString()); } /** * A hook method called at the end of {@link #execute()} which can * be overridden to perform any necessary cleanup activities (such * as the release of database connections, etc.). By default, * does nothing. * * @exception Exception Problem cleaning up. */ protected void cleanup() throws Exception { } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy