View Javadoc

    1   /*
    2    $Id: AntBuilder.java,v 1.12 2006/06/07 12:44:41 dierk Exp $
    3
    4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
    5
    6    Redistribution and use of this software and associated documentation
    7    ("Software"), with or without modification, are permitted provided
    8    that the following conditions are met:
    9
    10   1. Redistributions of source code must retain copyright
    11      statements and notices.  Redistributions must also contain a
    12      copy of this document.
    13
    14   2. Redistributions in binary form must reproduce the
    15      above copyright notice, this list of conditions and the
    16      following disclaimer in the documentation and/or other
    17      materials provided with the distribution.
    18
    19   3. The name "groovy" must not be used to endorse or promote
    20      products derived from this Software without prior written
    21      permission of The Codehaus.  For written permission,
    22      please contact info@codehaus.org.
    23
    24   4. Products derived from this Software may not be called "groovy"
    25      nor may "groovy" appear in their names without prior written
    26      permission of The Codehaus. "groovy" is a registered
    27      trademark of The Codehaus.
    28
    29   5. Due credit should be given to The Codehaus -
    30      http://groovy.codehaus.org/
    31
    32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
    33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
    34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
    35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
    36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
    43   OF THE POSSIBILITY OF SUCH DAMAGE.
    44
    45   */
    46  package groovy.util;
    47
    48
    49  import java.util.Collections;
    50  import java.util.Iterator;
    51  import java.util.Map;
    52  import java.util.logging.Level;
    53  import java.util.logging.Logger;
    54
    55  import org.apache.tools.ant.BuildLogger;
    56  import org.apache.tools.ant.NoBannerLogger;
    57  import org.apache.tools.ant.Project;
    58  import org.apache.tools.ant.RuntimeConfigurable;
    59  import org.apache.tools.ant.Target;
    60  import org.apache.tools.ant.Task;
    61  import org.apache.tools.ant.UnknownElement;
    62  import org.apache.tools.ant.helper.AntXMLContext;
    63  import org.apache.tools.ant.helper.ProjectHelper2;
    64  import org.codehaus.groovy.ant.FileScanner;
    65  import org.xml.sax.Attributes;
    66  import org.xml.sax.Locator;
    67  import org.xml.sax.SAXParseException;
    68  import org.xml.sax.helpers.AttributesImpl;
    69
    70  /***
    71   * Allows Ant tasks to be used with GroovyMarkup
    72   *
    73   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>, changes by Dierk Koenig (dk)
    74   * @version $Revision: 1.12 $
    75   */
    76  public class AntBuilder extends BuilderSupport {
    77
    78      private static final Class[] addTaskParamTypes = { String.class };
    79
    80      private Logger log = Logger.getLogger(getClass().getName());
    81      private Project project;
    82      private final AntXMLContext antXmlContext;
    83      private final ProjectHelper2.ElementHandler antElementHandler = new ProjectHelper2.ElementHandler();
    84      private final Target collectorTarget = new Target();
    85      private Object lastCompletedNode;
    86
    87
    88
    89      public AntBuilder() {
    90          this(createProject());
    91      }
    92
    93      public AntBuilder(final Project project) {
    94          this.project = project;
    95
    96          antXmlContext = new AntXMLContext(project);
    97          collectorTarget.setProject(project);
    98          antXmlContext.setCurrentTarget(collectorTarget);
    99          antXmlContext.setLocator(new AntBuilderLocator());
    100
    101         // FileScanner is a Groovy hack (utility?)
    102         project.addDataTypeDefinition("fileScanner", FileScanner.class);
    103     }
    104
    105     // dk: introduced for convenience in subclasses
    106     protected Project getProject() {
    107         return project;
    108     }
    109
    110     /***
    111      * @return Factory method to create new Project instances
    112      */
    113     protected static Project createProject() {
    114         Project project = new Project();
    115         BuildLogger logger = new NoBannerLogger();
    116
    117         logger.setMessageOutputLevel(org.apache.tools.ant.Project.MSG_INFO);
    118         logger.setOutputPrintStream(System.out);
    119         logger.setErrorPrintStream(System.err);
    120
    121         project.addBuildListener(logger);
    122
    123         project.init();
    124         project.getBaseDir();
    125         return project;
    126     }
    127
    128     protected void setParent(Object parent, Object child) {
    129     }
    130
    131
    132     /***
    133      * We don't want to return the node as created in {@link #createNode(Object, Map, Object)}
    134      * but the one made ready by {@link #nodeCompleted(Object, Object)}
    135      * @see groovy.util.BuilderSupport#doInvokeMethod(java.lang.String, java.lang.Object, java.lang.Object)
    136      */
    137     protected Object doInvokeMethod(String methodName, Object name, Object args) {
    138     	super.doInvokeMethod(methodName, name, args);
    139
    140
    141     	// return the completed node
    142     	return lastCompletedNode;
    143     }
    144
    145     /***
    146      * Determines, when the ANT Task that is represented by the "node" should perform.
    147      * Node must be an ANT Task or no "perform" is called.
    148      * If node is an ANT Task, it performs right after complete contstruction.
    149      * If node is nested in a TaskContainer, calling "perform" is delegated to that
    150      * TaskContainer.
    151      * @param parent note: null when node is root
    152      * @param node the node that now has all its children applied
    153      */
    154     protected void nodeCompleted(final Object parent, final Object node) {
    155
    156     	antElementHandler.onEndElement(null, null, antXmlContext);
    157
    158     	lastCompletedNode = node;
    159         if (parent != null) {
    160             log.finest("parent is not null: no perform on nodeCompleted");
    161             return; // parent will care about when children perform
    162         }
    163
    164         // as in Target.execute()
    165         if (node instanceof Task) {
    166             Object task = node;
    167             // "Unwrap" the UnknownElement to return the real task to the calling code
    168             if (node instanceof UnknownElement) {
    169             	final UnknownElement unknownElement = (UnknownElement) node;
    170             	unknownElement.maybeConfigure();
    171                 task = unknownElement.getRealThing();
    172             }
    173
    174             lastCompletedNode = task;
    175             // UnknownElement may wrap everything: task, path, ...
    176             if (task instanceof Task) {
    177             	((Task) task).perform();
    178             }
    179         }
    180         else {
    181             final RuntimeConfigurable r = (RuntimeConfigurable) node;
    182             r.maybeConfigure(project);
    183         }
    184     }
    185
    186     protected Object createNode(Object tagName) {
    187         return createNode(tagName.toString(), Collections.EMPTY_MAP);
    188     }
    189
    190     protected Object createNode(Object name, Object value) {
    191         Object task = createNode(name);
    192         setText(task, value.toString());
    193         return task;
    194     }
    195
    196     protected Object createNode(Object name, Map attributes, Object value) {
    197         Object task = createNode(name, attributes);
    198         setText(task, value.toString());
    199         return task;
    200     }
    201
    202     /***
    203      * Builds an {@link Attributes} from a {@link Map}
    204      * @param attributes the attributes to wrap
    205      */
    206     protected static Attributes buildAttributes(final Map attributes) {
    207     	final AttributesImpl attr = new AttributesImpl();
    208     	for (final Iterator iter=attributes.entrySet().iterator(); iter.hasNext(); ) {
    209     		final Map.Entry entry = (Map.Entry) iter.next();
    210     		final String attributeName = (String) entry.getKey();
    211     		final String attributeValue = String.valueOf(entry.getValue());
    212     		attr.addAttribute(null, attributeName, attributeName, "CDATA", attributeValue);
    213     	}
    214     	return attr;
    215     }
    216
    217     protected Object createNode(final Object name, final Map attributes) {
    218
    219     	final String tagName = name.toString();
    220
    221         try
    222 		{
    223 			antElementHandler.onStartElement("", tagName, tagName, buildAttributes(attributes), antXmlContext);
    224 		}
    225 		catch (final SAXParseException e)
    226 		{
    227             log.log(Level.SEVERE, "Caught: " + e, e);
    228 		}
    229
    230 		final RuntimeConfigurable wrapper = (RuntimeConfigurable) antXmlContext.getWrapperStack().lastElement();
    231     	return wrapper.getProxy();
    232     }
    233
    234     protected void setText(Object task, String text) {
    235     	final char[] characters = text.toCharArray();
    236         try {
    237           	antElementHandler.characters(characters, 0, characters.length, antXmlContext);
    238         }
    239         catch (final SAXParseException e) {
    240             log.log(Level.WARNING, "SetText failed: " + task + ". Reason: " + e, e);
    241         }
    242     }
    243
    244     public Project getAntProject() {
    245         return project;
    246     }
    247 }
    248
    249 /***
    250  * Would be nice to retrieve location information (from AST?).
    251  * In a first time, without info
    252  */
    253 class AntBuilderLocator implements Locator {
    254 	public int getColumnNumber()
    255 	{
    256 		return 0;
    257 	}
    258 	public int getLineNumber()
    259 	{
    260 		return 0;
    261 	}
    262 	public String getPublicId()
    263 	{
    264 		return "";
    265 	}
    266 	public String getSystemId()
    267 	{
    268 		return "";
    269 	}
    270 }