KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > IntrospectionHelper


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18
19 package org.apache.tools.ant;
20
21 import java.lang.reflect.Constructor JavaDoc;
22 import java.lang.reflect.InvocationTargetException JavaDoc;
23 import java.lang.reflect.Method JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Collections JavaDoc;
26 import java.util.Enumeration JavaDoc;
27 import java.util.Hashtable JavaDoc;
28 import java.util.HashMap JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Locale JavaDoc;
31 import java.util.Map JavaDoc;
32 import org.apache.tools.ant.types.EnumeratedAttribute;
33 import org.apache.tools.ant.taskdefs.PreSetDef;
34
35 /**
36  * Helper class that collects the methods a task or nested element
37  * holds to set attributes, create nested elements or hold PCDATA
38  * elements.
39  * The class is final as it has a private constructor.
40  */

41 public final class IntrospectionHelper {
42
43     /**
44      * EMPTY_MAP was added in java 1.3 (EMPTY_SET and EMPTY_LIST
45      * is in java 1.2!)
46      */

47     private static final Map JavaDoc EMPTY_MAP
48         = Collections.unmodifiableMap(new HashMap JavaDoc(0));
49
50     /**
51      * Helper instances we've already created (Class.getName() to IntrospectionHelper).
52      */

53     private static final Map JavaDoc HELPERS = new Hashtable JavaDoc();
54
55     /**
56      * Map from primitive types to wrapper classes for use in
57      * createAttributeSetter (Class to Class). Note that char
58      * and boolean are in here even though they get special treatment
59      * - this way we only need to test for the wrapper class.
60      */

61     private static final Map JavaDoc PRIMITIVE_TYPE_MAP = new HashMap JavaDoc(8);
62
63     // Set up PRIMITIVE_TYPE_MAP
64
static {
65         Class JavaDoc[] primitives = {Boolean.TYPE, Byte.TYPE, Character.TYPE,
66                               Short.TYPE, Integer.TYPE, Long.TYPE,
67                               Float.TYPE, Double.TYPE};
68         Class JavaDoc[] wrappers = {Boolean JavaDoc.class, Byte JavaDoc.class, Character JavaDoc.class,
69                             Short JavaDoc.class, Integer JavaDoc.class, Long JavaDoc.class,
70                             Float JavaDoc.class, Double JavaDoc.class};
71         for (int i = 0; i < primitives.length; i++) {
72             PRIMITIVE_TYPE_MAP.put (primitives[i], wrappers[i]);
73         }
74     }
75
76     private static final int MAX_REPORT_NESTED_TEXT = 20;
77     private static final String JavaDoc ELLIPSIS = "...";
78
79     /**
80      * Map from attribute names to attribute types
81      * (String to Class).
82      */

83     private Hashtable JavaDoc attributeTypes = new Hashtable JavaDoc();
84
85     /**
86      * Map from attribute names to attribute setter methods
87      * (String to AttributeSetter).
88      */

89     private Hashtable JavaDoc attributeSetters = new Hashtable JavaDoc();
90
91     /**
92      * Map from attribute names to nested types
93      * (String to Class).
94      */

95     private Hashtable JavaDoc nestedTypes = new Hashtable JavaDoc();
96
97     /**
98      * Map from attribute names to methods to create nested types
99      * (String to NestedCreator).
100      */

101     private Hashtable JavaDoc nestedCreators = new Hashtable JavaDoc();
102
103     /**
104      * Vector of methods matching add[Configured](Class) pattern.
105      */

106     private List JavaDoc addTypeMethods = new ArrayList JavaDoc();
107
108     /**
109      * The method to invoke to add PCDATA.
110      */

111     private Method JavaDoc addText = null;
112
113     /**
114      * The class introspected by this instance.
115      */

116     private Class JavaDoc bean;
117
118     /**
119      * Sole constructor, which is private to ensure that all
120      * IntrospectionHelpers are created via {@link #getHelper(Class) getHelper}.
121      * Introspects the given class for bean-like methods.
122      * Each method is examined in turn, and the following rules are applied:
123      * <p>
124      * <ul>
125      * <li>If the method is <code>Task.setLocation(Location)</code>,
126      * <code>Task.setTaskType(String)</code>
127      * or <code>TaskContainer.addTask(Task)</code>, it is ignored. These
128      * methods are handled differently elsewhere.
129      * <li><code>void addText(String)</code> is recognised as the method for
130      * adding PCDATA to a bean.
131      * <li><code>void setFoo(Bar)</code> is recognised as a method for
132      * setting the value of attribute <code>foo</code>, so long as
133      * <code>Bar</code> is non-void and is not an array type. Non-String
134      * parameter types always overload String parameter types, but that is
135      * the only guarantee made in terms of priority.
136      * <li><code>Foo createBar()</code> is recognised as a method for
137      * creating a nested element called <code>bar</code> of type
138      * <code>Foo</code>, so long as <code>Foo</code> is not a primitive or
139      * array type.
140      * <li><code>void addConfiguredFoo(Bar)</code> is recognised as a
141      * method for storing a pre-configured element called
142      * <code>foo</code> and of type <code>Bar</code>, so long as
143      * <code>Bar</code> is not an array, primitive or String type.
144      * <code>Bar</code> must have an accessible constructor taking no
145      * arguments.
146      * <li><code>void addFoo(Bar)</code> is recognised as a method for storing
147      * an element called <code>foo</code> and of type <code>Bar</code>, so
148      * long as <code>Bar</code> is not an array, primitive or String type.
149      * <code>Bar</code> must have an accessible constructor taking no
150      * arguments. This is distinct from the 'addConfigured' idiom in that
151      * the nested element is added to the parent immediately after it is
152      * constructed; in practice this means that <code>addFoo(Bar)</code> should
153      * do little or nothing with its argument besides storing it for later use.
154      * </ul>
155      * Note that only one method is retained to create/set/addConfigured/add
156      * any element or attribute.
157      *
158      * @param bean The bean type to introspect.
159      * Must not be <code>null</code>.
160      *
161      * @see #getHelper(Class)
162      */

163     private IntrospectionHelper(final Class JavaDoc bean) {
164         this.bean = bean;
165         Method JavaDoc[] methods = bean.getMethods();
166         for (int i = 0; i < methods.length; i++) {
167             final Method JavaDoc m = methods[i];
168             final String JavaDoc name = m.getName();
169             Class JavaDoc returnType = m.getReturnType();
170             Class JavaDoc[] args = m.getParameterTypes();
171
172             // check of add[Configured](Class) pattern
173
if (args.length == 1 && java.lang.Void.TYPE.equals(returnType)
174                 && ("add".equals(name) || "addConfigured".equals(name))) {
175                 insertAddTypeMethod(m);
176                 continue;
177             }
178             // not really user settable properties on tasks/project components
179
if (org.apache.tools.ant.ProjectComponent.class.isAssignableFrom(
180                     bean)
181                  && args.length == 1 && isHiddenSetMethod(name, args[0])) {
182                 continue;
183             }
184             // hide addTask for TaskContainers
185
if (isContainer() && args.length == 1 && "addTask".equals(name)
186                 && org.apache.tools.ant.Task.class.equals(args[0])) {
187                 continue;
188             }
189             if ("addText".equals(name) && java.lang.Void.TYPE.equals(returnType)
190                 && args.length == 1 && java.lang.String JavaDoc.class.equals(args[0])) {
191
192                 addText = methods[i];
193             } else if (name.startsWith("set")
194                        && java.lang.Void.TYPE.equals(returnType)
195                        && args.length == 1 && !args[0].isArray()) {
196                 String JavaDoc propName = getPropertyName(name, "set");
197                 if (attributeSetters.get(propName) != null) {
198                     if (java.lang.String JavaDoc.class.equals(args[0])) {
199                         /*
200                             Ignore method m, as there is an overloaded
201                             form of this method that takes in a
202                             non-string argument, which gains higher
203                             priority.
204                         */

205                         continue;
206                     }
207                     /*
208                         If the argument is not a String or Location,
209                         and if there
210                         is an overloaded form of this method already defined,
211                         we just override that with the new one.
212                         This mechanism does not guarantee any specific order
213                         in which the methods will be selected: so any code
214                         that depends on the order in which "set" methods have
215                         been defined, is not guaranteed to be selected in any
216                         particular order.
217                     */

218                 }
219                 AttributeSetter as = createAttributeSetter(m, args[0], propName);
220                 if (as != null) {
221                     attributeTypes.put(propName, args[0]);
222                     attributeSetters.put(propName, as);
223                 }
224             } else if (name.startsWith("create") && !returnType.isArray()
225                        && !returnType.isPrimitive() && args.length == 0) {
226
227                 String JavaDoc propName = getPropertyName(name, "create");
228                 // Check if a create of this property is already present
229
// add takes preference over create for CB purposes
230
if (nestedCreators.get(propName) == null) {
231                     nestedTypes.put(propName, returnType);
232                     nestedCreators.put(propName, new CreateNestedCreator(m));
233                 }
234             } else if (name.startsWith("addConfigured")
235                 && java.lang.Void.TYPE.equals(returnType) && args.length == 1
236                 && !java.lang.String JavaDoc.class.equals(args[0])
237                 && !args[0].isArray() && !args[0].isPrimitive()) {
238                 try {
239                     Constructor JavaDoc constructor = null;
240                     try {
241                         constructor = args[0].getConstructor(new Class JavaDoc[] {});
242                     } catch (NoSuchMethodException JavaDoc ex) {
243                         constructor =
244                             args[0].getConstructor(new Class JavaDoc[] {Project.class});
245                     }
246                     String JavaDoc propName = getPropertyName(name, "addConfigured");
247                     nestedTypes.put(propName, args[0]);
248                     nestedCreators.put(propName, new AddNestedCreator(m,
249                         constructor, AddNestedCreator.ADD_CONFIGURED));
250                 } catch (NoSuchMethodException JavaDoc nse) {
251                     // ignore
252
}
253             } else if (name.startsWith("add")
254                 && java.lang.Void.TYPE.equals(returnType) && args.length == 1
255                 && !java.lang.String JavaDoc.class.equals(args[0])
256                 && !args[0].isArray() && !args[0].isPrimitive()) {
257                 try {
258                     Constructor JavaDoc constructor = null;
259                     try {
260                         constructor = args[0].getConstructor(new Class JavaDoc[] {});
261                     } catch (NoSuchMethodException JavaDoc ex) {
262                         constructor =
263                             args[0].getConstructor(new Class JavaDoc[] {Project.class});
264                     }
265
266                     String JavaDoc propName = getPropertyName(name, "add");
267                     if (nestedTypes.get(propName) != null) {
268                         /*
269                          * Ignore this method as there is an addConfigured
270                          * form of this method that has a higher
271                          * priority
272                          */

273                         continue;
274                     }
275                     nestedTypes.put(propName, args[0]);
276                     nestedCreators.put(propName, new AddNestedCreator(m,
277                         constructor, AddNestedCreator.ADD));
278                 } catch (NoSuchMethodException JavaDoc nse) {
279                     // ignore
280
}
281             }
282         }
283     }
284
285     /**
286      * Certain set methods are part of the Ant core interface to tasks and
287      * therefore not to be considered for introspection
288      *
289      * @param name the name of the set method
290      * @param type the type of the set method's parameter
291      * @return true if the given set method is to be hidden.
292      */

293     private boolean isHiddenSetMethod(String JavaDoc name, Class JavaDoc type) {
294         if ("setLocation".equals(name)
295              && org.apache.tools.ant.Location.class.equals(type)) {
296             return true;
297         }
298
299         if ("setTaskType".equals(name)
300              && java.lang.String JavaDoc.class.equals(type)) {
301             return true;
302         }
303
304         return false;
305     }
306
307     /**
308      * Returns a helper for the given class, either from the cache
309      * or by creating a new instance.
310      *
311      * @param c The class for which a helper is required.
312      * Must not be <code>null</code>.
313      *
314      * @return a helper for the specified class
315      */

316     public static synchronized IntrospectionHelper getHelper(Class JavaDoc c) {
317         return getHelper(null, c);
318     }
319
320     /**
321      * Returns a helper for the given class, either from the cache
322      * or by creating a new instance.
323      *
324      * The method will make sure the helper will be cleaned up at the end of
325      * the project, and only one instance will be created for each class.
326      *
327      * @param p the project instance.
328      * @param c The class for which a helper is required.
329      * Must not be <code>null</code>.
330      *
331      * @return a helper for the specified class
332      */

333     public static IntrospectionHelper getHelper(Project p, Class JavaDoc c) {
334         IntrospectionHelper ih = (IntrospectionHelper) HELPERS.get(c.getName());
335         // If a helper cannot be found, or if the helper is for another
336
// classloader, create a new IH
337
if (ih == null || ih.bean != c) {
338             ih = new IntrospectionHelper(c);
339             if (p != null) {
340                 // #30162: do *not* cache this if there is no project, as we
341
// cannot guarantee that the cache will be cleared.
342
HELPERS.put(c.getName(), ih);
343             }
344         }
345         return ih;
346     }
347
348     /**
349      * Sets the named attribute in the given element, which is part of the
350      * given project.
351      *
352      * @param p The project containing the element. This is used when files
353      * need to be resolved. Must not be <code>null</code>.
354      * @param element The element to set the attribute in. Must not be
355      * <code>null</code>.
356      * @param attributeName The name of the attribute to set. Must not be
357      * <code>null</code>.
358      * @param value The value to set the attribute to. This may be interpreted
359      * or converted to the necessary type if the setter method
360      * doesn't just take a string. Must not be <code>null</code>.
361      *
362      * @exception BuildException if the introspected class doesn't support
363      * the given attribute, or if the setting
364      * method fails.
365      */

366     public void setAttribute(Project p, Object JavaDoc element, String JavaDoc attributeName,
367                              String JavaDoc value) throws BuildException {
368         AttributeSetter as
369             = (AttributeSetter) attributeSetters.get(
370                 attributeName.toLowerCase(Locale.US));
371         if (as == null) {
372             if (element instanceof DynamicAttributeNS) {
373                 DynamicAttributeNS dc = (DynamicAttributeNS) element;
374                 String JavaDoc uriPlusPrefix =
375                     ProjectHelper.extractUriFromComponentName(attributeName);
376                 String JavaDoc uri =
377                     ProjectHelper.extractUriFromComponentName(uriPlusPrefix);
378                 String JavaDoc localName =
379                     ProjectHelper.extractNameFromComponentName(attributeName);
380                 String JavaDoc qName = ("".equals(uri)
381                                 ? localName : (uri + ":" + localName));
382
383                 dc.setDynamicAttribute(uri, localName, qName, value);
384                 return;
385             } else if (element instanceof DynamicAttribute) {
386                 DynamicAttribute dc = (DynamicAttribute) element;
387                 dc.setDynamicAttribute(attributeName.toLowerCase(Locale.US), value);
388                 return;
389             } else {
390                 if (attributeName.indexOf(':') != -1) {
391                     return; // Ignore attribute from unknown uri's
392
}
393                 String JavaDoc msg = getElementName(p, element)
394                     + " doesn't support the \"" + attributeName
395                     + "\" attribute.";
396                 throw new UnsupportedAttributeException(msg, attributeName);
397             }
398         }
399         try {
400             as.set(p, element, value);
401         } catch (IllegalAccessException JavaDoc ie) {
402             // impossible as getMethods should only return public methods
403
throw new BuildException(ie);
404         } catch (InvocationTargetException JavaDoc ite) {
405             Throwable JavaDoc t = ite.getTargetException();
406             if (t instanceof BuildException) {
407                 throw (BuildException) t;
408             }
409             throw new BuildException(t);
410         }
411     }
412
413
414     /**
415      * Adds PCDATA to an element, using the element's
416      * <code>void addText(String)</code> method, if it has one. If no
417      * such method is present, a BuildException is thrown if the
418      * given text contains non-whitespace.
419      *
420      * @param project The project which the element is part of.
421      * Must not be <code>null</code>.
422      * @param element The element to add the text to.
423      * Must not be <code>null</code>.
424      * @param text The text to add.
425      * Must not be <code>null</code>.
426      *
427      * @exception BuildException if non-whitespace text is provided and no
428      * method is available to handle it, or if
429      * the handling method fails.
430      */

431     public void addText(Project project, Object JavaDoc element, String JavaDoc text)
432         throws BuildException {
433         if (addText == null) {
434             text = text.trim();
435             // Element doesn't handle text content
436
if (text.length() == 0) {
437                 // Only whitespace - ignore
438
return;
439             } else {
440                 // Not whitespace - fail
441
String JavaDoc msg = project.getElementName(element)
442                     + " doesn't support nested text data (\""
443                     + condenseText(text) + "\").";
444                 throw new BuildException(msg);
445             }
446         }
447         try {
448             addText.invoke(element, new Object JavaDoc[] {text});
449         } catch (IllegalAccessException JavaDoc ie) {
450             // impossible as getMethods should only return public methods
451
throw new BuildException(ie);
452         } catch (InvocationTargetException JavaDoc ite) {
453             Throwable JavaDoc t = ite.getTargetException();
454             if (t instanceof BuildException) {
455                 throw (BuildException) t;
456             }
457             throw new BuildException(t);
458         }
459     }
460
461     /**
462      * Utility method to throw a NotSupported exception
463      *
464      * @param project the Project instance.
465      * @param parent the object which doesn't support a requested element
466      * @param elementName the name of the Element which is trying to be created.
467      */

468     public void throwNotSupported(Project project, Object JavaDoc parent,
469         String JavaDoc elementName) {
470         String JavaDoc msg = project.getElementName(parent)
471             + " doesn't support the nested \"" + elementName + "\" element.";
472         throw new UnsupportedElementException(msg, elementName);
473     }
474
475     private NestedCreator getNestedCreator(
476         Project project, String JavaDoc parentUri, Object JavaDoc parent,
477         String JavaDoc elementName, UnknownElement child) throws BuildException {
478
479         String JavaDoc uri = ProjectHelper.extractUriFromComponentName(elementName);
480         String JavaDoc name = ProjectHelper.extractNameFromComponentName(elementName);
481
482         if (uri.equals(ProjectHelper.ANT_CORE_URI)) {
483             uri = "";
484         }
485         if (parentUri.equals(ProjectHelper.ANT_CORE_URI)) {
486             parentUri = "";
487         }
488         NestedCreator nc = null;
489         if (uri.equals(parentUri) || uri.equals("")) {
490             nc = (NestedCreator) nestedCreators.get(
491                 name.toLowerCase(Locale.US));
492         }
493         if (nc == null) {
494             nc = createAddTypeCreator(project, parent, elementName);
495         }
496         if (nc == null && parent instanceof DynamicElementNS) {
497             DynamicElementNS dc = (DynamicElementNS) parent;
498             String JavaDoc qName = (child == null ? name : child.getQName());
499             final Object JavaDoc nestedElement =
500                 dc.createDynamicElement(
501                     (child == null ? "" : child.getNamespace()),
502                     name, qName);
503             if (nestedElement != null) {
504                 nc = new NestedCreator(null) {
505                     Object JavaDoc create(
506                         Project project, Object JavaDoc parent, Object JavaDoc ignore) {
507                         return nestedElement;
508                     }
509                 };
510             }
511         }
512         if (nc == null && parent instanceof DynamicElement) {
513             DynamicElement dc = (DynamicElement) parent;
514             final Object JavaDoc nestedElement =
515                 dc.createDynamicElement(name.toLowerCase(Locale.US));
516             if (nestedElement != null) {
517                 nc = new NestedCreator(null) {
518                     Object JavaDoc create(
519                         Project project, Object JavaDoc parent, Object JavaDoc ignore) {
520                         return nestedElement;
521                     }
522                 };
523             }
524         }
525         if (nc == null) {
526             throwNotSupported(project, parent, elementName);
527         }
528         return nc;
529     }
530
531     /**
532      * Creates a named nested element. Depending on the results of the
533      * initial introspection, either a method in the given parent instance
534      * or a simple no-arg constructor is used to create an instance of the
535      * specified element type.
536      *
537      * @param project Project to which the parent object belongs.
538      * Must not be <code>null</code>. If the resulting
539      * object is an instance of ProjectComponent, its
540      * Project reference is set to this parameter value.
541      * @param parent Parent object used to create the instance.
542      * Must not be <code>null</code>.
543      * @param elementName Name of the element to create an instance of.
544      * Must not be <code>null</code>.
545      *
546      * @return an instance of the specified element type
547      * @deprecated since 1.6.x.
548      * This is not a namespace aware method.
549      *
550      * @exception BuildException if no method is available to create the
551      * element instance, or if the creating method
552      * fails.
553      */

554     public Object JavaDoc createElement(Project project, Object JavaDoc parent,
555         String JavaDoc elementName) throws BuildException {
556         NestedCreator nc = getNestedCreator(project, "", parent, elementName, null);
557         try {
558             Object JavaDoc nestedElement = nc.create(project, parent, null);
559             if (project != null) {
560                 project.setProjectReference(nestedElement);
561             }
562             return nestedElement;
563         } catch (IllegalAccessException JavaDoc ie) {
564             // impossible as getMethods should only return public methods
565
throw new BuildException(ie);
566         } catch (InstantiationException JavaDoc ine) {
567             // impossible as getMethods should only return public methods
568
throw new BuildException(ine);
569         } catch (InvocationTargetException JavaDoc ite) {
570             Throwable JavaDoc t = ite.getTargetException();
571             if (t instanceof BuildException) {
572                 throw (BuildException) t;
573             }
574             throw new BuildException(t);
575         }
576     }
577
578     /**
579      * returns an object that creates and stores an object
580      * for an element of a parent.
581      *
582      * @param project Project to which the parent object belongs.
583      * @param parentUri The namespace uri of the parent object.
584      * @param parent Parent object used to create the creator object to
585      * create and store and instance of a subelement.
586      * @param elementName Name of the element to create an instance of.
587      * @param ue The unknown element associated with the element.
588      * @return a creator object to create and store the element instance.
589      */

590     public Creator getElementCreator(
591         Project project, String JavaDoc parentUri, Object JavaDoc parent, String JavaDoc elementName,
592         UnknownElement ue) {
593         NestedCreator nc = getNestedCreator(
594             project, parentUri, parent, elementName, ue);
595         return new Creator(project, parent, nc);
596     }
597
598     /**
599      * Indicates whether the introspected class is a dynamic one,
600      * supporting arbitrary nested elements and/or attributes.
601      *
602      * @return <code>true<code> if the introspected class is dynamic;
603      * <code>false<code> otherwise.
604      * @since Ant 1.6.3
605      *
606      * @see DynamicElement
607      * @see DynamicElementNS
608      */

609     public boolean isDynamic() {
610         return DynamicElement.class.isAssignableFrom(bean)
611             || DynamicElementNS.class.isAssignableFrom(bean);
612     }
613
614     /**
615      * Indicates whether the introspected class is a task container,
616      * supporting arbitrary nested tasks/types.
617      *
618      * @return <code>true<code> if the introspected class is a container;
619      * <code>false<code> otherwise.
620      * @since Ant 1.6.3
621      *
622      * @see TaskContainer
623      */

624     public boolean isContainer() {
625         return TaskContainer.class.isAssignableFrom(bean);
626     }
627
628     /**
629      * Indicates if this element supports a nested element of the
630      * given name.
631      *
632      * @param elementName the name of the nested element being checked
633      *
634      * @return true if the given nested element is supported
635      */

636     public boolean supportsNestedElement(String JavaDoc elementName) {
637         return nestedCreators.containsKey(elementName.toLowerCase(Locale.US))
638             || isDynamic()
639             || addTypeMethods.size() != 0;
640     }
641
642     /**
643      * Indicate if this element supports a nested element of the
644      * given name.
645      *
646      * @param parentUri the uri of the parent
647      * @param elementName the name of the nested element being checked
648      *
649      * @return true if the given nested element is supported
650      */

651     public boolean supportsNestedElement(String JavaDoc parentUri, String JavaDoc elementName) {
652         if (parentUri.equals(ProjectHelper.ANT_CORE_URI)) {
653             parentUri = "";
654         }
655         String JavaDoc uri = ProjectHelper.extractUriFromComponentName(elementName);
656         if (uri.equals(ProjectHelper.ANT_CORE_URI)) {
657             uri = "";
658         }
659         String JavaDoc name = ProjectHelper.extractNameFromComponentName(elementName);
660
661         return (
662             nestedCreators.containsKey(name.toLowerCase(Locale.US))
663             && (uri.equals(parentUri) || "".equals(uri)))
664             || isDynamic() || addTypeMethods.size() != 0;
665     }
666
667     /**
668      * Stores a named nested element using a storage method determined
669      * by the initial introspection. If no appropriate storage method
670      * is available, this method returns immediately.
671      *
672      * @param project Ignored in this implementation.
673      * May be <code>null</code>.
674      *
675      * @param parent Parent instance to store the child in.
676      * Must not be <code>null</code>.
677      *
678      * @param child Child instance to store in the parent.
679      * Should not be <code>null</code>.
680      *
681      * @param elementName Name of the child element to store.
682      * May be <code>null</code>, in which case
683      * this method returns immediately.
684      *
685      * @exception BuildException if the storage method fails.
686      */

687     public void storeElement(Project project, Object JavaDoc parent, Object JavaDoc child,
688         String JavaDoc elementName) throws BuildException {
689         if (elementName == null) {
690             return;
691         }
692         NestedCreator ns = (NestedCreator) nestedCreators.get(
693             elementName.toLowerCase(Locale.US));
694         if (ns == null) {
695             return;
696         }
697         try {
698             ns.store(parent, child);
699         } catch (IllegalAccessException JavaDoc ie) {
700             // impossible as getMethods should only return public methods
701
throw new BuildException(ie);
702         } catch (InstantiationException JavaDoc ine) {
703             // impossible as getMethods should only return public methods
704
throw new BuildException(ine);
705         } catch (InvocationTargetException JavaDoc ite) {
706             Throwable JavaDoc t = ite.getTargetException();
707