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 }