001: /*
002: $Id: AntBuilder.java 4077 2006-09-26 19:51:42Z glaforge $
003:
004: Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005:
006: Redistribution and use of this software and associated documentation
007: ("Software"), with or without modification, are permitted provided
008: that the following conditions are met:
009:
010: 1. Redistributions of source code must retain copyright
011: statements and notices. Redistributions must also contain a
012: copy of this document.
013:
014: 2. Redistributions in binary form must reproduce the
015: above copyright notice, this list of conditions and the
016: following disclaimer in the documentation and/or other
017: materials provided with the distribution.
018:
019: 3. The name "groovy" must not be used to endorse or promote
020: products derived from this Software without prior written
021: permission of The Codehaus. For written permission,
022: please contact info@codehaus.org.
023:
024: 4. Products derived from this Software may not be called "groovy"
025: nor may "groovy" appear in their names without prior written
026: permission of The Codehaus. "groovy" is a registered
027: trademark of The Codehaus.
028:
029: 5. Due credit should be given to The Codehaus -
030: http://groovy.codehaus.org/
031:
032: THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033: ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034: NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
036: THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042: ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043: OF THE POSSIBILITY OF SUCH DAMAGE.
044:
045: */
046: package groovy.util;
047:
048: import java.util.Collections;
049: import java.util.Iterator;
050: import java.util.Map;
051: import java.util.logging.Level;
052: import java.util.logging.Logger;
053:
054: import org.apache.tools.ant.BuildLogger;
055: import org.apache.tools.ant.NoBannerLogger;
056: import org.apache.tools.ant.Project;
057: import org.apache.tools.ant.RuntimeConfigurable;
058: import org.apache.tools.ant.Target;
059: import org.apache.tools.ant.Task;
060: import org.apache.tools.ant.UnknownElement;
061: import org.apache.tools.ant.helper.AntXMLContext;
062: import org.apache.tools.ant.helper.ProjectHelper2;
063: import org.codehaus.groovy.ant.FileScanner;
064: import org.xml.sax.Attributes;
065: import org.xml.sax.Locator;
066: import org.xml.sax.SAXParseException;
067: import org.xml.sax.helpers.AttributesImpl;
068: import groovy.xml.QName;
069:
070: /**
071: * Allows Ant tasks to be used with GroovyMarkup
072: *
073: * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>, changes by Dierk Koenig (dk)
074: * @version $Revision: 4077 $
075: */
076: public class AntBuilder extends BuilderSupport {
077:
078: private static final Class[] addTaskParamTypes = { String.class };
079:
080: private Logger log = Logger.getLogger(getClass().getName());
081: private Project project;
082: private final AntXMLContext antXmlContext;
083: private final ProjectHelper2.ElementHandler antElementHandler = new ProjectHelper2.ElementHandler();
084: private final Target collectorTarget;
085: private Object lastCompletedNode;
086:
087: public AntBuilder() {
088: this (createProject());
089: }
090:
091: public AntBuilder(final Project project) {
092: this (project, new Target());
093: }
094:
095: public AntBuilder(final Project project, final Target owningTarget) {
096: this .project = project;
097:
098: collectorTarget = owningTarget;
099:
100: antXmlContext = new AntXMLContext(project);
101: collectorTarget.setProject(project);
102: antXmlContext.setCurrentTarget(collectorTarget);
103: antXmlContext.setLocator(new AntBuilderLocator());
104:
105: // FileScanner is a Groovy hack (utility?)
106: project.addDataTypeDefinition("fileScanner", FileScanner.class);
107: }
108:
109: // dk: introduced for convenience in subclasses
110: protected Project getProject() {
111: return project;
112: }
113:
114: /**
115: * @return Factory method to create new Project instances
116: */
117: protected static Project createProject() {
118: Project project = new Project();
119: BuildLogger logger = new NoBannerLogger();
120:
121: logger
122: .setMessageOutputLevel(org.apache.tools.ant.Project.MSG_INFO);
123: logger.setOutputPrintStream(System.out);
124: logger.setErrorPrintStream(System.err);
125:
126: project.addBuildListener(logger);
127:
128: project.init();
129: project.getBaseDir();
130: return project;
131: }
132:
133: protected void setParent(Object parent, Object child) {
134: }
135:
136: /**
137: * We don't want to return the node as created in {@link #createNode(Object, Map, Object)}
138: * but the one made ready by {@link #nodeCompleted(Object, Object)}
139: * @see groovy.util.BuilderSupport#doInvokeMethod(java.lang.String, java.lang.Object, java.lang.Object)
140: */
141: protected Object doInvokeMethod(String methodName, Object name,
142: Object args) {
143: super .doInvokeMethod(methodName, name, args);
144:
145: // return the completed node
146: return lastCompletedNode;
147: }
148:
149: /**
150: * Determines, when the ANT Task that is represented by the "node" should perform.
151: * Node must be an ANT Task or no "perform" is called.
152: * If node is an ANT Task, it performs right after complete contstruction.
153: * If node is nested in a TaskContainer, calling "perform" is delegated to that
154: * TaskContainer.
155: * @param parent note: null when node is root
156: * @param node the node that now has all its children applied
157: */
158: protected void nodeCompleted(final Object parent, final Object node) {
159:
160: antElementHandler.onEndElement(null, null, antXmlContext);
161:
162: lastCompletedNode = node;
163: if (parent != null) {
164: log
165: .finest("parent is not null: no perform on nodeCompleted");
166: return; // parent will care about when children perform
167: }
168:
169: // as in Target.execute()
170: if (node instanceof Task) {
171: Object task = node;
172: // "Unwrap" the UnknownElement to return the real task to the calling code
173: if (node instanceof UnknownElement) {
174: final UnknownElement unknownElement = (UnknownElement) node;
175: unknownElement.maybeConfigure();
176: task = unknownElement.getRealThing();
177: }
178:
179: lastCompletedNode = task;
180: // UnknownElement may wrap everything: task, path, ...
181: if (task instanceof Task) {
182: ((Task) task).perform();
183: }
184: } else {
185: final RuntimeConfigurable r = (RuntimeConfigurable) node;
186: r.maybeConfigure(project);
187: }
188: }
189:
190: protected Object createNode(Object tagName) {
191: return createNode(tagName, Collections.EMPTY_MAP);
192: }
193:
194: protected Object createNode(Object name, Object value) {
195: Object task = createNode(name);
196: setText(task, value.toString());
197: return task;
198: }
199:
200: protected Object createNode(Object name, Map attributes,
201: Object value) {
202: Object task = createNode(name, attributes);
203: setText(task, value.toString());
204: return task;
205: }
206:
207: /**
208: * Builds an {@link Attributes} from a {@link Map}
209: * @param attributes the attributes to wrap
210: */
211: protected static Attributes buildAttributes(final Map attributes) {
212: final AttributesImpl attr = new AttributesImpl();
213: for (final Iterator iter = attributes.entrySet().iterator(); iter
214: .hasNext();) {
215: final Map.Entry entry = (Map.Entry) iter.next();
216: final String attributeName = (String) entry.getKey();
217: final String attributeValue = String.valueOf(entry
218: .getValue());
219: attr.addAttribute(null, attributeName, attributeName,
220: "CDATA", attributeValue);
221: }
222: return attr;
223: }
224:
225: protected Object createNode(final Object name, final Map attributes) {
226:
227: String tagName = name.toString();
228: String ns = "";
229:
230: if (name instanceof QName) {
231: QName q = (QName) name;
232: tagName = q.getLocalPart();
233: ns = q.getNamespaceURI();
234: }
235:
236: try {
237: antElementHandler.onStartElement(ns, tagName, tagName,
238: buildAttributes(attributes), antXmlContext);
239: } catch (final SAXParseException e) {
240: log.log(Level.SEVERE, "Caught: " + e, e);
241: }
242:
243: final RuntimeConfigurable wrapper = (RuntimeConfigurable) antXmlContext
244: .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,
252: characters.length, antXmlContext);
253: } catch (final SAXParseException e) {
254: log.log(Level.WARNING, "SetText failed: " + task
255: + ". Reason: " + e, e);
256: }
257: }
258:
259: public Project getAntProject() {
260: return project;
261: }
262: }
263:
264: /**
265: * Would be nice to retrieve location information (from AST?).
266: * In a first time, without info
267: */
268: class AntBuilderLocator implements Locator {
269: public int getColumnNumber() {
270: return 0;
271: }
272:
273: public int getLineNumber() {
274: return 0;
275: }
276:
277: public String getPublicId() {
278: return "";
279: }
280:
281: public String getSystemId() {
282: return "";
283: }
284: }
|