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