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.10 $
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 String objStr;
331 try {
332 objStr = object.toString();
333 } catch (Exception e) {
334 objStr = object.getClass().getName();
335 }
336 log.fine("Setting bean property on: " + objStr + " name: " + name + " value: " + value);
337 }
338
339 IntrospectionHelper ih = IntrospectionHelper.getHelper(object.getClass());
340
341 if (value instanceof String) {
342 try {
343 ih.setAttribute(getAntProject(), object, name.toLowerCase(), (String) value);
344 return;
345 }
346 catch (Exception e) {
347
348 }
349 }
350
351 try {
352
353 ih.storeElement(getAntProject(), object, value, name);
354 }
355 catch (Exception e) {
356
357 InvokerHelper.setProperty(object, name, value);
358 }
359 }
360
361 /***
362 * Creates a nested object of the given object with the specified name
363 */
364 protected Object createNestedObject(Object object, String name) {
365 Object dataType = null;
366 if (object != null) {
367 IntrospectionHelper ih = IntrospectionHelper.getHelper(object.getClass());
368
369 if (ih != null) {
370 try {
371
372
373 String namespaceUri = "";
374 UnknownElement unknownElement = null;
375 dataType = ih.getElementCreator(getAntProject(), namespaceUri, object, name.toLowerCase(), unknownElement).create();
376 }
377 catch (BuildException be) {
378 log.log(Level.SEVERE, "Caught: " + be, be);
379 }
380 }
381 }
382 if (dataType == null) {
383 dataType = createDataType(name);
384 }
385 return dataType;
386 }
387
388 protected Object createDataType(String name) {
389 Object dataType = null;
390
391 Class type = (Class) getAntProject().getDataTypeDefinitions().get(name);
392
393 if (type != null) {
394
395 Constructor ctor = null;
396 boolean noArg = false;
397
398
399
400 try {
401 ctor = type.getConstructor(new Class[0]);
402 noArg = true;
403 }
404 catch (NoSuchMethodException nse) {
405 try {
406 ctor = type.getConstructor(new Class[] { Project.class });
407 noArg = false;
408 }
409 catch (NoSuchMethodException nsme) {
410 log.log(Level.INFO, "datatype '" + name + "' didn't have a constructor with an Ant Project", nsme);
411 }
412 }
413
414 if (noArg) {
415 dataType = createDataType(ctor, new Object[0], name, "no-arg constructor");
416 }
417 else {
418 dataType = createDataType(ctor, new Object[] { getAntProject()}, name, "an Ant project");
419 }
420 if (dataType != null) {
421 ((DataType) dataType).setProject(getAntProject());
422 }
423 }
424
425 return dataType;
426 }
427
428 /***
429 * @return an object create with the given constructor and args.
430 * @param ctor a constructor to use creating the object
431 * @param args the arguments to pass to the constructor
432 * @param name the name of the data type being created
433 * @param argDescription a human readable description of the args passed
434 */
435 protected Object createDataType(Constructor ctor, Object[] args, String name, String argDescription) {
436 try {
437 Object datatype = ctor.newInstance(args);
438 return datatype;
439 }
440 catch (InstantiationException ie) {
441 log.log(Level.SEVERE, "datatype '" + name + "' couldn't be created with " + argDescription, ie);
442 }
443 catch (IllegalAccessException iae) {
444 log.log(Level.SEVERE, "datatype '" + name + "' couldn't be created with " + argDescription, iae);
445 }
446 catch (InvocationTargetException ite) {
447 log.log(Level.SEVERE, "datatype '" + name + "' couldn't be created with " + argDescription, ite);
448 }
449 return null;
450 }
451
452 /***
453 * @param taskName the name of the task to create
454 * @return a newly created task
455 */
456 protected Task createTask(String taskName) {
457 return createTask(taskName, (Class) getAntProject().getTaskDefinitions().get(taskName));
458 }
459
460 protected Task createTask(String taskName, Class taskType) {
461 if (taskType == null) {
462 return null;
463 }
464 try {
465 Object o = taskType.newInstance();
466 Task task = null;
467 if (o instanceof Task) {
468 task = (Task) o;
469 }
470 else {
471 TaskAdapter taskA = new TaskAdapter();
472 taskA.setProxy(o);
473 task = taskA;
474 }
475
476 task.setProject(getAntProject());
477 task.setTaskName(taskName);
478
479 return task;
480 }
481 catch (Exception e) {
482 log.log(Level.WARNING, "Could not create task: " + taskName + ". Reason: " + e, e);
483 return null;
484 }
485 }
486
487 protected Task getParentTask() {
488 Object current = getCurrent();
489 if (current instanceof Task) {
490 return (Task) current;
491 }
492 return null;
493 }
494 }