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 }