1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package groovy.util;
47
48
49 import java.lang.reflect.Constructor;
50 import java.lang.reflect.InvocationTargetException;
51 import java.lang.reflect.Method;
52 import java.util.Collections;
53 import java.util.Iterator;
54 import java.util.Map;
55 import java.util.logging.Level;
56 import java.util.logging.Logger;
57
58 import org.apache.tools.ant.*;
59 import org.apache.tools.ant.types.DataType;
60 import org.codehaus.groovy.ant.FileScanner;
61 import org.codehaus.groovy.runtime.InvokerHelper;
62
63 /***
64 * Allows Ant tasks to be used with GroovyMarkup
65 *
66 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>, changes by Dierk Koenig (dk)
67 * @version $Revision: 1.9 $
68 */
69 public class AntBuilder extends BuilderSupport {
70
71 private static final Class[] addTaskParamTypes = { String.class };
72
73 private Logger log = Logger.getLogger(getClass().getName());
74 private Project project;
75
76 public AntBuilder() {
77 this.project = createProject();
78 }
79
80 public AntBuilder(Project project) {
81 this.project = project;
82 }
83
84
85 protected Project getProject() {
86 return project;
87 }
88
89 /***
90 * @return Factory method to create new Project instances
91 */
92 protected Project createProject() {
93 Project project = new Project();
94 BuildLogger logger = new NoBannerLogger();
95
96 logger.setMessageOutputLevel(org.apache.tools.ant.Project.MSG_INFO);
97 logger.setOutputPrintStream(System.out);
98 logger.setErrorPrintStream(System.err);
99
100 project.addBuildListener(logger);
101
102 project.init();
103 project.getBaseDir();
104 return project;
105 }
106
107 protected void setParent(Object parent, Object child) {
108 }
109
110 /***
111 * Determines, when the ANT Task that is represented by the "node" should perform.
112 * Node must be an ANT Task or no "perform" is called.
113 * If node is an ANT Task, it performs right after complete contstruction.
114 * If node is nested in a TaskContainer, calling "perform" is delegated to that
115 * TaskContainer.
116 * @param parent note: null when node is root
117 * @param node the node that now has all its children applied
118 */
119 protected void nodeCompleted(Object parent, Object node) {
120 if (parent instanceof TaskContainer) {
121 log.finest("parent is TaskContainer: no perform on nodeCompleted");
122 return;
123 }
124 if (node instanceof Task) {
125 Task task = (Task) node;
126 task.perform();
127 }
128 }
129
130 protected Object createNode(Object tagName) {
131 return createNode(tagName.toString(), Collections.EMPTY_MAP);
132 }
133
134 protected Object createNode(Object name, Object value) {
135 Object task = createNode(name);
136 setText(task, value.toString());
137 return task;
138 }
139
140 protected Object createNode(Object name, Map attributes, Object value) {
141 Object task = createNode(name, attributes);
142 setText(task, value.toString());
143 return task;
144 }
145
146 protected Object createNode(Object name, Map attributes) {
147
148 if (name.equals("fileScanner")) {
149 return new FileScanner(project);
150 }
151
152 String tagName = name.toString();
153 Object answer = null;
154
155 Object parentObject = getCurrent();
156 Object parentTask = getParentTask();
157
158
159
160
161
162
163
164
165 Object nested = null;
166 if (parentObject != null && !(parentTask instanceof TaskContainer)) {
167 nested = createNestedObject(parentObject, tagName);
168 }
169
170 Task task = null;
171 if (nested == null) {
172 task = createTask(tagName);
173 if (task != null) {
174 if (log.isLoggable(Level.FINE)) {
175 log.fine("Creating an ant Task for name: " + tagName);
176 }
177
178
179
180
181
182
183 if (task instanceof TaskAdapter) {
184 answer = ((TaskAdapter) task).getProxy();
185 }
186 else {
187 answer = task;
188 }
189
190
191 Object id = attributes.remove("id");
192 if (id != null) {
193 project.addReference((String) id, task);
194 }
195
196
197 task.init();
198
199
200 setBeanProperties(task, attributes);
201
202
203 if (parentObject instanceof TaskContainer){
204 ((TaskContainer)parentObject).addTask(task);
205 }
206 }
207 }
208
209 if (task == null) {
210 if (nested == null) {
211 if (log.isLoggable(Level.FINE)) {
212 log.fine("Trying to create a data type for tag: " + tagName);
213 }
214 nested = createDataType(tagName);
215 }
216 else {
217 if (log.isLoggable(Level.FINE)) {
218 log.fine("Created nested property tag: " + tagName);
219 }
220 }
221
222 if (nested != null) {
223 answer = nested;
224
225
226 Object id = attributes.remove("id");
227 if (id != null) {
228 project.addReference((String) id, nested);
229 }
230
231 try {
232 InvokerHelper.setProperty(nested, "name", tagName);
233 }
234 catch (Exception e) {
235 }
236
237
238 setBeanProperties(nested, attributes);
239
240
241 if (parentObject != null) {
242 IntrospectionHelper ih = IntrospectionHelper.getHelper(parentObject.getClass());
243 try {
244 if (log.isLoggable(Level.FINE)) {
245 log.fine(
246 "About to set the: "
247 + tagName
248 + " property on: "
249 + parentObject
250 + " to value: "
251 + nested
252 + " with type: "
253 + nested.getClass());
254 }
255
256 ih.storeElement(project, parentObject, nested, tagName);
257 }
258 catch (Exception e) {
259 log.log(Level.WARNING, "Caught exception setting nested: " + tagName, e);
260 }
261
262
263
264
265 try {
266 InvokerHelper.setProperty(parentObject, tagName, nested);
267 }
268 catch (Exception e) {
269 log.fine("Caught exception trying to set property: " + tagName + " on: " + parentObject);
270 }
271 }
272 }
273 else {
274 log.log(Level.WARNING, "Could not convert tag: " + tagName + " into an Ant task, data type or property. Maybe the task is not on the classpath?");
275 }
276 }
277
278 return answer;
279 }
280
281 protected void setText(Object task, String text) {
282
283 Method method = getAccessibleMethod(task.getClass(), "addText", addTaskParamTypes);
284 if (method != null) {
285 Object[] args = { text };
286 try {
287 method.invoke(task, args);
288 }
289 catch (Exception e) {
290 log.log(Level.WARNING, "Cannot call addText on: " + task + ". Reason: " + e, e);
291 }
292 }
293 }
294
295 protected Method getAccessibleMethod(Class theClass, String name, Class[] paramTypes) {
296 while (true) {
297 try {
298 Method answer = theClass.getDeclaredMethod(name, paramTypes);
299 if (answer != null) {
300 return answer;
301 }
302 }
303 catch (Exception e) {
304
305 }
306 theClass = theClass.getSuperclass();
307 if (theClass == null) {
308 return null;
309 }
310 }
311 }
312
313 public Project getAntProject() {
314 return project;
315 }
316
317
318
319 protected void setBeanProperties(Object object, Map map) {
320 for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
321 Map.Entry entry = (Map.Entry) iter.next();
322 String name = (String) entry.getKey();
323 Object value = entry.getValue();
324 setBeanProperty(object, name, ((value == null) ? null : value.toString()));
325 }
326 }
327
328 protected void setBeanProperty(Object object, String name, Object value) {
329 if (log.isLoggable(Level.FINE)) {
330 log.fine("Setting bean property on: " + object + " name: " + name + " value: " + value);
331 }
332
333 IntrospectionHelper ih = IntrospectionHelper.getHelper(object.getClass());
334
335 if (value instanceof String) {
336 try {
337 ih.setAttribute(getAntProject(), object, name.toLowerCase(), (String) value);
338 return;
339 }
340 catch (Exception e) {
341
342 }
343 }
344
345 try {
346
347 ih.storeElement(getAntProject(), object, value, name);
348 }
349 catch (Exception e) {
350
351 InvokerHelper.setProperty(object, name, value);
352 }
353 }
354
355 /***
356 * Creates a nested object of the given object with the specified name
357 */
358 protected Object createNestedObject(Object object, String name) {
359 Object dataType = null;
360 if (object != null) {
361 IntrospectionHelper ih = IntrospectionHelper.getHelper(object.getClass());
362
363 if (ih != null) {
364 try {
365
366
367 String namespaceUri = "";
368 UnknownElement unknownElement = null;
369 dataType = ih.getElementCreator(getAntProject(), namespaceUri, object, name.toLowerCase(), unknownElement).create();
370 }
371 catch (BuildException be) {
372 log.log(Level.SEVERE, "Caught: " + be, be);
373 }
374 }
375 }
376 if (dataType == null) {
377 dataType = createDataType(name);
378 }
379 return dataType;
380 }
381
382 protected Object createDataType(String name) {
383 Object dataType = null;
384
385 Class type = (Class) getAntProject().getDataTypeDefinitions().get(name);
386
387 if (type != null) {
388
389 Constructor ctor = null;
390 boolean noArg = false;
391
392
393
394 try {
395 ctor = type.getConstructor(new Class[0]);
396 noArg = true;
397 }
398 catch (NoSuchMethodException nse) {
399 try {
400 ctor = type.getConstructor(new Class[] { Project.class });
401 noArg = false;
402 }
403 catch (NoSuchMethodException nsme) {
404 log.log(Level.INFO, "datatype '" + name + "' didn't have a constructor with an Ant Project", nsme);
405 }
406 }
407
408 if (noArg) {
409 dataType = createDataType(ctor, new Object[0], name, "no-arg constructor");
410 }
411 else {
412 dataType = createDataType(ctor, new Object[] { getAntProject()}, name, "an Ant project");
413 }
414 if (dataType != null) {
415 ((DataType) dataType).setProject(getAntProject());
416 }
417 }
418
419 return dataType;
420 }
421
422 /***
423 * @return an object create with the given constructor and args.
424 * @param ctor a constructor to use creating the object
425 * @param args the arguments to pass to the constructor
426 * @param name the name of the data type being created
427 * @param argDescription a human readable description of the args passed
428 */
429 protected Object createDataType(Constructor ctor, Object[] args, String name, String argDescription) {
430 try {
431 Object datatype = ctor.newInstance(args);
432 return datatype;
433 }
434 catch (InstantiationException ie) {
435 log.log(Level.SEVERE, "datatype '" + name + "' couldn't be created with " + argDescription, ie);
436 }
437 catch (IllegalAccessException iae) {
438 log.log(Level.SEVERE, "datatype '" + name + "' couldn't be created with " + argDescription, iae);
439 }
440 catch (InvocationTargetException ite) {
441 log.log(Level.SEVERE, "datatype '" + name + "' couldn't be created with " + argDescription, ite);
442 }
443 return null;
444 }
445
446 /***
447 * @param taskName the name of the task to create
448 * @return a newly created task
449 */
450 protected Task createTask(String taskName) {
451 return createTask(taskName, (Class) getAntProject().getTaskDefinitions().get(taskName));
452 }
453
454 protected Task createTask(String taskName, Class taskType) {
455 if (taskType == null) {
456 return null;
457 }
458 try {
459 Object o = taskType.newInstance();
460 Task task = null;
461 if (o instanceof Task) {
462 task = (Task) o;
463 }
464 else {
465 TaskAdapter taskA = new TaskAdapter();
466 taskA.setProxy(o);
467 task = taskA;
468 }
469
470 task.setProject(getAntProject());
471 task.setTaskName(taskName);
472
473 return task;
474 }
475 catch (Exception e) {
476 log.log(Level.WARNING, "Could not create task: " + taskName + ". Reason: " + e, e);
477 return null;
478 }
479 }
480
481 protected Task getParentTask() {
482 Object current = getCurrent();
483 if (current instanceof Task) {
484 return (Task) current;
485 }
486 return null;
487 }
488 }