GrandTask.java
// $Id$
/* ====================================================================
* Copyright (c) 2002-2003, Christophe Labouisse
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS 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
* COPYRIGHT OWNER OR 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.
*/
package net.ggtools.grand.tasks;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import net.ggtools.grand.ant.AntProject;
import net.ggtools.grand.exceptions.GrandException;
import net.ggtools.grand.filters.GraphFilter;
import net.ggtools.grand.graph.GraphProducer;
import net.ggtools.grand.graph.GraphWriter;
import net.ggtools.grand.log.AntLog;
import net.ggtools.grand.output.DotWriter;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.BuildListener;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Property;
import org.apache.tools.ant.types.PropertySet;
/**
* A task to create graphs.
*
* @author Christophe Labouisse
*/
public class GrandTask extends Task {
/**
* Field DEFAULT_OUTCONFIG_PREFIX.
*/
private static final String DEFAULT_OUTCONFIG_PREFIX = "dot";
/**
* Field buildFile.
*/
private File buildFile;
/**
* Field output.
*/
private File output;
/**
* Field outputConfigurationFile.
*/
private File outputConfigurationFile;
/**
* Field outputConfigurationPrefix.
*/
private String outputConfigurationPrefix;
/**
* The sets of properties to pass to the graphed project.
*/
private final List<PropertySet> propertySets = new ArrayList<PropertySet>();
/**
* Field filters.
*/
private final List<FilterType> filters = new LinkedList<FilterType>();
/**
* Field showGraphName.
*/
private boolean showGraphName = false;
/**
* Field inheritAll.
*/
private boolean inheritAll = false;
/**
* Field properties.
*/
private final List<Property> properties = new LinkedList<Property>();
/**
* Check the parameters validity before execution.
*
*/
private void checkParams() {
if (output == null) {
final String message = "required attribute missing";
log(message, Project.MSG_ERR);
throw new BuildException(message);
}
if (outputConfigurationFile != null && outputConfigurationPrefix != null) {
final String message = "cannot specify both outputconfigfile and outputconfigprefix";
log(message, Project.MSG_ERR);
throw new BuildException(message);
}
for (FilterType filter : filters) {
filter.checkParameters();
}
}
/**
* Method execute.
* @see org.apache.tools.ant.Task#execute()
*/
@Override
public final void execute() {
checkParams();
final GraphProducer graphProject = initAntProject();
log("Done loading project ", Project.MSG_VERBOSE);
log("Setting up filter chain");
GraphProducer producer = graphProject;
int numFilters = 0;
for (FilterType f : filters) {
log("Adding filter " + f.getFilterName(), Project.MSG_VERBOSE);
final GraphFilter filter = f.getFilter();
filter.setProducer(producer);
producer = filter;
numFilters++;
}
if (numFilters > 0) {
log("Loaded " + numFilters + " filter"
+ ((numFilters > 1) ? "s" : ""));
}
try {
Properties override = null;
if (outputConfigurationFile != null) {
log("Overriding default output configuration from " + outputConfigurationFile);
override = new Properties();
override.load(new FileInputStream(outputConfigurationFile));
}
if (outputConfigurationPrefix != null) {
log("Overriding default output configuration from project properties "
+ outputConfigurationPrefix + ".*");
override = new Properties();
for (String property : getProject().getProperties().keySet()) {
if (property.startsWith(outputConfigurationPrefix)) {
// one may use " or ' or just ' in project properties
override.put(property.replace(outputConfigurationPrefix, DEFAULT_OUTCONFIG_PREFIX),
getProject().getProperty(property).replaceAll("'", "\""));
}
}
}
final GraphWriter writer = new DotWriter(override);
writer.setProducer(producer);
writer.setShowGraphName(showGraphName);
log("Writing output to " + output);
writer.write(output);
} catch (final IOException e) {
log("Cannot write graph", Project.MSG_ERR);
throw new BuildException("Cannot write graph", e);
} catch (final GrandException e) {
log("Cannot process graph", Project.MSG_ERR);
throw new BuildException("Cannot write graph", e);
}
}
/**
* Create and initialize a GraphProducer according to the
* task parameters.
*
* @return an initialized GraphProducer.
*/
private GraphProducer initAntProject() {
Project antProject;
if (buildFile == null) {
// Working with current project
log("Using current project");
antProject = getProject();
} else {
// Open a new project.
log("Loading project " + buildFile);
antProject = loadNewProject();
}
for (PropertySet ps : propertySets) {
addAlmostAll(antProject, ps.getProperties());
}
if (properties.size() > 0) {
for (Property prop : properties) {
prop.setProject(antProject);
prop.setTaskName("property");
prop.execute();
}
}
return new AntProject(antProject);
}
/**
* Load an initialize a new project from a Ant build file.
*
* @return a new initialized Ant project.
*/
private Project loadNewProject() {
final Project antProject = new Project();
// Set the current project listeners to the graphed project
// in order to get some trace if we need to execute some tasks
// in the graphed project.
for (final BuildListener listener : getProject().getBuildListeners()) {
antProject.addBuildListener(listener);
}
antProject.init();
if (!inheritAll) {
// set Java built-in properties separately,
// b/c we won't inherit them.
antProject.setSystemProperties();
} else {
// set all properties from calling project
final Properties props = new Properties();
for (Map.Entry<String, Object> entry : getProject().getProperties().entrySet()) {
props.put(entry.getKey(), entry.getValue());
}
addAlmostAll(antProject, props);
}
antProject.setUserProperty("ant.file", buildFile.getAbsolutePath());
final ProjectHelper loader = ProjectHelper.getProjectHelper();
antProject.addReference("ant.projectHelper", loader);
loader.parse(antProject, buildFile);
getProject().copyUserProperties(antProject);
return antProject;
}
/**
* Method setProject.
* @param project Project
* @see org.apache.tools.ant.ProjectComponent#setProject(org.apache.tools.ant.Project)
*/
@Override
public final void setProject(final Project project) {
super.setProject(project);
AntLog.setCurrentProject(project);
AntLog.setCurrentTask(this);
}
/**
* Sets the buildFile.
*
* @param file File
*/
public final void setBuildFile(final File file) {
buildFile = file;
}
/**
* Sets the output file.
*
* @param file File
*/
public final void setOutput(final File file) {
output = file;
}
/**
* Set a property file to override the output default configuration.
* @deprecated use {@link #setOutputConfigFile(File)}.
* @param propertyFile File
*/
@Deprecated
public final void setPropertyFile(final File propertyFile) {
log("Using of deprecated \"propertyfile\" attribute, use \"outputconfigfile\" from now on",
Project.MSG_WARN);
outputConfigurationFile = propertyFile;
}
/**
* Set a property file to override the output default configuration.
* @param propertyFile File
*/
public final void setOutputConfigFile(final File propertyFile) {
outputConfigurationFile = propertyFile;
}
/**
* Set a property prefix to override the output default configuration.
* @param propertyPrefix String
*/
public final void setOutputConfigPrefix(final String propertyPrefix) {
if (propertyPrefix == null) {
return;
}
if (propertyPrefix.isEmpty()) {
outputConfigurationPrefix = DEFAULT_OUTCONFIG_PREFIX;
return;
}
outputConfigurationPrefix = propertyPrefix;
}
/**
* Method setShowGraphName.
* @param show boolean
*/
public final void setShowGraphName(final boolean show) {
showGraphName = show;
}
/**
* If true, pass all properties to the new Ant project.
* Defaults to true.
* @param value if true pass all properties to the new Ant project.
*/
public final void setInheritAll(final boolean value) {
inheritAll = value;
}
/**
* Add a filter to the task.
* @param filter FilterType
*/
public final void addFilter(final FilterType filter) {
filters.add(filter);
}
/**
* Add a new property to be passed to the graphed project.
*
* @param p the property to set.
*/
public final void addProperty(final Property p) {
properties.add(p);
}
/**
* Set of properties to pass to the graphed project.
*
* @param ps property set to add
*/
public final void addPropertyset(final PropertySet ps) {
propertySets.add(ps);
}
/**
* Copies all properties from the given table to the destination project -
* omitting those that have already been set in the destination project as
* well as properties named basedir or ant.file.
* @param props properties to copy to the new project
* @since Ant 1.6
* @param destProject Project
*/
private void addAlmostAll(final Project destProject, final Properties props) {
for (Map.Entry<Object, Object> entry : props.entrySet()) {
final String key = entry.getKey().toString();
if ("basedir".equals(key) || "ant.file".equals(key)) {
// basedir and ant.file should not be altered.
continue;
}
final String value = entry.getValue().toString();
// don't re-set user properties, avoid the warning message
if (destProject.getProperty(key) == null) {
// no user property
destProject.setNewProperty(key, value);
}
}
}
}