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
/*
 * 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.
 */

package freemarker.ext.ant;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;

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 org.w3c.dom.Document;
import org.xml.sax.SAXParseException;

import freemarker.ext.dom.NodeModel;
import freemarker.ext.xml.NodeListModel;
import freemarker.template.Configuration;
import freemarker.template.SimpleHash;
import freemarker.template.SimpleScalar;
import freemarker.template.Template;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateNodeModel;
import freemarker.template._ObjectWrappers;
import freemarker.template.utility.ClassUtil;
import freemarker.template.utility.SecurityUtilities;


/**
 * 

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:

*
    *
  • {@code 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 {@code .node} instead, which initially contains the DOM Document wrapped with {@link freemarker.ext.dom.NodeModel}.
  • *
  • {@code properties}: a {@link freemarker.template.SimpleHash} containing * properties of the project that executes the task
  • *
  • {@code userProperties}: a {@link freemarker.template.SimpleHash} containing * user properties of the project that executes the task
  • *
  • {@code project}: the DOM tree of the XML file specified by the * {@code projectfile}. It will not be available if you didn't specify the * {@code projectfile} attribute.
  • *
  • further custom models can be instantiated and made available to the * templates using the {@code models} attribute.
  • *
*

It supports the following attributes:

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
FreeMarker XML ant task 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:

* * * * * * * * * * * * * * * * * * *
FreeMarker XML ant task nested 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 {@code file} * attribute. * The following variables are added to the Jython runtime's local * namespace before the script is invoked: *
    *
  • {@code model}: The data model as java.util.HashMap. * You can read and modify the data model with this variable. *
  • {@code doc}: The XML document as org.w3c.dom.Document. *
  • {@code 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 * {@code prepareModel} element. The accessible Jython variables are the * same as with the {@code prepareModel} element, except that there is no * {@code model} variable, but there is {@code 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
* @deprecated FMPP is a more complete solution. */ @Deprecated 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", "utf-8"); 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 {@code .} */ 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. {@code .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; } @Override 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. try (Writer writer = new BufferedWriter( new OutputStreamWriter(new FileOutputStream(outFile), encoding))) { 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(); } 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(_ObjectWrappers.SAFE_OBJECT_WRAPPER); 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