org.nuiton.eugene.GeneratorTask Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ant-eugene-task Show documentation
Show all versions of ant-eugene-task Show documentation
Ant task to use the eugene library
The newest version!
/*
* #%L
* EUGene :: Ant task
*
* $Id: GeneratorTask.java 1012 2010-11-28 11:24:27Z tchemit $
* $HeadURL: http://svn.nuiton.org/svn/eugene/tags/eugene-2.4.2/ant-eugene-task/src/main/java/org/nuiton/eugene/GeneratorTask.java $
* %%
* Copyright (C) 2006 - 2010 CodeLutin
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
package org.nuiton.eugene;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.ServiceLoader;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
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.nuiton.eugene.models.Model;
import org.nuiton.eugene.models.object.ObjectModel;
import org.nuiton.eugene.models.object.ObjectModelReader;
import org.nuiton.eugene.models.state.StateModel;
import org.nuiton.eugene.models.state.StateModelReader;
import org.nuiton.util.FileUtil;
import org.nuiton.util.Resource;
import org.nuiton.util.ZipUtil;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
/**
* Ant generator Task.
*
* Exemple d'utilisation dans ant :
*
*
* < -- Define eugene task (classpath must contains templates) -->
* <taskdef name="generator" classname="org.nuiton.eugene.GeneratorTask"
* classpath="${compile.classpath}" />
*
* <target name="generate" description="generate">
* <generator srcdir="${modelDir}" destdir="${targetgen}"
* resolver="org.nuiton.exemple.ResourceResolver"
* templates="org.nuiton.example.JavaBeanGenerator"
* properties="defaultPackage=org.nuiton,fullPackagePath=org.nuiton,extraPackages=org.nuiton" />
* </target>
*
*
* Created: 14 janv. 2004
*
* @author Benjamin Poussin Copyright Code Lutin
* @version $Revision: 1012 $
*
* Mise a jour: $Date: 2010-11-28 12:24:27 +0100 (Sun, 28 Nov 2010) $ par :
* */
public class GeneratorTask extends MatchingTask { // GeneratorTask
/** Template used in generation (comma separated). */
protected String templates;
/** Model directory. */
protected File srcDir;
/** Single model file. */
protected File srcFile;
/** Destination directory. */
protected File destDir;
/**
* Transformation to do on model (comma separated).
*
* Values are :
*
* - object (transform into object model)
*
- state (transform into state model)
*
*/
protected String transformations = "object";
/** URI Resolver. */
protected String resolver;
/**
* Additional generator properties.
*
* Values are :
*
* - fullPackagePath : full package path
*
- extraPackages : extra package path
*
- defaultPackage (extra generated model files)
*
*/
protected Properties properties = new Properties();
/** Overwrite already existing generated files. */
protected boolean overwrite;
/** Encoding. Default to UTF-8. */
protected String encoding = "UTF-8";
/** Generation directory (default to 'build') */
protected String buildDirectory = "build";
/**
* Set templates (fully-qualified-name) to use
*
* (comma-separated).
*
* @param templates template to use.
*/
public void setTemplates(String templates) {
this.templates = templates;
}
/**
* Transformation to do.
*
* Values are :
*
* - object (transform into object model)
*
- state (transform into state model)
*
*
* @param transformations transformations
*/
public void setTransformations(String transformations) {
this.transformations = transformations;
}
/**
* Permet d'ajouter des properties. exemple: toto=1,package=org.nuiton
*
* @param properties properties
*/
public void setProperties(String properties) {
String[] props = properties.split(",");
for (String prop : props) {
String[] pv = prop.split("=");
this.properties.put(pv[0], pv[1]);
}
}
/**
* Set overwrite value.
*
* @param overwrite overwrite value
*/
public void setOverwrite(boolean overwrite) {
this.overwrite = overwrite;
}
/**
* Set encoding.
*
* @param encoding encoding
*/
public void setEncoding(String encoding) {
this.encoding = encoding;
}
/**
* Set source directory.
*
* @param srcDir source directory
*/
public void setSrcdir(File srcDir) {
this.srcDir = srcDir;
}
/**
* Set source file.
*
* @param srcFile source file
*/
public void setSrcfile(File srcFile) {
this.srcFile = srcFile;
}
/**
* Set destination directory
*
* @param destDir destination directory
*/
public void setDestdir(File destDir) {
this.destDir = destDir;
}
/**
* Set build directory.
*
* @param buildDirectory build directory
*/
public void setBuilddirectory(String buildDirectory) {
this.buildDirectory = buildDirectory;
}
/**
* Set URI resolver (FQN).
*
* @param resolver uri resolver
*/
public void setResolver(String resolver) {
this.resolver = resolver;
}
@Override
public void execute() throws BuildException {
// check
if (templates == null) {
throw new BuildException("templates attribute must be set!",
getLocation());
}
if (destDir == null) {
throw new BuildException("destDir attribute must be set!",
getLocation());
}
if (!destDir.isDirectory()) {
throw new BuildException("destination directory \"" + destDir
+ "\" does not exist or is not a directory", getLocation());
}
if (srcFile == null && srcDir == null) {
throw new BuildException(
"srcFile or srcDir attribute must be set!", getLocation());
}
if (srcFile != null && !srcFile.isFile()) {
throw new BuildException("src file \"" + srcFile
+ "\" does not exist or is not a file", getLocation());
}
String[] templateGenerators = templates.split(",");
Template[] generators = new Template[templateGenerators.length];
for (int i = 0; i < templateGenerators.length; i++) {
String templateName = templateGenerators[i].trim();
try {
Template template = (Template)
Class.forName(templateName).newInstance();
generators[i] = template;
properties.put(Template.PROP_OVERWRITE, overwrite);
properties.setProperty(Template.PROP_ENCODING, encoding);
template.getConfiguration().getProperties().putAll(properties);
} catch (ClassCastException e) {
log("Generator don't inherit Template Class", e, Project.MSG_ERR);
} catch (ClassNotFoundException e) {
log("Unable to find generator " + templateName, e, Project.MSG_ERR);
} catch (InstantiationException e) {
log("Unable to instanciate template " + templateName, e, Project.MSG_ERR);
} catch (IllegalAccessException e) {
log("Unable to parse input file " + templateName, e, Project.MSG_ERR);
}
}
if (srcFile != null) {
// generate the source files
doExecute(srcFile, destDir, generators);
} else {
DirectoryScanner scanner;
scanner = getDirectoryScanner(srcDir);
// Process all the files marked for styling
String[] includedFilenames = scanner.getIncludedFiles();
List includedFiles = new ArrayList(
includedFilenames.length);
for (String includedFilename : includedFilenames) {
includedFiles.add(new File(srcDir, includedFilename));
}
doExecute(includedFiles, destDir, generators);
}
}
/**
* Equivalent to
* doExecute(new File[] { srcFile }, destDir, generators).
*
* @param srcFile file to apply generator to
* @param destDir destination directory
* @param generators generators to apply
* @throws BuildException if can't generate
*/
protected void doExecute(File srcFile, File destDir, Template[] generators)
throws BuildException {
doExecute(Collections.singletonList(srcFile), destDir, generators);
}
/**
* Execute generation on specified files.
*
* @param srcFiles files to apply generator to
* @param destDir destination directory
* @param generators generators to apply
* @throws BuildException if can't generate
* @throws BuildException if io errors while generation
*/
protected void doExecute(List srcFiles, File destDir,
Template[] generators) throws BuildException {
List modelFiles = doConvertFiles(srcFiles, destDir);
for (Template generator : generators) {
if (generator != null) {
File[] modelFilesArray =
modelFiles.toArray(new File[modelFiles.size()]);
log("Applying " + generator.getClass().getSimpleName()
+ " on " + Arrays.toString(modelFilesArray),
Project.MSG_INFO);
String[] transformationsArray = transformations.split(",");
for (String transformation : transformationsArray) {
if ("object".equals(transformation)) {
ModelReader objectModelReader =
new ObjectModelReader();
try {
ObjectModel model =
objectModelReader.read(modelFilesArray);
generator.applyTemplate(model, destDir);
} catch (IOException e) {
throw new BuildException(
"Can't apply template on object model", e);
}
}
else if ("state".equals(transformation)) {
ModelReader stateModelReader =
new StateModelReader();
try {
Model model = stateModelReader.read(modelFilesArray);
generator.applyTemplate(model, destDir);
} catch (IOException e) {
throw new BuildException(
"Can't apply template on state model", e);
}
}
}
}
}
}
/**
* Convert srcFiles and return only eugene models files.
*
* Do following convertions : - unzip archive (zipped files) - xslt
* transformation (xmi files) - do nothing on model files
*
* @param srcFiles
* @param destDir
* @return model file list
*/
protected List doConvertFiles(List srcFiles, File destDir) {
List result = new ArrayList();
// transform tranformations list
String[] transformationsArray = transformations.split(",");
for (File file : srcFiles) {
File currentFile = file;
// unzip if needed
// after loop file is xmi
if (isArchiveFile(currentFile)) {
File unzipDirectory = new File(buildDirectory, "xmi");
if (unzipDirectory.exists()) {
unzipDirectory.mkdirs();
}
// log
log("Unzip " + currentFile.getAbsolutePath() + " into "
+ unzipDirectory.getAbsolutePath(), Project.MSG_INFO);
try {
ZipUtil.uncompress(file, unzipDirectory);
} catch (IOException e) {
throw new BuildException("Error on unzip archive", e);
}
String xmiName = currentFile.getName().substring(0,
currentFile.getName().lastIndexOf('.')) + ".xmi";
currentFile = new File(unzipDirectory, xmiName);
}
// transform file if needed
// after loop file is model (object, state, ui)
if (isXmiFile(currentFile)) {
String xmiVersion = getXmiVersion(currentFile);
if (xmiVersion != null) {
// model directory
File outputDirectory = new File(buildDirectory, "models");
outputDirectory.mkdirs();
// single model name
String modelName = currentFile.getName().substring(0,
currentFile.getName().lastIndexOf('.'));
// copy .properties file
String propertyPath = currentFile.getParent();
File propertyFile = new File(propertyPath, modelName
+ ".properties");
if (propertyFile.exists()) {
File propertyOutputFile = new File(outputDirectory,
propertyFile.getName());
try {
FileUtil.copy(propertyFile, propertyOutputFile);
} catch (IOException ioe) {
log("Cannot copy .properties file", ioe,
Project.MSG_ERR);
}
}
for (String transformation : transformationsArray) {
// object
if (transformation.trim().equalsIgnoreCase("object")) {
File outputFile = new File(outputDirectory,
modelName + ".objectmodel");
if (xmiVersion.equals("1.2")) {
log("Apply XMI 1.2 to object model XSLT on "
+ currentFile.getAbsolutePath(),
Project.MSG_INFO);
executeXSLT(currentFile, outputFile,
"xmi1.2ToObjectModel.xsl");
} else if (xmiVersion.equals("2.1")) {
log("Apply XMI 2.1 to object model XSLT on "
+ currentFile.getAbsolutePath(),
Project.MSG_INFO);
executeXSLT(currentFile, outputFile,
"xmi2.1ToObjectModel.xsl");
}
// can have more than one model file
// for one xmi
result.add(outputFile);
}
// state
else if (transformation.trim()
.equalsIgnoreCase("state")) {
File outputFile = new File(outputDirectory,
modelName + ".statemodel");
if (xmiVersion.equals("1.2")) {
log("Apply XMI 1.2 to state model XSLT on "
+ currentFile.getAbsolutePath(),
Project.MSG_INFO);
executeXSLT(currentFile, outputFile,
"xmi1.2ToStateModel.xsl");
} else if (xmiVersion.equals("2.1")) {
throw new BuildException(
"State model transformation is not " +
"supported for xmi 2.1");
}
// can have more than one model file
// for one xmi
result.add(outputFile);
}
}
} else {
throw new BuildException(
"Can't get xmi version from file : "
+ currentFile.getAbsolutePath());
}
}
// others files
// we can have model files or non model file
else if (isModelFile(currentFile)) {
result.add(currentFile);
}
}
return result;
}
/**
* Test if file is an archive.
*
* @param file file to test
* @return test result
*/
protected boolean isArchiveFile(File file) {
String fileName = file.getName();
boolean result = fileName.endsWith(".zargo")
|| fileName.endsWith(".zuml");
return result;
}
/**
* Test if file is a xmi.
*
* @param file file to test
* @return test result
*/
protected boolean isXmiFile(File file) {
String fileName = file.getName();
boolean result = fileName.endsWith(".uml") || fileName.endsWith(".xmi");
return result;
}
/**
* Test if file is a model
*
* @param file file to test
* @return test result
*/
protected boolean isModelFile(File file) {
String fileName = file.getName();
boolean result = fileName.endsWith(".objectmodel")
|| fileName.endsWith(".statemodel");
return result;
}
/**
* Try to find xmi version on a file.
*
* @param xmiFile file to inspect
* @return version or null if version can't have been found
*/
protected String getXmiVersion(File xmiFile) {
String version = null;
SAXParserFactory factory = SAXParserFactory.newInstance();
try {
SAXParser parser = factory.newSAXParser();
XmiVersionHandler handler = new XmiVersionHandler();
parser.parse(xmiFile, handler);
version = handler.getVersion();
} catch (ParserConfigurationException e) {
log("Can't parse file as xmi", e, Project.MSG_DEBUG);
} catch (SAXException e) {
log("Can't parse file as xmi", e, Project.MSG_DEBUG);
} catch (IOException e) {
log("Can't parse file as xmi", e, Project.MSG_DEBUG);
}
return version;
}
/**
* Sax handler to find xmi version into xmi document.
*/
protected class XmiVersionHandler extends DefaultHandler {
public String version;
public XmiVersionHandler() {
}
public String getVersion() {
return version;
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
if (qName.equals("XMI")) {
version = attributes.getValue("xmi.version");
log("XMI version found : " + version, Project.MSG_DEBUG);
}
if (version == null) {
version = attributes.getValue("xmi:version");
log("XMI version found : " + version, Project.MSG_DEBUG);
}
}
}
/**
* Do XSLT transformation on given file using specific stylesheet.
*
* @param xmiFile xmi file to transform
* @param modelFile result of transformation
* @param stylesheet stylesheet to use
*/
protected void executeXSLT(File xmiFile, File modelFile, String stylesheet) {
// Transformation XSL
try {
// Load Transformer with service loader
Iterator itTransformerFactory = ServiceLoader
.load(TransformerFactory.class/*, urlLoader*/).iterator();
if (!itTransformerFactory.hasNext()) {
throw new BuildException("No XSLT Transformer found");
}
TransformerFactory transformerFactory = itTransformerFactory.next();
URL uxsl = Resource.getURL(stylesheet);
StreamSource stylesource = new StreamSource(uxsl.openStream());
Transformer transformer = transformerFactory
.newTransformer(stylesource);
if (properties.containsKey("fullPackagePath")) {
transformer.setParameter("fullPackagePath", properties
.getProperty("fullPackagePath"));
}
if (properties.containsKey("extraPackages")) {
transformer.setParameter("extraPackages", properties
.getProperty("extraPackages"));
}
if (resolver != null && !resolver.isEmpty()) {
Class> clazz = (Class>) Class.forName(resolver/*, true, urlLoader*/);
URIResolver tresolver = null;
// Try to set the base using the constructor
try {
// Look for a constructor with a String parameter (base)
Constructor> withBaseConstructor = clazz
.getConstructor(String.class);
// Set the xmi folder as the base
String base = xmiFile.getParentFile().getAbsolutePath();
// Instantiate
tresolver = (URIResolver) withBaseConstructor
.newInstance(base);
} catch (Exception eee) {
log("Unable to instantiate resolver with String parameter",
eee, Project.MSG_WARN);
}
// If resolver is still not created, create it using the default
// constructor
if (tresolver == null) {
tresolver = (URIResolver) clazz.newInstance();
}
transformer.setURIResolver(tresolver);
}
transformer.transform(new StreamSource(xmiFile.getAbsolutePath()),
new StreamResult(modelFile.getAbsolutePath()));
} catch (TransformerException e) {
throw new BuildException("Transformation exception (xslt)", e);
} catch (MalformedURLException e) {
throw new BuildException("Invalid jar url", e);
} catch (InstantiationException e) {
throw new BuildException("Can't init resolver", e);
} catch (IllegalAccessException e) {
throw new BuildException("Can't access resolver", e);
} catch (ClassNotFoundException e) {
throw new BuildException("Can't find resolver", e);
} catch (IOException e) {
throw new BuildException(
"Error while trying to access stylesheet", e);
} catch (SecurityException e) {
throw new BuildException(
"Error while trying to access stylesheet", e);
}
}
} // GeneratorTask