/**
* GRANITE DATA SERVICES
* Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S.
*
* This file is part of the Granite Data Services Platform.
*
* Granite Data Services 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 2.1 of the License, or (at your option) any later version.
*
* Granite Data Services 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 Lesser
* General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA, or see <http://www.gnu.org/licenses/>.
*/
package org.granite.generator.ant;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.types.ZipFileSet;
import org.granite.generator.Generator;
import org.granite.generator.Output;
import org.granite.generator.TemplateUri;
import org.granite.generator.Transformer;
import org.granite.generator.as3.As3TypeFactory;
import org.granite.generator.as3.DefaultEntityFactory;
import org.granite.generator.as3.DefaultRemoteDestinationFactory;
import org.granite.generator.as3.EntityFactory;
import org.granite.generator.as3.JavaAs3GroovyConfiguration;
import org.granite.generator.as3.JavaAs3Input;
import org.granite.generator.as3.PackageTranslator;
import org.granite.generator.as3.RemoteDestinationFactory;
import org.granite.generator.as3.reflect.JavaType.Kind;
import org.granite.generator.gsp.GroovyTemplateFactory;
/**
* @author Franck WOLFF
*/
public abstract class AbstractAntJavaGenTask extends Task implements JavaAs3GroovyConfiguration {
///////////////////////////////////////////////////////////////////////////
// Configurable fields (xml attributes).
private String outputdir = ".";
private String baseoutputdir = null;
private String uid = "uid";
private String entitytemplate = null;
private String entitybasetemplate = null;
private String beantemplate = null;
private String beanbasetemplate = null;
private String interfacetemplate = null;
private String enumtemplate = null;
private String remotetemplate = null;
private String remotebasetemplate = null;
private boolean tide = false;
private String clienttypefactory = null;
private String entityfactory = null;
private String remotedestinationfactory = null;
private String transformer = null;
private Path classpath = null;
private List<FileSet> fileSets = new ArrayList<FileSet>();
private List<ZipFileSet> zipFileSets = new ArrayList<ZipFileSet>();
private List<PackageTranslator> translators = new ArrayList<PackageTranslator>();
///////////////////////////////////////////////////////////////////////////
// Configuration implementation fields.
private File outputDirFile = null;
private File baseOutputDirFile = null;
private As3TypeFactory clientTypeFactoryImpl = null;
private Transformer<?, ?, ?> transformerImpl = null;
private EntityFactory entityFactoryImpl = null;
private RemoteDestinationFactory remoteDestinationFactoryImpl = null;
private GroovyTemplateFactory groovyTemplateFactory = null;
private TemplateUri[] entityTemplateUris = null;
private TemplateUri[] interfaceTemplateUris = null;
private TemplateUri[] beanTemplateUris = null;
private TemplateUri[] enumTemplateUris = null;
private TemplateUri[] remoteTemplateUris = null;
private Map<Class<?>, File> filesetClasses = null;
///////////////////////////////////////////////////////////////////////////
// Task attributes.
public void setOutputdir(String outputdir) {
this.outputdir = outputdir;
}
public void setBaseoutputdir(String baseoutputdir) {
this.baseoutputdir = baseoutputdir;
}
public void setAs3typefactory(String as3typefactory) {
this.clienttypefactory = as3typefactory;
}
public void setClienttypefactory(String clienttypefactory) {
this.clienttypefactory = clienttypefactory;
}
public void setEntityfactory(String entityfactory) {
this.entityfactory = entityfactory;
}
public void setRemotedestinationfactory(String remotedestinationfactory) {
this.remotedestinationfactory = remotedestinationfactory;
}
public void setUid(String uid) {
this.uid = uid;
}
public void setEntitytemplate(String entitytemplate) {
this.entitytemplate = entitytemplate;
}
public void setEntitybasetemplate(String entitybasetemplate) {
this.entitybasetemplate = entitybasetemplate;
}
public void setBeantemplate(String beantemplate) {
this.beantemplate = beantemplate;
}
public void setBeanbasetemplate(String beanbasetemplate) {
this.beanbasetemplate = beanbasetemplate;
}
public void setInterfacetemplate(String interfacetemplate) {
this.interfacetemplate = interfacetemplate;
}
public void setEnumtemplate(String enumtemplate) {
this.enumtemplate = enumtemplate;
}
public void setRemotetemplate(String remotetemplate) {
this.remotetemplate = remotetemplate;
}
public void setRemotebasetemplate(String remotebasetemplate) {
this.remotebasetemplate = remotebasetemplate;
}
public void setTide(boolean tide) {
this.tide = tide;
}
public void setTransformer(String transformer) {
this.transformer = transformer;
}
protected abstract As3TypeFactory initDefaultClientTypeFactory();
protected abstract Transformer<?, ?, ?> initDefaultTransformer();
///////////////////////////////////////////////////////////////////////////
// Task inner elements.
public void addFileset(FileSet fileSet) {
fileSets.add(fileSet);
}
public void addZipfileset(ZipFileSet zipFileSet) {
zipFileSets.add(zipFileSet);
}
public void setClasspath(Path path) {
if (classpath == null)
classpath = path;
else
classpath.append(path);
}
public Path createClasspath() {
if (classpath == null)
classpath = new Path(getProject());
return classpath.createPath();
}
public void setClasspathRef(Reference r) {
createClasspath().setRefid(r);
}
public void addTranslator(PackageTranslator translator) {
translators.add(translator);
}
///////////////////////////////////////////////////////////////////////////
// Task execution.
@Override
public void execute() throws BuildException {
log("Using output dir: " + outputdir, Project.MSG_INFO);
log("Using classpath: " + classpath, Project.MSG_INFO);
AntClassLoader loader = new AntClassLoader(AntJavaAs3Task.class.getClassLoader(), getProject(), classpath, true);
try {
loader.setThreadContextLoader();
filesetClasses = new HashMap<Class<?>, File>();
// Build a Set of all ".class" files in filesets (ignoring nested classes).
log("Loading all Java classes referenced by inner fileset(s) {", Project.MSG_INFO);
for (FileSet fileSet : fileSets) {
DirectoryScanner scanner = fileSet.getDirectoryScanner(getProject());
scanner.setCaseSensitive(true);
scanner.scan();
StringBuilder sb = new StringBuilder(" ");
String[] names = scanner.getIncludedFiles();
for (String name : names) {
if (name.endsWith(".class")) {
log(name, Project.MSG_VERBOSE);
try {
File jFile = new File(scanner.getBasedir(), name);
if (!jFile.exists())
throw new FileNotFoundException(jFile.toString());
String jClassName = normalizeClassName(name.substring(0, name.length() - 6));
Class<?> jClass = loader.loadClass(jClassName);
if (!jClass.isMemberClass() || jClass.isEnum()) {
sb.setLength(4);
sb.append(jClass.toString());
log(sb.toString(), Project.MSG_INFO);
filesetClasses.put(jClass, jFile);
}
} catch (Exception e) {
log(getStackTrace(e));
throw new BuildException("Could not load Java class file: " + name, e);
}
}
else
log("Skipping non class file: " + name, Project.MSG_WARN);
}
}
log("}", Project.MSG_INFO);
// Add all ".class" files from zipfilesets (ignoring nested classes).
log("Loading all Java classes referenced by inner zipfileset(s) {", Project.MSG_INFO);
for (ZipFileSet zipFileSet : zipFileSets) {
File zip = zipFileSet.getSrc(getProject());
if (zip == null)
throw new BuildException("zipfileset must use the src attribute");
if (!zip.getName().endsWith(".jar"))
throw new BuildException("Zipfileset src isn't a '.jar' file: " + zip);
log(" -- scanning: " + zip + " --", Project.MSG_INFO);
DirectoryScanner scanner = zipFileSet.getDirectoryScanner(getProject());
scanner.setCaseSensitive(true);
scanner.scan();
StringBuilder sb = new StringBuilder(" ");
String[] names = scanner.getIncludedFiles();
for (String name : names) {
if (name.endsWith(".class")) {
log(name, Project.MSG_VERBOSE);
try {
String jClassName = normalizeClassName(name.substring(0, name.length() - 6));
Class<?> jClass = loader.loadClass(jClassName);
if (!jClass.isMemberClass() || jClass.isEnum()) {
sb.setLength(4);
sb.append(jClass.toString());
log(sb.toString(), Project.MSG_INFO);
filesetClasses.put(jClass, zip);
}
} catch (Exception e) {
log(getStackTrace(e));
throw new BuildException("Could not load Java class file: " + name, e);
}
}
else
log("Skipping non class file: " + name, Project.MSG_WARN);
}
}
log("}", Project.MSG_INFO);
log("Setting up the generator...", Project.MSG_INFO);
// ClientTypeFactory.
if (clienttypefactory == null) {
clientTypeFactoryImpl = initDefaultClientTypeFactory();
}
else {
log("Instantiating custom ClientTypeFactory class: " + clienttypefactory, Project.MSG_INFO);
clientTypeFactoryImpl = newInstance(loader, clienttypefactory);
}
// EntityFactory
if (entityfactory == null)
entityFactoryImpl = new DefaultEntityFactory();
else {
log("Instantiating custom EntityFactory class: " + entityfactory, Project.MSG_INFO);
entityFactoryImpl = newInstance(loader, entityfactory);
}
// RemoteDestinationFactory
if (remotedestinationfactory == null)
remoteDestinationFactoryImpl = new DefaultRemoteDestinationFactory();
else {
log("Instantiating custom RemoteDestinationFactory class: " + remotedestinationfactory, Project.MSG_INFO);
remoteDestinationFactoryImpl = newInstance(loader, remotedestinationfactory);
}
// Listener.
AntListener listener = new AntListener(this);
// Transformer.
if (transformer == null)
transformerImpl = initDefaultTransformer();
else {
log("Instantiating custom Transformer class: " + transformer, Project.MSG_INFO);
transformerImpl = newInstance(loader, transformer);
}
transformerImpl.setListener(listener);
// Enum templates.
String baseTemplateUri = null;
String templateUri = defaultTemplateUri("ENUM");
if (enumtemplate != null) {
log("Using custom enum template: " + enumtemplate, Project.MSG_INFO);
templateUri = enumtemplate;
}
enumTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
// Interface templates.
templateUri = defaultTemplateUri("INTERFACE");
if (interfacetemplate != null) {
log("Using custom interface template: " + interfacetemplate, Project.MSG_INFO);
templateUri = interfacetemplate;
}
interfaceTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
// Entity templates.
baseTemplateUri = defaultTemplateUri("ENTITY_BASE");
templateUri = defaultTemplateUri("ENTITY");
if (entitytemplate != null) {
log("Using custom entity template: " + entitytemplate, Project.MSG_INFO);
templateUri = entitytemplate;
}
if (entitybasetemplate != null) {
log("Using custom entity base template: " + entitybasetemplate, Project.MSG_INFO);
baseTemplateUri = entitybasetemplate;
}
else if (tide) {
log("Using tide entity base template.", Project.MSG_INFO);
baseTemplateUri = defaultTemplateUri("TIDE_ENTITY_BASE");
}
entityTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
// Other bean templates.
baseTemplateUri = defaultTemplateUri("BEAN_BASE");
templateUri = defaultTemplateUri("BEAN");
if (beantemplate != null) {
log("Using custom bean template: " + beantemplate, Project.MSG_INFO);
templateUri = beantemplate;
}
if (beanbasetemplate != null) {
log("Using custom bean base template: " + beanbasetemplate, Project.MSG_INFO);
baseTemplateUri = beanbasetemplate;
}
else if (tide) {
log("Using tide bean base template.", Project.MSG_INFO);
baseTemplateUri = defaultTemplateUri("TIDE_BEAN_BASE");
}
beanTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
// Remote service templates.
baseTemplateUri = defaultTemplateUri("REMOTE_BASE");
templateUri = defaultTemplateUri("REMOTE");
if (remotetemplate != null) {
log("Using custom remote template: " + remotetemplate, Project.MSG_INFO);
templateUri = remotetemplate;
}
else if (tide) {
log("Using Tide remote destination template.", Project.MSG_INFO);
templateUri = defaultTemplateUri("TIDE_REMOTE");
}
if (remotebasetemplate != null) {
log("Using custom remote base template: " + remotebasetemplate, Project.MSG_INFO);
baseTemplateUri = remotebasetemplate;
}
else if (tide) {
log("Using Tide remote destination base template.", Project.MSG_INFO);
baseTemplateUri = defaultTemplateUri("TIDE_REMOTE_BASE");
}
remoteTemplateUris = createTemplateUris(baseTemplateUri, templateUri);
// Create the generator.
Generator generator = new Generator(this);
generator.add(transformerImpl);
// Call the generator for each ".class".
log("Calling the generator for each Java class {", Project.MSG_INFO);
int count = 0;
for (Map.Entry<Class<?>, File> classFile : filesetClasses.entrySet()) {
if (classFile.getKey().isAnonymousClass())
continue;
try {
JavaAs3Input input = new JavaAs3Input(classFile.getKey(), classFile.getValue());
for (Output<?> output : generator.generate(input)) {
if (output.isOutdated())
count++;
}
} catch (Exception e) {
log(getStackTrace(e));
throw new BuildException("Could not generate AS3 beans for: " + classFile.getKey(), e);
}
}
log("}", Project.MSG_INFO);
log("Files affected: " + count + (count == 0 ? " (nothing to do)." : "."));
} finally {
loader.resetThreadContextLoader();
}
}
private String normalizeClassName(String name) {
return name.replace('/', '.').replace('\\', '.');
}
///////////////////////////////////////////////////////////////////////////
// Configuration implementation methods.
@Override
public As3TypeFactory getAs3TypeFactory() {
return clientTypeFactoryImpl;
}
public As3TypeFactory getClientTypeFactory() {
return clientTypeFactoryImpl;
}
@Override
public EntityFactory getEntityFactory() {
return entityFactoryImpl;
}
@Override
public RemoteDestinationFactory getRemoteDestinationFactory() {
return remoteDestinationFactoryImpl;
}
@Override
public File getBaseOutputDir(JavaAs3Input input) {
if (baseOutputDirFile == null)
baseOutputDirFile = new File(baseoutputdir != null ? baseoutputdir : outputdir);
return baseOutputDirFile;
}
@Override
public File getOutputDir(JavaAs3Input input) {
if (outputDirFile == null)
outputDirFile = new File(outputdir);
return outputDirFile;
}
@Override
public TemplateUri[] getTemplateUris(Kind kind, Class<?> clazz) {
switch (kind) {
case ENTITY:
return entityTemplateUris;
case INTERFACE:
return interfaceTemplateUris;
case ENUM:
return enumTemplateUris;
case BEAN:
return beanTemplateUris;
case REMOTE_DESTINATION:
return remoteTemplateUris;
default:
throw new IllegalArgumentException("Unknown template kind: " + kind + " / " + clazz);
}
}
protected abstract String defaultTemplateUri(String type);
@Override
public List<PackageTranslator> getTranslators() {
return translators;
}
@Override
public String getUid() {
return uid;
}
@Override
public boolean isGenerated(Class<?> clazz) {
return filesetClasses.containsKey(clazz);
}
@Override
public ClassLoader getClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
@Override
public GroovyTemplateFactory getGroovyTemplateFactory() {
if (groovyTemplateFactory == null)
groovyTemplateFactory = new GroovyTemplateFactory();
return groovyTemplateFactory;
}
@Override
public File getWorkingDirectory() {
return getProject().getBaseDir();
}
///////////////////////////////////////////////////////////////////////////
// Utilities.
@SuppressWarnings("unchecked")
private <T> T newInstance(ClassLoader loader, String className) {
try {
return (T)loader.loadClass(className).newInstance();
} catch (Exception e) {
log(getStackTrace(e));
throw new BuildException("Could not instantiate custom class: " + className, e);
}
}
private static String getStackTrace(Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
return sw.toString();
}
private TemplateUri[] createTemplateUris(String baseUri, String uri) {
if (uri == null || uri.length() == 0)
return new TemplateUri[0];
if (baseUri != null && baseUri.length() == 0)
baseUri = null;
TemplateUri[] templateUris = new TemplateUri[baseUri == null ? 1 : 2];
int i = 0;
if (baseUri != null)
templateUris[i++] = new TemplateUri(baseUri, true);
templateUris[i] = new TemplateUri(uri, false);
return templateUris;
}
}