1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jelly.tags.ant;
18
19 import java.lang.reflect.Constructor;
20 import java.lang.reflect.InvocationTargetException;
21 import java.lang.reflect.Method;
22 import java.util.Iterator;
23 import java.util.Map;
24
25 import org.apache.commons.beanutils.BeanUtils;
26 import org.apache.commons.beanutils.MethodUtils;
27 import org.apache.commons.jelly.JellyTagException;
28 import org.apache.commons.jelly.MapTagSupport;
29 import org.apache.commons.jelly.Tag;
30 import org.apache.commons.jelly.XMLOutput;
31 import org.apache.commons.jelly.expression.Expression;
32 import org.apache.commons.jelly.impl.BeanSource;
33 import org.apache.commons.jelly.impl.StaticTag;
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.apache.tools.ant.BuildException;
37 import org.apache.tools.ant.IntrospectionHelper;
38 import org.apache.tools.ant.Project;
39 import org.apache.tools.ant.Task;
40 import org.apache.tools.ant.TaskAdapter;
41 import org.apache.tools.ant.TaskContainer;
42 import org.apache.tools.ant.types.DataType;
43
44 /***
45 * Tag supporting ant's Tasks as well as
46 * dynamic runtime behaviour for 'unknown' tags.
47 *
48 * @author <a href="mailto:bob@eng.werken.com">bob mcwhirter</a>
49 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
50 */
51 public class AntTag extends MapTagSupport implements TaskSource {
52
53 /*** The Log to which logging calls will be made. */
54 private static final Log log = LogFactory.getLog(AntTag.class);
55
56 private static final Class[] addTaskParamTypes = { String.class };
57
58 /*** store the name of the manifest tag for special handling */
59 private static final String ANT_MANIFEST_TAG = "manifest";
60
61 /*** The name of this tag. */
62 protected String tagName;
63
64 /*** The general object underlying this tag. */
65 protected Object object;
66
67 /*** Task, if this tag represents a task. */
68 protected Task task;
69
70
71 /*** Construct with a project and tag name.
72 *
73 * @param tagName The name on the tag.
74 */
75 public AntTag(String tagName) {
76 this.tagName = tagName;
77 }
78
79 public String toString() {
80 return "[AntTag: name=" + getTagName() + "]";
81 }
82
83
84
85
86 /*** Retrieve the general object underlying this tag.
87 *
88 * @return The object underlying this tag.
89 */
90 public Object getTaskObject() {
91 return this.object;
92 }
93
94 /***
95 * Allows nested tags to set a property on the task object of this tag
96 */
97 public void setTaskProperty(String name, Object value) throws JellyTagException {
98 Object object = getTaskObject();
99 if ( object != null ) {
100 setBeanProperty( object, name, value );
101 }
102 }
103
104
105
106 public void doTag(XMLOutput output) throws JellyTagException {
107
108 Project project = getAntProject();
109 String tagName = getTagName();
110 Object parentObject = findBeanAncestor();
111 Object parentTask = findParentTaskObject();
112
113
114
115
116
117
118
119
120 Object nested = null;
121 if (parentObject != null && !( parentTask instanceof TaskContainer) ) {
122 nested = createNestedObject( parentObject, tagName );
123 }
124
125 if (nested == null) {
126 task = createTask( tagName );
127
128 if (task != null) {
129
130 if ( log.isDebugEnabled() ) {
131 log.debug( "Creating an ant Task for name: " + tagName );
132 }
133
134
135
136
137
138
139 if ( task instanceof TaskAdapter ) {
140 setObject( ((TaskAdapter)task).getProxy() );
141 }
142 else {
143 setObject( task );
144 }
145
146
147 Object id = getAttributes().remove( "id" );
148 if ( id != null ) {
149 project.addReference( (String) id, task );
150 }
151
152
153
154
155 task.init();
156
157
158 String body = getBodyText();
159
160
161 setBeanProperties();
162
163
164 Method method = MethodUtils.getAccessibleMethod( task.getClass(),
165 "addText",
166 addTaskParamTypes );
167 if (method != null) {
168 Object[] args = { body };
169 try {
170 method.invoke(this.task, args);
171 }
172 catch (IllegalAccessException e) {
173 throw new JellyTagException(e);
174 }
175 catch (InvocationTargetException e) {
176 throw new JellyTagException(e);
177 }
178 }
179
180
181
182
183
184
185 task.perform();
186 }
187 }
188
189 if (task == null) {
190
191 if (nested == null) {
192
193 if ( log.isDebugEnabled() ) {
194 log.debug( "Trying to create a data type for tag: " + tagName );
195 }
196 nested = createDataType( tagName );
197 }
198 else {
199 if ( log.isDebugEnabled() ) {
200 log.debug( "Created nested property tag: " + tagName );
201 }
202 }
203
204 if ( nested != null ) {
205 setObject( nested );
206
207
208 Object id = getAttributes().remove( "id" );
209 if ( id != null ) {
210 project.addReference( (String) id, nested );
211 }
212
213
214
215
216
217
218
219
220
221
222
223 String body = getBodyText();
224
225
226 setBeanProperties();
227
228
229 if ( parentObject != null ) {
230 IntrospectionHelper ih = IntrospectionHelper.getHelper( parentObject.getClass() );
231 try {
232 if (log.isDebugEnabled()) {
233 log.debug("About to set the: " + tagName
234 + " property on: " + parentObject + " to value: "
235 + nested + " with type: " + nested.getClass()
236 );
237 }
238
239 ih.storeElement( project, parentObject, nested, tagName.toLowerCase() );
240 }
241 catch (Exception e) {
242 log.warn( "Caught exception setting nested: " + tagName, e );
243 }
244
245
246
247
248 try {
249 BeanUtils.setProperty( parentObject, tagName, nested );
250 }
251 catch (Exception e) {
252 log.debug("Caught exception trying to set property: " + tagName + " on: " + parentObject);
253 }
254 }
255 }
256 else {
257 log.warn("Could not convert tag: " + tagName + " into an Ant task, data type or property");
258
259
260 StaticTag tag = new StaticTag("", tagName, tagName);
261 tag.setParent( getParent() );
262 tag.setBody( getBody() );
263
264 tag.setContext(context);
265
266 for (Iterator iter = getAttributes().entrySet().iterator(); iter.hasNext();) {
267 Map.Entry entry = (Map.Entry) iter.next();
268 String name = (String) entry.getKey();
269 Object value = entry.getValue();
270
271 tag.setAttribute(name, value);
272 }
273
274 tag.doTag(output);
275 }
276 }
277 }
278
279
280
281
282 public String getTagName() {
283 return this.tagName;
284 }
285
286 /*** Set the object underlying this tag.
287 *
288 * @param object The object.
289 */
290 public void setObject(Object object) {
291 this.object = object;
292 }
293
294 public Project getAntProject() {
295 Project project = AntTagLibrary.getProject(context);
296 if (project == null) {
297 throw new NullPointerException("No Ant Project object is available");
298 }
299 return project;
300 }
301
302
303
304
305 /***
306 * Sets the properties on the Ant task
307 */
308 public void setBeanProperties() throws JellyTagException {
309 Object object = getTaskObject();
310 if ( object != null ) {
311 Map map = getAttributes();
312 for ( Iterator iter = map.entrySet().iterator(); iter.hasNext(); ) {
313 Map.Entry entry = (Map.Entry) iter.next();
314 String name = (String) entry.getKey();
315 Object value = entry.getValue();
316 setBeanProperty( object, name, value );
317 }
318 }
319 }
320
321 public void setAttribute(String name, Object value) {
322 if ( value == null ) {
323
324 super.setAttribute( name, "" );
325 }
326 else {
327 if ( value instanceof Expression )
328 {
329 super.setAttribute( name, ((Expression) value).evaluateRecurse(context) );
330 }
331 else
332 {
333 super.setAttribute( name, value.toString() );
334 }
335 }
336 }
337
338 public void setBeanProperty(Object object, String name, Object value) throws JellyTagException {
339 if ( log.isDebugEnabled() ) {
340 log.debug( "Setting bean property on: "+ object + " name: " + name + " value: " + value );
341 }
342
343 IntrospectionHelper ih = IntrospectionHelper.getHelper( object.getClass() );
344
345 if ( value instanceof String ) {
346 try {
347 ih.setAttribute( getAntProject(), object, name.toLowerCase(), (String) value );
348 return;
349 }
350 catch (Exception e) {
351
352 }
353 }
354
355 try {
356
357 ih.storeElement( getAntProject(), object, value, name );
358 }
359 catch (Exception e) {
360
361 try {
362
363 BeanUtils.setProperty( object, name, value );
364 }
365 catch (IllegalAccessException ex) {
366 throw new JellyTagException(ex);
367 }
368 catch (InvocationTargetException ex) {
369 throw new JellyTagException(ex);
370 }
371 }
372 }
373
374
375 /***
376 * Creates a nested object of the given object with the specified name
377 */
378 public Object createNestedObject(Object object, String name) {
379 Object dataType = null;
380 if ( object != null ) {
381 IntrospectionHelper ih = IntrospectionHelper.getHelper( object.getClass() );
382
383 if ( ih != null ) {
384 try {
385 dataType = ih.createElement( getAntProject(), object, name.toLowerCase() );
386 } catch (BuildException be) {
387 if (object instanceof Tag)
388 {
389 if (log.isDebugEnabled()) {
390 log.debug("Failed attempt to create an ant datatype for a jelly tag", be);
391 }
392 } else {
393 log.error(be);
394 }
395 }
396 }
397 }
398
399 if ( dataType == null ) {
400 dataType = createDataType( name );
401 }
402
403 return dataType;
404 }
405
406 public Object createDataType(String name) {
407
408 Object dataType = null;
409
410 Class type = (Class) getAntProject().getDataTypeDefinitions().get(name);
411
412 if ( type != null ) {
413
414 Constructor ctor = null;
415 boolean noArg = false;
416
417
418
419 try {
420 ctor = type.getConstructor(new Class[0]);
421 noArg = true;
422 }
423 catch (NoSuchMethodException nse) {
424 try {
425 ctor = type.getConstructor(new Class[] { Project.class });
426 noArg = false;
427 } catch (NoSuchMethodException nsme) {
428 log.info("datatype '" + name
429 + "' didn't have a constructor with an Ant Project", nsme);
430 }
431 }
432
433 if (noArg) {
434 dataType = createDataType(ctor, new Object[0], name, "no-arg constructor");
435 }
436 else {
437 dataType = createDataType(ctor, new Object[] { getAntProject() }, name, "an Ant project");
438 }
439 if (dataType != null) {
440 ((DataType)dataType).setProject( getAntProject() );
441 }
442 }
443
444 return dataType;
445 }
446
447 /***
448 * @return an object create with the given constructor and args.
449 * @param ctor a constructor to use creating the object
450 * @param args the arguments to pass to the constructor
451 * @param name the name of the data type being created
452 * @param argDescription a human readable description of the args passed
453 */
454 private Object createDataType(Constructor ctor, Object[] args, String name, String argDescription) {
455 try {
456 Object datatype = ctor.newInstance(args);
457 return datatype;
458 } catch (InstantiationException ie) {
459 log.error("datatype '" + name + "' couldn't be created with " + argDescription, ie);
460 } catch (IllegalAccessException iae) {
461 log.error("datatype '" + name + "' couldn't be created with " + argDescription, iae);
462 } catch (InvocationTargetException ite) {
463 log.error("datatype '" + name + "' couldn't be created with " + argDescription, ite);
464 }
465 return null;
466 }
467
468 /***
469 * @param taskName
470 * @return
471 * @throws JellyTagException
472 */
473 public Task createTask(String taskName) throws JellyTagException {
474 return createTask( taskName,
475 (Class) getAntProject().getTaskDefinitions().get( taskName ) );
476 }
477
478 public Task createTask(String taskName,
479 Class taskType) throws JellyTagException {
480
481 if (taskType == null) {
482 return null;
483 }
484
485 Object o = null;
486 try {
487 o = taskType.newInstance();
488 } catch (InstantiationException e) {
489 throw new JellyTagException(e);
490 }
491 catch (IllegalAccessException e) {
492 throw new JellyTagException(e);
493 }
494
495 Task task = null;
496 if ( o instanceof Task ) {
497 task = (Task) o;
498 }
499 else {
500 TaskAdapter taskA=new TaskAdapter();
501 taskA.setProxy( o );
502 task=taskA;
503 }
504
505 task.setProject(getAntProject());
506 task.setTaskName(taskName);
507
508 return task;
509 }
510
511 /***
512 * Attempts to look up in the parent hierarchy for a tag that implements the
513 * TaskSource interface, which returns an Ant Task object or that implements
514 * BeanSource interface which creates a bean,
515 * or will return the parent tag, which is also a bean.
516 */
517 protected Object findBeanAncestor() throws JellyTagException {
518 Tag tag = getParent();
519 while (tag != null) {
520 if (tag instanceof BeanSource) {
521 BeanSource beanSource = (BeanSource) tag;
522 return beanSource.getBean();
523 }
524 if (tag instanceof TaskSource) {
525 TaskSource taskSource = (TaskSource) tag;
526 return taskSource.getTaskObject();
527 }
528 tag = tag.getParent();
529 }
530 return getParent();
531 }
532
533 /***
534 * Walks the hierarchy until it finds a parent TaskSource and returns its source or returns null
535 */
536 protected Object findParentTaskObject() throws JellyTagException {
537 Tag tag = getParent();
538 while (tag != null) {
539 if (tag instanceof TaskSource) {
540 TaskSource source = (TaskSource) tag;
541 return source.getTaskObject();
542 }
543 tag = tag.getParent();
544 }
545 return null;
546 }
547
548 }