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

freemarker.ext.ant.FreemarkerXmlTask Maven / Gradle / Ivy

There is a newer version: 2.3.32_1
Show newest version
/*
 * Copyright (c) 2003 The Visigoth Software Society. All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowledgement:
 *       "This product includes software developed by the
 *        Visigoth Software Society (http://www.visigoths.org/)."
 *    Alternately, this acknowledgement may appear in the software itself,
 *    if and wherever such third-party acknowledgements normally appear.
 *
 * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the 
 *    project contributors may be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact [email protected].
 *
 * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
 *    nor may "FreeMarker" or "Visigoth" appear in their names
 *    without prior written permission of the Visigoth Software Society.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Visigoth Software Society. For more
 * information on the Visigoth Software Society, please see
 * http://www.visigoths.org/
 */

package freemarker.ext.ant;

import java.io.*;
import java.util.*;

import org.w3c.dom.*;
import org.xml.sax.SAXParseException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.MatchingTask;
import freemarker.ext.xml.NodeListModel;
import freemarker.ext.dom.NodeModel;
import freemarker.template.utility.ClassUtil;
import freemarker.template.utility.SecurityUtilities;
import freemarker.template.*;


/**
 * 

This is an Ant task for transforming * XML documents using FreeMarker templates. It uses the adapter class * {@link NodeListModel}. It will read a set of XML documents, and pass them to * the template for processing, building the corresponding output files in the * destination directory.

*

It makes the following variables available to the template in the data model:

*
    *
  • document: Deprecated! The DOM tree of the currently processed XML file wrapped with the legacy {@link freemarker.ext.xml.NodeListModel}. For new projects you should use the .node instead, which initially contains the DOM Document wrapped with {@link freemarker.ext.dom.NodeModel}.
  • *
  • properties: a {@link freemarker.template.SimpleHash} containing * properties of the project that executes the task
  • *
  • userProperties: a {@link freemarker.template.SimpleHash} containing * user properties of the project that executes the task
  • *
  • project: the DOM tree of the XML file specified by the * projectfile. It will not be available if you didn't specify the * projectfile attribute.
  • *
  • further custom models can be instantiated and made available to the * templates using the models attribute.
  • *
*

It supports the following attributes:

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
AttributeDescriptionRequired
basedirlocation of the XML files. Defaults to the project's * basedir.No
destdirlocation to store the generated files.Yes
includescomma-separated list of patterns of files that must be * included; all files are included when omitted.No
includesfilethe name of a file that contains * include patterns.No
excludescomma-separated list of patterns of files that must be * excluded; no files (except default excludes) are excluded when omitted.No
excludesfilethe name of a file that contains * exclude patterns.No
defaultexcludesindicates whether default excludes should be used * (yes | no); default excludes are used when omitted.No
extensionextension of generated files. Defaults to .html.No
templatename of the FreeMarker template file that will be * applied by default to XML filesNo
templateDirlocation of the FreeMarker template(s) to be used, defaults * to the project's baseDirNo
projectfilepath to the project file. The poject file must be an XML file. * If omitted, it will not be available to templates No
incrementalindicates whether all files should be regenerated (no), or * only those that are older than the XML file, the template file, or the * project file (yes). Defaults to yes. No
encodingThe encoding of the output files. Defaults to platform * default encoding.No
templateEncodingThe encoding of the template files. Defaults to platform * default encoding.No
validationWhether to validate the XML input. Defaults to off.No
modelsA list of [name=]className pairs separated by spaces, * commas, or semicolons that specifies further models that should be * available to templates. If name is omitted, the unqualified class name * is used as the name. Every class that is specified must implement the * TemplateModel interface and have a no-args constructor.No
* *

It supports the following nesed elements:

* * * * * * * * * * * * * * * * *
ElementDescriptionRequired
prepareModel * This element executes Jython script before the processing of each XML * files, that you can use to modify the data model. * You either enter the Jython script directly nested into this * element, or specify a Jython script file with the file * attribute. * The following variables are added to the Jython runtime's local * namespace before the script is invoked: *
    *
  • model: The data model as java.util.HashMap. * You can read and modify the data model with this variable. *
  • doc: The XML document as org.w3c.dom.Document. *
  • project: The project document (if used) as * org.w3c.dom.Document. *
* If this element is used, Jython classes (tried with Jython 2.1) * must be available. *
No
prepareEnvironmentThis element executes Jython script before the processing * of each XML files, that you can use to modify the freemarker environment * ({@link freemarker.core.Environment}). The script is executed after the * prepareModel element. The accessible Jython variables are the * same as with the prepareModel element, except that there is no * model variable, but there is env variable, which is * the FreeMarker environment ({@link freemarker.core.Environment}). * If this element is used, Jython classes (tried with Jython 2.1) * must be available. * No
* * @author Attila Szegedi * @author Jonathan Revusky, [email protected] * @deprecated FMPP is a more complete solution. * @version $Id: FreemarkerXmlTask.java,v 1.58.2.1 2006/04/26 11:07:58 revusky Exp $ */ public class FreemarkerXmlTask extends MatchingTask { private JythonAntTask prepareModel; private JythonAntTask prepareEnvironment; private final DocumentBuilderFactory builderFactory; private DocumentBuilder builder; /** the {@link Configuration} used by this task. */ private Configuration cfg = new Configuration(); /** the destination directory */ private File destDir; /** the base directory */ private File baseDir; //Where the templates live private File templateDir; /** the template= attribute */ private String templateName; /** The template in its parsed form */ private Template parsedTemplate; /** last modified of the template sheet */ private long templateFileLastModified = 0; /** the projectFile= attribute */ private String projectAttribute = null; private File projectFile = null; /** The DOM tree of the project wrapped into FreeMarker TemplateModel */ private TemplateModel projectTemplate; // The DOM tree wrapped using the freemarker.ext.dom wrapping. private TemplateNodeModel projectNode; private TemplateModel propertiesTemplate; private TemplateModel userPropertiesTemplate; /** last modified of the project file if it exists */ private long projectFileLastModified = 0; /** check the last modified date on files. defaults to true */ private boolean incremental = true; /** the default output extension is .html */ private String extension = ".html"; private String encoding = SecurityUtilities.getSystemProperty("file.encoding"); private String templateEncoding = encoding; private boolean validation = false; private String models = ""; private final Map modelsMap = new HashMap(); /** * Constructor creates the SAXBuilder. */ public FreemarkerXmlTask() { builderFactory = DocumentBuilderFactory.newInstance(); builderFactory.setNamespaceAware(true); } /** * Set the base directory. Defaults to . */ public void setBasedir(File dir) { baseDir = dir; } /** * Set the destination directory into which the generated * files should be copied to * @param dir the name of the destination directory */ public void setDestdir(File dir) { destDir = dir; } /** * Set the output file extension. .html by default. */ public void setExtension(String extension) { this.extension = extension; } public void setTemplate(String templateName) { this.templateName = templateName; } public void setTemplateDir(File templateDir) throws BuildException { this.templateDir = templateDir; try { cfg.setDirectoryForTemplateLoading(templateDir); } catch (Exception e) { throw new BuildException(e); } } /** * Set the path to the project XML file */ public void setProjectfile(String projectAttribute) { this.projectAttribute = projectAttribute; } /** * Turn on/off incremental processing. On by default */ public void setIncremental(String incremental) { this.incremental = !(incremental.equalsIgnoreCase("false") || incremental.equalsIgnoreCase("no") || incremental.equalsIgnoreCase("off")); } /** * Set encoding for generated files. Defaults to platform default encoding. */ public void setEncoding(String encoding) { this.encoding = encoding; } public void setTemplateEncoding(String inputEncoding) { this.templateEncoding = inputEncoding; } /** * Sets whether to validate the XML input. */ public void setValidation(boolean validation) { this.validation = validation; } public void setModels(String models) { this.models = models; } public void execute() throws BuildException { DirectoryScanner scanner; String[] list; if (baseDir == null) { baseDir = getProject().getBaseDir(); } if (destDir == null ) { String msg = "destdir attribute must be set!"; throw new BuildException(msg, getLocation()); } File templateFile = null; if (templateDir == null) { if (templateName != null) { templateFile = new File(templateName); if (!templateFile.isAbsolute()) { templateFile = new File(getProject().getBaseDir(), templateName); } templateDir = templateFile.getParentFile(); templateName = templateFile.getName(); } else { templateDir = baseDir; } setTemplateDir(templateDir); } else if (templateName != null) { if (new File(templateName).isAbsolute()) { throw new BuildException("Do not specify an absolute location for the template as well as a templateDir"); } templateFile = new File(templateDir, templateName); } if (templateFile != null) { templateFileLastModified = templateFile.lastModified(); } try { if (templateName != null) { parsedTemplate = cfg.getTemplate(templateName, templateEncoding); } } catch (IOException ioe) { throw new BuildException(ioe.toString()); } // get the last modification of the template log("Transforming into: " + destDir.getAbsolutePath(), Project.MSG_INFO); // projectFile relative to baseDir if (projectAttribute != null && projectAttribute.length() > 0) { projectFile = new File(baseDir, projectAttribute); if (projectFile.isFile()) projectFileLastModified = projectFile.lastModified(); else { log ("Project file is defined, but could not be located: " + projectFile.getAbsolutePath(), Project.MSG_INFO ); projectFile = null; } } generateModels(); // find the files/directories scanner = getDirectoryScanner(baseDir); propertiesTemplate = wrapMap(project.getProperties()); userPropertiesTemplate = wrapMap(project.getUserProperties()); builderFactory.setValidating(validation); try { builder = builderFactory.newDocumentBuilder(); } catch(ParserConfigurationException e) { throw new BuildException("Could not create document builder", e, getLocation()); } // get a list of files to work on list = scanner.getIncludedFiles(); for (int i = 0;i < list.length; ++i) { process(baseDir, list[i], destDir); } } public void addConfiguredJython(JythonAntTask jythonAntTask) { this.prepareEnvironment = jythonAntTask; } public void addConfiguredPrepareModel(JythonAntTask prepareModel) { this.prepareModel = prepareModel; } public void addConfiguredPrepareEnvironment(JythonAntTask prepareEnvironment) { this.prepareEnvironment = prepareEnvironment; } /** * Process an XML file using FreeMarker */ private void process(File baseDir, String xmlFile, File destDir) throws BuildException { File outFile=null; File inFile=null; try { // the current input file relative to the baseDir inFile = new File(baseDir,xmlFile); // the output file relative to basedir outFile = new File(destDir, xmlFile.substring(0, xmlFile.lastIndexOf('.')) + extension); // only process files that have changed if (!incremental || (inFile.lastModified() > outFile.lastModified() || templateFileLastModified > outFile.lastModified() || projectFileLastModified > outFile.lastModified())) { ensureDirectoryFor(outFile); //-- command line status log("Input: " + xmlFile, Project.MSG_INFO ); if (projectTemplate == null && projectFile != null) { Document doc = builder.parse(projectFile); projectTemplate = new NodeListModel(builder.parse(projectFile)); projectNode = NodeModel.wrap(doc); } // Build the file DOM Document docNode = builder.parse(inFile); TemplateModel document = new NodeListModel(docNode); TemplateNodeModel docNodeModel = NodeModel.wrap(docNode); HashMap root = new HashMap(); root.put("document", document); insertDefaults(root); // Process the template and write out // the result as the outFile. Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), encoding)); try { if (parsedTemplate == null) { throw new BuildException("No template file specified in build script or in XML file"); } if (prepareModel != null) { Map vars = new HashMap(); vars.put("model", root); vars.put("doc", docNode); if (projectNode != null) { vars.put("project", ((NodeModel) projectNode).getNode()); } prepareModel.execute(vars); } freemarker.core.Environment env = parsedTemplate.createProcessingEnvironment(root, writer); env.setCurrentVisitorNode(docNodeModel); if (prepareEnvironment != null) { Map vars = new HashMap(); vars.put("env", env); vars.put("doc", docNode); if (projectNode != null) { vars.put("project", ((NodeModel) projectNode).getNode()); } prepareEnvironment.execute(vars); } env.process(); writer.flush(); } finally { writer.close(); } log("Output: " + outFile, Project.MSG_INFO ); } } catch (SAXParseException spe) { Throwable rootCause = spe; if (spe.getException() != null) rootCause = spe.getException(); log("XML parsing error in " + inFile.getAbsolutePath(), Project.MSG_ERR); log("Line number " + spe.getLineNumber()); log("Column number " + spe.getColumnNumber()); throw new BuildException(rootCause, getLocation()); } catch (Throwable e) { if (outFile != null ) { if(!outFile.delete() && outFile.exists()) { log("Failed to delete " + outFile, Project.MSG_WARN); } } e.printStackTrace(); throw new BuildException(e, getLocation()); } } private void generateModels() { StringTokenizer modelTokenizer = new StringTokenizer(models, ",; "); while(modelTokenizer.hasMoreTokens()) { String modelSpec = modelTokenizer.nextToken(); String name = null; String clazz = null; int sep = modelSpec.indexOf('='); if(sep == -1) { // No explicit name - use unqualified class name clazz = modelSpec; int dot = clazz.lastIndexOf('.'); if(dot == -1) { // clazz in the default package name = clazz; } else { name = clazz.substring(dot + 1); } } else { name = modelSpec.substring(0, sep); clazz = modelSpec.substring(sep + 1); } try { modelsMap.put(name, ClassUtil.forName(clazz).newInstance()); } catch(Exception e) { throw new BuildException(e); } } } /** * create directories as needed */ private void ensureDirectoryFor( File targetFile ) throws BuildException { File directory = new File( targetFile.getParent() ); if (!directory.exists()) { if (!directory.mkdirs()) { throw new BuildException("Unable to create directory: " + directory.getAbsolutePath(), getLocation()); } } } private static TemplateModel wrapMap(Map table) { SimpleHash model = new SimpleHash(); for (Iterator it = table.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry)it.next(); model.put(String.valueOf(entry.getKey()), new SimpleScalar(String.valueOf(entry.getValue()))); } return model; } protected void insertDefaults(Map root) { root.put("properties", propertiesTemplate); root.put("userProperties", userPropertiesTemplate); if (projectTemplate != null) { root.put("project", projectTemplate); root.put("project_node", projectNode); } if(modelsMap.size() > 0) { for (Iterator it = modelsMap.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); root.put(entry.getKey(), entry.getValue()); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy