View Javadoc

    1   /*
    2    $Id: AntBuilder.java 4077 2006-09-26 19:51:42Z glaforge $
    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  import groovy.xml.QName;
    70
    71  /***
    72   * Allows Ant tasks to be used with GroovyMarkup
    73   *
    74   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>, changes by Dierk Koenig (dk)
    75   * @version $Revision: 4077 $
    76   */
    77  public class AntBuilder extends BuilderSupport {
    78
    79      private static final Class[] addTaskParamTypes = { String.class };
    80
    81      private Logger log = Logger.getLogger(getClass().getName());
    82      private Project project;
    83      private final AntXMLContext antXmlContext;
    84      private final ProjectHelper2.ElementHandler antElementHandler = new ProjectHelper2.ElementHandler();
    85      private final Target collectorTarget;
    86      private Object lastCompletedNode;
    87
    88
    89
    90      public AntBuilder() {
    91          this(createProject());
    92      }
    93
    94      public AntBuilder(final Project project) {
    95          this(project, new Target());
    96      }
    97
    98      public AntBuilder(final Project project, final Target owningTarget) {
    99          this.project = project;
    100
    101         collectorTarget = owningTarget;
    102
    103         antXmlContext = new AntXMLContext(project);
    104         collectorTarget.setProject(project);
    105         antXmlContext.setCurrentTarget(collectorTarget);
    106         antXmlContext.setLocator(new AntBuilderLocator());
    107
    108         // FileScanner is a Groovy hack (utility?)
    109         project.addDataTypeDefinition("fileScanner", FileScanner.class);
    110     }
    111
    112     // dk: introduced for convenience in subclasses
    113     protected Project getProject() {
    114         return project;
    115     }
    116
    117     /***
    118      * @return Factory method to create new Project instances
    119      */
    120     protected static Project createProject() {
    121         Project project = new Project();
    122         BuildLogger logger = new NoBannerLogger();
    123
    124         logger.setMessageOutputLevel(org.apache.tools.ant.Project.MSG_INFO);
    125         logger.setOutputPrintStream(System.out);
    126         logger.setErrorPrintStream(System.err);
    127
    128         project.addBuildListener(logger);
    129
    130         project.init();
    131         project.getBaseDir();
    132         return project;
    133     }
    134
    135     protected void setParent(Object parent, Object child) {
    136     }
    137
    138
    139     /***
    140      * We don't want to return the node as created in {@link #createNode(Object, Map, Object)}
    141      * but the one made ready by {@link #nodeCompleted(Object, Object)}
    142      * @see groovy.util.BuilderSupport#doInvokeMethod(java.lang.String, java.lang.Object, java.lang.Object)
    143      */
    144     protected Object doInvokeMethod(String methodName, Object name, Object args) {
    145     	super.doInvokeMethod(methodName, name, args);
    146
    147
    148     	// return the completed node
    149     	return lastCompletedNode;
    150     }
    151
    152     /***
    153      * Determines, when the ANT Task that is represented by the "node" should perform.
    154      * Node must be an ANT Task or no "perform" is called.
    155      * If node is an ANT Task, it performs right after complete contstruction.
    156      * If node is nested in a TaskContainer, calling "perform" is delegated to that
    157      * TaskContainer.
    158      * @param parent note: null when node is root
    159      * @param node the node that now has all its children applied
    160      */
    161     protected void nodeCompleted(final Object parent, final Object node) {
    162
    163     	antElementHandler.onEndElement(null, null, antXmlContext);
    164
    165     	lastCompletedNode = node;
    166         if (parent != null) {
    167             log.finest("parent is not null: no perform on nodeCompleted");
    168             return; // parent will care about when children perform
    169         }
    170
    171         // as in Target.execute()
    172         if (node instanceof Task) {
    173             Object task = node;
    174             // "Unwrap" the UnknownElement to return the real task to the calling code
    175             if (node instanceof UnknownElement) {
    176             	final UnknownElement unknownElement = (UnknownElement) node;
    177             	unknownElement.maybeConfigure();
    178                 task = unknownElement.getRealThing();
    179             }
    180
    181             lastCompletedNode = task;
    182             // UnknownElement may wrap everything: task, path, ...
    183             if (task instanceof Task) {
    184             	((Task) task).perform();
    185             }
    186         }
    187         else {
    188             final RuntimeConfigurable r = (RuntimeConfigurable) node;
    189             r.maybeConfigure(project);
    190         }
    191     }
    192
    193     protected Object createNode(Object tagName) {
    194         return createNode(tagName, Collections.EMPTY_MAP);
    195     }
    196
    197     protected Object createNode(Object name, Object value) {
    198         Object task = createNode(name);
    199         setText(task, value.toString());
    200         return task;
    201     }
    202
    203     protected Object createNode(Object name, Map attributes, Object value) {
    204         Object task = createNode(name, attributes);
    205         setText(task, value.toString());
    206         return task;
    207     }
    208
    209     /***
    210      * Builds an {@link Attributes} from a {@link Map}
    211      * @param attributes the attributes to wrap
    212      */
    213     protected static Attributes buildAttributes(final Map attributes) {
    214     	final AttributesImpl attr = new AttributesImpl();
    215     	for (final Iterator iter=attributes.entrySet().iterator(); iter.hasNext(); ) {
    216     		final Map.Entry entry = (Map.Entry) iter.next();
    217     		final String attributeName = (String) entry.getKey();
    218     		final String attributeValue = String.valueOf(entry.getValue());
    219     		attr.addAttribute(null, attributeName, attributeName, "CDATA", attributeValue);
    220     	}
    221     	return attr;
    222     }
    223
    224     protected Object createNode(final Object name, final Map attributes) {
    225
    226         String tagName = name.toString();
    227         String ns = "";
    228
    229         if(name instanceof QName) {
    230             QName q = (QName)name;
    231             tagName = q.getLocalPart();
    232             ns = q.getNamespaceURI();
    233         }
    234
    235         try
    236 		{
    237 			antElementHandler.onStartElement(ns, tagName, tagName, buildAttributes(attributes), antXmlContext);
    238 		}
    239 		catch (final SAXParseException e)
    240 		{
    241             log.log(Level.SEVERE, "Caught: " + e, e);
    242 		}
    243
    244 		final RuntimeConfigurable wrapper = (RuntimeConfigurable) antXmlContext.getWrapperStack().lastElement();
    245     	return wrapper.getProxy();
    246     }
    247
    248     protected void setText(Object task, String text) {
    249     	final char[] characters = text.toCharArray();
    250         try {
    251           	antElementHandler.characters(characters, 0, characters.length, antXmlContext);
    252         }
    253         catch (final SAXParseException e) {
    254             log.log(Level.WARNING, "SetText failed: " + task + ". Reason: " + e, e);
    255         }
    256     }
    257
    258     public Project getAntProject() {
    259         return project;
    260     }
    261 }
    262
    263 /***
    264  * Would be nice to retrieve location information (from AST?).
    265  * In a first time, without info
    266  */
    267 class AntBuilderLocator implements Locator {
    268 	public int getColumnNumber()
    269 	{
    270 		return 0;
    271 	}
    272 	public int getLineNumber()
    273 	{
    274 		return 0;
    275 	}
    276 	public String getPublicId()
    277 	{
    278 		return "";
    279 	}
    280 	public String getSystemId()
    281 	{
    282 		return "";
    283 	}
    284 }