GrandTask.java

  1. // $Id$
  2. /* ====================================================================
  3.  * Copyright (c) 2002-2003, Christophe Labouisse
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms, with or without
  7.  * modification, are permitted provided that the following conditions
  8.  * are met:
  9.  *
  10.  * 1. Redistributions of source code must retain the above copyright
  11.  *    notice, this list of conditions and the following disclaimer.
  12.  *
  13.  * 2. Redistributions in binary form must reproduce the above
  14.  *    copyright notice, this list of conditions and the following
  15.  *    disclaimer in the documentation and/or other materials provided
  16.  *    with the distribution.
  17.  *
  18.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  21.  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  22.  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  23.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  24.  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  25.  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  26.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  27.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  28.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  29.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  30.  */

  31. package net.ggtools.grand.tasks;

  32. import java.io.File;
  33. import java.io.FileInputStream;
  34. import java.io.IOException;
  35. import java.util.ArrayList;
  36. import java.util.LinkedList;
  37. import java.util.List;
  38. import java.util.Map;
  39. import java.util.Properties;

  40. import net.ggtools.grand.ant.AntProject;
  41. import net.ggtools.grand.exceptions.GrandException;
  42. import net.ggtools.grand.filters.GraphFilter;
  43. import net.ggtools.grand.graph.GraphProducer;
  44. import net.ggtools.grand.graph.GraphWriter;
  45. import net.ggtools.grand.log.AntLog;
  46. import net.ggtools.grand.output.DotWriter;

  47. import org.apache.tools.ant.BuildException;
  48. import org.apache.tools.ant.BuildListener;
  49. import org.apache.tools.ant.Project;
  50. import org.apache.tools.ant.ProjectHelper;
  51. import org.apache.tools.ant.Task;
  52. import org.apache.tools.ant.taskdefs.Property;
  53. import org.apache.tools.ant.types.PropertySet;

  54. /**
  55.  * A task to create graphs.
  56.  *
  57.  * @author Christophe Labouisse
  58.  */
  59. public class GrandTask extends Task {

  60.     /**
  61.      * Field DEFAULT_OUTCONFIG_PREFIX.
  62.      */
  63.     private static final String DEFAULT_OUTCONFIG_PREFIX = "dot";

  64.     /**
  65.      * Field buildFile.
  66.      */
  67.     private File buildFile;

  68.     /**
  69.      * Field output.
  70.      */
  71.     private File output;

  72.     /**
  73.      * Field outputConfigurationFile.
  74.      */
  75.     private File outputConfigurationFile;

  76.     /**
  77.      * Field outputConfigurationPrefix.
  78.      */
  79.     private String outputConfigurationPrefix;

  80.     /**
  81.      * The sets of properties to pass to the graphed project.
  82.      */
  83.     private final List<PropertySet> propertySets = new ArrayList<PropertySet>();

  84.     /**
  85.      * Field filters.
  86.      */
  87.     private final List<FilterType> filters = new LinkedList<FilterType>();

  88.     /**
  89.      * Field showGraphName.
  90.      */
  91.     private boolean showGraphName = false;

  92.     /**
  93.      * Field inheritAll.
  94.      */
  95.     private boolean inheritAll = false;

  96.     /**
  97.      * Field properties.
  98.      */
  99.     private final List<Property> properties = new LinkedList<Property>();

  100.     /**
  101.      * Check the parameters validity before execution.
  102.      *
  103.      */
  104.     private void checkParams() {
  105.         if (output == null) {
  106.             final String message = "required attribute missing";
  107.             log(message, Project.MSG_ERR);
  108.             throw new BuildException(message);
  109.         }

  110.         if (outputConfigurationFile != null && outputConfigurationPrefix != null) {
  111.             final String message = "cannot specify both outputconfigfile and outputconfigprefix";
  112.             log(message, Project.MSG_ERR);
  113.             throw new BuildException(message);
  114.         }

  115.         for (FilterType filter : filters) {
  116.             filter.checkParameters();
  117.         }
  118.     }

  119.     /**
  120.      * Method execute.
  121.      * @see org.apache.tools.ant.Task#execute()
  122.      */
  123.     @Override
  124.     public final void execute() {
  125.         checkParams();

  126.         final GraphProducer graphProject = initAntProject();

  127.         log("Done loading project ", Project.MSG_VERBOSE);

  128.         log("Setting up filter chain");
  129.         GraphProducer producer = graphProject;
  130.         int numFilters = 0;

  131.         for (FilterType f : filters) {
  132.             log("Adding filter " + f.getFilterName(), Project.MSG_VERBOSE);
  133.             final GraphFilter filter = f.getFilter();
  134.             filter.setProducer(producer);
  135.             producer = filter;
  136.             numFilters++;
  137.         }

  138.         if (numFilters > 0) {
  139.             log("Loaded " + numFilters + " filter"
  140.                     + ((numFilters > 1) ? "s" : ""));
  141.         }

  142.         try {
  143.             Properties override = null;
  144.             if (outputConfigurationFile != null) {
  145.                 log("Overriding default output configuration from " + outputConfigurationFile);
  146.                 override = new Properties();
  147.                 override.load(new FileInputStream(outputConfigurationFile));
  148.             }
  149.             if (outputConfigurationPrefix != null) {
  150.                 log("Overriding default output configuration from project properties "
  151.                         + outputConfigurationPrefix + ".*");
  152.                 override = new Properties();
  153.                 for (String property : getProject().getProperties().keySet()) {
  154.                     if (property.startsWith(outputConfigurationPrefix)) {
  155.                         // one may use &quot; or &apos; or just ' in project properties
  156.                         override.put(property.replace(outputConfigurationPrefix, DEFAULT_OUTCONFIG_PREFIX),
  157.                                 getProject().getProperty(property).replaceAll("'", "\""));
  158.                     }
  159.                 }
  160.             }

  161.             final GraphWriter writer = new DotWriter(override);
  162.             writer.setProducer(producer);
  163.             writer.setShowGraphName(showGraphName);

  164.             log("Writing output to " + output);
  165.             writer.write(output);
  166.         } catch (final IOException e) {
  167.             log("Cannot write graph", Project.MSG_ERR);
  168.             throw new BuildException("Cannot write graph", e);
  169.         } catch (final GrandException e) {
  170.             log("Cannot process graph", Project.MSG_ERR);
  171.             throw new BuildException("Cannot write graph", e);
  172.         }
  173.     }

  174.     /**
  175.      * Create and initialize a GraphProducer according to the
  176.      * task parameters.
  177.      *
  178.      * @return an initialized GraphProducer.
  179.      */
  180.     private GraphProducer initAntProject() {

  181.         Project antProject;

  182.         if (buildFile == null) {
  183.             // Working with current project
  184.             log("Using current project");
  185.             antProject = getProject();
  186.         } else {
  187.             // Open a new project.
  188.             log("Loading project " + buildFile);

  189.             antProject = loadNewProject();
  190.         }

  191.         for (PropertySet ps : propertySets) {
  192.             addAlmostAll(antProject, ps.getProperties());
  193.         }

  194.         if (properties.size() > 0) {
  195.             for (Property prop : properties) {
  196.                 prop.setProject(antProject);
  197.                 prop.setTaskName("property");
  198.                 prop.execute();
  199.             }
  200.         }

  201.         return new AntProject(antProject);
  202.     }

  203.     /**
  204.      * Load an initialize a new project from a Ant build file.
  205.      *
  206.      * @return a new initialized Ant project.
  207.      */
  208.     private Project loadNewProject() {
  209.         final Project antProject = new Project();

  210.         // Set the current project listeners to the graphed project
  211.         // in order to get some trace if we need to execute some tasks
  212.         // in the graphed project.
  213.         for (final BuildListener listener : getProject().getBuildListeners()) {
  214.             antProject.addBuildListener(listener);
  215.         }

  216.         antProject.init();

  217.         if (!inheritAll) {
  218.             // set Java built-in properties separately,
  219.             // b/c we won't inherit them.
  220.             antProject.setSystemProperties();

  221.         } else {
  222.             // set all properties from calling project
  223.             final Properties props = new Properties();
  224.             for (Map.Entry<String, Object> entry : getProject().getProperties().entrySet()) {
  225.                 props.put(entry.getKey(), entry.getValue());
  226.             }
  227.             addAlmostAll(antProject, props);
  228.         }

  229.         antProject.setUserProperty("ant.file", buildFile.getAbsolutePath());
  230.         final ProjectHelper loader = ProjectHelper.getProjectHelper();
  231.         antProject.addReference("ant.projectHelper", loader);
  232.         loader.parse(antProject, buildFile);

  233.         getProject().copyUserProperties(antProject);

  234.         return antProject;
  235.     }

  236.     /**
  237.      * Method setProject.
  238.      * @param project Project
  239.      * @see org.apache.tools.ant.ProjectComponent#setProject(org.apache.tools.ant.Project)
  240.      */
  241.     @Override
  242.     public final void setProject(final Project project) {
  243.         super.setProject(project);
  244.         AntLog.setCurrentProject(project);
  245.         AntLog.setCurrentTask(this);
  246.     }

  247.     /**
  248.      * Sets the buildFile.
  249.      *
  250.      * @param file File
  251.      */
  252.     public final void setBuildFile(final File file) {
  253.         buildFile = file;
  254.     }

  255.     /**
  256.      * Sets the output file.
  257.      *
  258.      * @param file File
  259.      */
  260.     public final void setOutput(final File file) {
  261.         output = file;
  262.     }

  263.     /**
  264.      * Set a property file to override the output default configuration.
  265.      * @deprecated use {@link #setOutputConfigFile(File)}.
  266.      * @param propertyFile File
  267.      */
  268.     @Deprecated
  269.     public final void setPropertyFile(final File propertyFile) {
  270.         log("Using of deprecated \"propertyfile\" attribute, use \"outputconfigfile\" from now on",
  271.                 Project.MSG_WARN);
  272.         outputConfigurationFile = propertyFile;
  273.     }

  274.     /**
  275.      * Set a property file to override the output default configuration.
  276.      * @param propertyFile File
  277.      */
  278.     public final void setOutputConfigFile(final File propertyFile) {
  279.         outputConfigurationFile = propertyFile;
  280.     }

  281.     /**
  282.      * Set a property prefix to override the output default configuration.
  283.      * @param propertyPrefix String
  284.      */
  285.     public final void setOutputConfigPrefix(final String propertyPrefix) {
  286.         if (propertyPrefix == null) {
  287.             return;
  288.         }
  289.         if (propertyPrefix.isEmpty()) {
  290.             outputConfigurationPrefix = DEFAULT_OUTCONFIG_PREFIX;
  291.             return;
  292.         }
  293.         outputConfigurationPrefix = propertyPrefix;
  294.     }

  295.     /**
  296.      * Method setShowGraphName.
  297.      * @param show boolean
  298.      */
  299.     public final void setShowGraphName(final boolean show) {
  300.         showGraphName = show;
  301.     }

  302.     /**
  303.      * If true, pass all properties to the new Ant project.
  304.      * Defaults to true.
  305.      * @param value if true pass all properties to the new Ant project.
  306.      */
  307.     public final void setInheritAll(final boolean value) {
  308.         inheritAll = value;
  309.     }

  310.     /**
  311.      * Add a filter to the task.
  312.      * @param filter FilterType
  313.      */
  314.     public final void addFilter(final FilterType filter) {
  315.         filters.add(filter);
  316.     }

  317.     /**
  318.      * Add a new property to be passed to the graphed project.
  319.      *
  320.      * @param p the property to set.
  321.      */
  322.     public final void addProperty(final Property p) {
  323.         properties.add(p);
  324.     }

  325.     /**
  326.      * Set of properties to pass to the graphed project.
  327.      *
  328.      * @param ps property set to add
  329.      */
  330.     public final void addPropertyset(final PropertySet ps) {
  331.         propertySets.add(ps);
  332.     }

  333.     /**
  334.      * Copies all properties from the given table to the destination project -
  335.      * omitting those that have already been set in the destination project as
  336.      * well as properties named basedir or ant.file.
  337.      * @param props properties to copy to the new project
  338.      * @since Ant 1.6
  339.      * @param destProject Project
  340.      */
  341.     private void addAlmostAll(final Project destProject, final Properties props) {
  342.         for (Map.Entry<Object, Object> entry : props.entrySet()) {
  343.             final String key = entry.getKey().toString();
  344.             if ("basedir".equals(key) || "ant.file".equals(key)) {
  345.                 // basedir and ant.file should not be altered.
  346.                 continue;
  347.             }

  348.             final String value = entry.getValue().toString();
  349.             // don't re-set user properties, avoid the warning message
  350.             if (destProject.getProperty(key) == null) {
  351.                 // no user property
  352.                 destProject.setNewProperty(key, value);
  353.             }
  354.         }
  355.     }

  356. }