/*
* JBoss, Home of Professional Open Source
* Copyright ${year}, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This 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.
*
* This software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.richfaces.cdk;
import java.io.File;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.codehaus.plexus.util.DirectoryScanner;
import org.richfaces.cdk.apt.CdkProcessorImpl;
import org.richfaces.cdk.apt.LibraryCache;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
/**
* Configurable command-line interface of CDK generator.
*
* This class is similar functionality as {@link org.richfaces.builder.mojo.GenerateMojo} from richfaces-cdk-maven-plugin.
*
* @author Lukas Fryc
*/
@Parameters(resourceBundle = "cmdln")
public class CommandLineGenerator {
private static final String[] JAVA_INCLUDES = new String[] { "**/*.java" };
private static final String MAIN_CONFIG = "src/main/config/";
private static final String DEFAULT_TEMPLATES_ROOT = "src/main/templates/";
private static final String[] EMPTY = new String[0];
private static final String[] XML_INCLUDES = new String[] { "**/*.xml" };
@Parameter(names = { "-p", "--project" }, descriptionKey = "projectRoot")
String projectRoot = ".";
@Parameter(names = { "-d", "--debug" }, descriptionKey = "debug")
boolean debug = false;
@Parameter(names = { "-n", "--namespace" }, descriptionKey = "taglibNamespace")
private String taglibNamespace;
@Parameter(names = { "-t", "--templates" }, descriptionKey = "templateIncludes")
List<String> templateIncludes;
@Parameter(names = { "-h", "--help" }, descriptionKey = "help")
boolean help = false;
@Parameter(names = { "-r", "--force-recompile" }, descriptionKey = "forceRecompile")
boolean forceRecompile = false;
@Parameter(names = { "-e", "--cache-eagerly" }, descriptionKey = "cacheEagerly")
boolean cacheEagerly = false;
private List<String> compileSourceRoots;
protected String[] sourceIncludes;
protected String[] sourceExcludes;
protected String configRoot;
protected File outputDirectory;
protected File outputJavaDirectory;
protected File outputResourcesDirectory;
protected File outputTestDirectory;
protected File outputTestResourcesDirectory;
protected File outputLibraryCache;
protected Map<String, String> options = new HashMap<String, String>();
private Logger logger;
private void setup() {
compileSourceRoots = Arrays.asList(projectRoot + "/src/main/java");
if (templateIncludes == null || templateIncludes.isEmpty()) {
templateIncludes = getDefaultTemplateIncludes();
}
configRoot = projectRoot + "/" + MAIN_CONFIG;
outputDirectory = new File(projectRoot, "target/classes");
outputJavaDirectory = new File(projectRoot, "target/generated-sources/main/java");
outputResourcesDirectory = new File(projectRoot, "target/generated-sources/main/resources");
outputTestDirectory = new File(projectRoot, "target/generated-sources/test/java");
outputTestResourcesDirectory = new File(projectRoot, "target/generated-sources/test/resources");
outputLibraryCache = new File(projectRoot, "target/library-cache");
CustomLogger logger = new CustomLogger();
logger.setDebugEnabled(debug);
this.logger = logger;
}
public boolean isHelp() {
return help;
}
public void execute() {
setup();
TimeMeasure totalTime = new TimeMeasure("cdk", logger).info(true).start(new File(projectRoot).getAbsolutePath());
executeGenerator();
totalTime.stop();
}
private void executeGenerator() {
Generator generator = new Generator();
generator.setLog(logger);
generator.setLoader(createProjectClassLoader());
// Set source folders.
ArrayList<File> folders = new ArrayList<File>(compileSourceRoots.size());
for (String sourceFolder : compileSourceRoots) {
File folder = new File(sourceFolder);
if (folder.exists() && folder.isDirectory()) {
folders.add(folder);
}
}
TimeMeasure time = new TimeMeasure("file scanner", logger).info(true).start();
generator.addSources(Sources.JAVA_SOURCES, findJavaFiles(), folders);
// detect templates and configs directories.
generator.addSources(Sources.RENDERER_TEMPLATES, findTemplateFiles(), null);
generator.addSources(Sources.FACES_CONFIGS, findFacesConfigFiles(), null);
time.stop();
// Setup output folders.
setOutput(generator, outputJavaDirectory, Outputs.JAVA_CLASSES);
setOutput(generator, outputResourcesDirectory, Outputs.RESOURCES);
setOutput(generator, outputTestDirectory, Outputs.TEST_JAVA_CLASSES);
setOutput(generator, outputTestResourcesDirectory, Outputs.TEST_RESOURCES);
setOutput(generator, outputLibraryCache, Outputs.LIBRARY_CACHE);
options.put(LibraryCache.CACHE_ENABLED_OPTION, Boolean.toString(!forceRecompile));
options.put(CdkProcessorImpl.CACHE_EAGERLY_OPTION, Boolean.toString(cacheEagerly));
// configure CDK workers.
setupPlugins(generator);
if (null != options) {
generator.setOptions(options);
}
try {
if (taglibNamespace != null) {
generator.setNamespace(taglibNamespace);
}
// Build JSF library.
// LibraryBuilder builder = LibraryBuilder.createInstance(context);
generator.init();
generator.execute();
if (logger.getErrorCount() > 0) {
throw new IllegalStateException("Errors occurred while JSF library was built");
}
} catch (CdkException e) {
throw new IllegalStateException("CDK build error", e);
}
}
/**
* <p class="changed_added_4_0">
* </p>
*
* @param generator
* @throws MojoFailureException
*/
private void setupPlugins(Generator generator) {
// TODO - get additional modules, as Maven components ?
}
/**
* <p class="changed_added_4_0">
* This utility method sets output directory for particular type. I such directory does not exist, it is created.
* </p>
*
* @param generator
* @param directory
* @param type
*/
private static void setOutput(Generator generator, File directory, Outputs type) {
// if (!directory.exists()) {
// directory.mkdirs();
// }
generator.addOutputFolder(type, directory);
}
private Iterable<File> findTemplateFiles() {
String[] includes = templateIncludes.toArray(new String[templateIncludes.size()]);
String[] files = doScan(includes, EMPTY, new File(projectRoot));
return Collections2.transform(Arrays.asList(files), new StringToFile(projectRoot));
}
private Iterable<File> findJavaFiles() {
Set<File> javaSources = new HashSet<File>();
String[] includes = null == sourceIncludes ? JAVA_INCLUDES : sourceIncludes;
for (String compileRoot : compileSourceRoots) {
File rootFolder = new File(compileRoot);
String[] sources = doScan(includes, sourceExcludes, rootFolder);
for (String src : sources) {
javaSources.add(new File(rootFolder, src));
}
}
return javaSources;
}
private Iterable<File> findFacesConfigFiles() {
String[] files = doScan(XML_INCLUDES, EMPTY, new File(configRoot));
return Collections2.transform(Arrays.asList(files), new StringToFile(configRoot));
}
CdkClassLoader createProjectClassLoader() {
CdkClassLoader classLoader = null;
try {
// This Mojo executed befor process-resources phase, therefore we have to use original resource folders.
List<File> urls = getClassPathElements();
classLoader = new CdkClassLoader(urls, this.getClass().getClassLoader());
} catch (MalformedURLException e) {
throw new IllegalStateException(e);
}
return classLoader;
}
List<File> getClassPathElements() {
List<File> files = new ArrayList<File>();
files.add(new File(projectRoot, "target/classes"));
files.add(new File(projectRoot, "target/dependency"));
return files;
}
protected String[] doScan(String[] includes, String[] excludes, File rootFolder) {
if (!rootFolder.exists()) {
return new String[] {};
}
try {
DirectoryScanner directoryScanner = new DirectoryScanner();
directoryScanner.setFollowSymlinks(true);
directoryScanner.setBasedir(rootFolder);
directoryScanner.setExcludes(excludes);
directoryScanner.setIncludes(includes);
directoryScanner.addDefaultExcludes();
directoryScanner.scan();
return directoryScanner.getIncludedFiles();
} catch (IllegalStateException e) {
throw new IllegalStateException("Error scanning source root: \'" + rootFolder + "\'", e);
}
}
private List<String> getDefaultTemplateIncludes() {
List<String> includes = Lists.newLinkedList();
for (String xmlInclude : XML_INCLUDES) {
includes.add(DEFAULT_TEMPLATES_ROOT + xmlInclude);
}
return includes;
}
private class StringToFile implements Function<String, File> {
private String root;
public StringToFile(String root) {
this.root = root;
}
@Override
public File apply(String input) {
return new File(root, input);
}
};
}