FileDocCategorySizeDatePackage
IntrospectionHelper.javaAPI DocApache Ant 1.7059815Wed Dec 13 06:16:18 GMT 2006org.apache.tools.ant

IntrospectionHelper

public final class IntrospectionHelper extends Object
Helper class that collects the methods a task or nested element holds to set attributes, create nested elements or hold PCDATA elements. The class is final as it has a private constructor.

Fields Summary
private static final Map
EMPTY_MAP
EMPTY_MAP was added in java 1.3 (EMPTY_SET and EMPTY_LIST is in java 1.2!)
private static final Map
HELPERS
Helper instances we've already created (Class.getName() to IntrospectionHelper).
private static final Map
PRIMITIVE_TYPE_MAP
Map from primitive types to wrapper classes for use in createAttributeSetter (Class to Class). Note that char and boolean are in here even though they get special treatment - this way we only need to test for the wrapper class.
private static final int
MAX_REPORT_NESTED_TEXT
private static final String
ELLIPSIS
private Hashtable
attributeTypes
Map from attribute names to attribute types (String to Class).
private Hashtable
attributeSetters
Map from attribute names to attribute setter methods (String to AttributeSetter).
private Hashtable
nestedTypes
Map from attribute names to nested types (String to Class).
private Hashtable
nestedCreators
Map from attribute names to methods to create nested types (String to NestedCreator).
private List
addTypeMethods
Vector of methods matching add[Configured](Class) pattern.
private Method
addText
The method to invoke to add PCDATA.
private Class
bean
The class introspected by this instance.
Constructors Summary
private IntrospectionHelper(Class bean)
Sole constructor, which is private to ensure that all IntrospectionHelpers are created via {@link #getHelper(Class) getHelper}. Introspects the given class for bean-like methods. Each method is examined in turn, and the following rules are applied:

  • If the method is Task.setLocation(Location), Task.setTaskType(String) or TaskContainer.addTask(Task), it is ignored. These methods are handled differently elsewhere.
  • void addText(String) is recognised as the method for adding PCDATA to a bean.
  • void setFoo(Bar) is recognised as a method for setting the value of attribute foo, so long as Bar is non-void and is not an array type. Non-String parameter types always overload String parameter types, but that is the only guarantee made in terms of priority.
  • Foo createBar() is recognised as a method for creating a nested element called bar of type Foo, so long as Foo is not a primitive or array type.
  • void addConfiguredFoo(Bar) is recognised as a method for storing a pre-configured element called foo and of type Bar, so long as Bar is not an array, primitive or String type. Bar must have an accessible constructor taking no arguments.
  • void addFoo(Bar) is recognised as a method for storing an element called foo and of type Bar, so long as Bar is not an array, primitive or String type. Bar must have an accessible constructor taking no arguments. This is distinct from the 'addConfigured' idiom in that the nested element is added to the parent immediately after it is constructed; in practice this means that addFoo(Bar) should do little or nothing with its argument besides storing it for later use.
Note that only one method is retained to create/set/addConfigured/add any element or attribute.

param
bean The bean type to introspect. Must not be null.
see
#getHelper(Class)


                                                                                                                                                                                                                                                                                                                   
        
        this.bean = bean;
        Method[] methods = bean.getMethods();
        for (int i = 0; i < methods.length; i++) {
            final Method m = methods[i];
            final String name = m.getName();
            Class returnType = m.getReturnType();
            Class[] args = m.getParameterTypes();

            // check of add[Configured](Class) pattern
            if (args.length == 1 && java.lang.Void.TYPE.equals(returnType)
                && ("add".equals(name) || "addConfigured".equals(name))) {
                insertAddTypeMethod(m);
                continue;
            }
            // not really user settable properties on tasks/project components
            if (org.apache.tools.ant.ProjectComponent.class.isAssignableFrom(
                    bean)
                 && args.length == 1 && isHiddenSetMethod(name, args[0])) {
                continue;
            }
            // hide addTask for TaskContainers
            if (isContainer() && args.length == 1 && "addTask".equals(name)
                && org.apache.tools.ant.Task.class.equals(args[0])) {
                continue;
            }
            if ("addText".equals(name) && java.lang.Void.TYPE.equals(returnType)
                && args.length == 1 && java.lang.String.class.equals(args[0])) {

                addText = methods[i];
            } else if (name.startsWith("set")
                       && java.lang.Void.TYPE.equals(returnType)
                       && args.length == 1 && !args[0].isArray()) {
                String propName = getPropertyName(name, "set");
                if (attributeSetters.get(propName) != null) {
                    if (java.lang.String.class.equals(args[0])) {
                        /*
                            Ignore method m, as there is an overloaded
                            form of this method that takes in a
                            non-string argument, which gains higher
                            priority.
                        */
                        continue;
                    }
                    /*
                        If the argument is not a String or Location,
                        and if there
                        is an overloaded form of this method already defined,
                        we just override that with the new one.
                        This mechanism does not guarantee any specific order
                        in which the methods will be selected: so any code
                        that depends on the order in which "set" methods have
                        been defined, is not guaranteed to be selected in any
                        particular order.
                    */
                }
                AttributeSetter as = createAttributeSetter(m, args[0], propName);
                if (as != null) {
                    attributeTypes.put(propName, args[0]);
                    attributeSetters.put(propName, as);
                }
            } else if (name.startsWith("create") && !returnType.isArray()
                       && !returnType.isPrimitive() && args.length == 0) {

                String propName = getPropertyName(name, "create");
                // Check if a create of this property is already present
                // add takes preference over create for CB purposes
                if (nestedCreators.get(propName) == null) {
                    nestedTypes.put(propName, returnType);
                    nestedCreators.put(propName, new CreateNestedCreator(m));
                }
            } else if (name.startsWith("addConfigured")
                && java.lang.Void.TYPE.equals(returnType) && args.length == 1
                && !java.lang.String.class.equals(args[0])
                && !args[0].isArray() && !args[0].isPrimitive()) {
                try {
                    Constructor constructor = null;
                    try {
                        constructor = args[0].getConstructor(new Class[] {});
                    } catch (NoSuchMethodException ex) {
                        constructor =
                            args[0].getConstructor(new Class[] {Project.class});
                    }
                    String propName = getPropertyName(name, "addConfigured");
                    nestedTypes.put(propName, args[0]);
                    nestedCreators.put(propName, new AddNestedCreator(m,
                        constructor, AddNestedCreator.ADD_CONFIGURED));
                } catch (NoSuchMethodException nse) {
                    // ignore
                }
            } else if (name.startsWith("add")
                && java.lang.Void.TYPE.equals(returnType) && args.length == 1
                && !java.lang.String.class.equals(args[0])
                && !args[0].isArray() && !args[0].isPrimitive()) {
                try {
                    Constructor constructor = null;
                    try {
                        constructor = args[0].getConstructor(new Class[] {});
                    } catch (NoSuchMethodException ex) {
                        constructor =
                            args[0].getConstructor(new Class[] {Project.class});
                    }

                    String propName = getPropertyName(name, "add");
                    if (nestedTypes.get(propName) != null) {
                        /*
                         *  Ignore this method as there is an addConfigured
                         *  form of this method that has a higher
                         *  priority
                         */
                        continue;
                    }
                    nestedTypes.put(propName, args[0]);
                    nestedCreators.put(propName, new AddNestedCreator(m,
                        constructor, AddNestedCreator.ADD));
                } catch (NoSuchMethodException nse) {
                    // ignore
                }
            }
        }
    
Methods Summary
public voidaddText(Project project, java.lang.Object element, java.lang.String text)
Adds PCDATA to an element, using the element's void addText(String) method, if it has one. If no such method is present, a BuildException is thrown if the given text contains non-whitespace.

param
project The project which the element is part of. Must not be null.
param
element The element to add the text to. Must not be null.
param
text The text to add. Must not be null.
exception
BuildException if non-whitespace text is provided and no method is available to handle it, or if the handling method fails.

        if (addText == null) {
            text = text.trim();
            // Element doesn't handle text content
            if (text.length() == 0) {
                // Only whitespace - ignore
                return;
            } else {
                // Not whitespace - fail
                String msg = project.getElementName(element)
                    + " doesn't support nested text data (\""
                    + condenseText(text) + "\").";
                throw new BuildException(msg);
            }
        }
        try {
            addText.invoke(element, new Object[] {text});
        } catch (IllegalAccessException ie) {
            // impossible as getMethods should only return public methods
            throw new BuildException(ie);
        } catch (InvocationTargetException ite) {
            Throwable t = ite.getTargetException();
            if (t instanceof BuildException) {
                throw (BuildException) t;
            }
            throw new BuildException(t);
        }
    
public static voidclearCache()
Clears the static cache of on build finished.

        HELPERS.clear();
    
private java.lang.StringcondenseText(java.lang.String text)

        if (text.length() <= MAX_REPORT_NESTED_TEXT) {
            return text;
        }
        int ends = (MAX_REPORT_NESTED_TEXT - ELLIPSIS.length()) / 2;
        return new StringBuffer(text).replace(ends, text.length() - ends,
            ELLIPSIS).toString();
    
private org.apache.tools.ant.IntrospectionHelper$NestedCreatorcreateAddTypeCreator(Project project, java.lang.Object parent, java.lang.String elementName)

        if (addTypeMethods.size() == 0) {
            return null;
        }
        ComponentHelper helper = ComponentHelper.getComponentHelper(project);

        Object addedObject = null;
        Method addMethod = null;
        Class clazz = helper.getComponentClass(elementName);
        if (clazz == null) {
            return null;
        }
        addMethod = findMatchingMethod(clazz, addTypeMethods);
        if (addMethod == null) {
            return null;
        }
        addedObject = helper.createComponent(elementName);
        if (addedObject == null) {
            return null;
        }
        Object rObject = addedObject;
        if (addedObject instanceof PreSetDef.PreSetDefinition) {
            rObject = ((PreSetDef.PreSetDefinition) addedObject).createObject(
                project);
        }
        final Object nestedObject = addedObject;
        final Object realObject = rObject;

        return new NestedCreator(addMethod) {
            Object create(Project project, Object parent, Object ignore)
                    throws InvocationTargetException, IllegalAccessException {
                if (!getMethod().getName().endsWith("Configured")) {
                    getMethod().invoke(parent, new Object[] {realObject});
                }
                return nestedObject;
            }

            Object getRealObject() {
                return realObject;
            }

            void store(Object parent, Object child)
                    throws InvocationTargetException, IllegalAccessException,
                    InstantiationException {
                if (getMethod().getName().endsWith("Configured")) {
                    getMethod().invoke(parent, new Object[] {realObject});
                }
            }
        };
    
private org.apache.tools.ant.IntrospectionHelper$AttributeSettercreateAttributeSetter(java.lang.reflect.Method m, java.lang.Class arg, java.lang.String attrName)
Creates an implementation of AttributeSetter for the given attribute type. Conversions (where necessary) are automatically made for the following types:
  • String (left as it is)
  • Character/char (first character is used)
  • Boolean/boolean ({@link Project#toBoolean(String) Project.toBoolean(String)} is used)
  • Class (Class.forName is used)
  • File (resolved relative to the appropriate project)
  • Path (resolve relative to the appropriate project)
  • EnumeratedAttribute (uses its own {@link EnumeratedAttribute#setValue(String) setValue} method)
  • Other primitive types (wrapper classes are used with constructors taking String)
If none of the above covers the given parameters, a constructor for the appropriate class taking a String parameter is used if it is available.

param
m The method to invoke on the bean when the setter is invoked. Must not be null.
param
arg The type of the single argument of the bean's method. Must not be null.
param
attrName the name of the attribute for which the setter is being created.
return
an appropriate AttributeSetter instance, or null if no appropriate conversion is available.

        // use wrappers for primitive classes, e.g. int and
        // Integer are treated identically
        final Class reflectedArg = PRIMITIVE_TYPE_MAP.containsKey(arg)
            ? (Class) PRIMITIVE_TYPE_MAP.get(arg) : arg;

        // simplest case - setAttribute expects String
        if (java.lang.String.class.equals(reflectedArg)) {
            return new AttributeSetter(m) {
                public void set(Project p, Object parent, String value)
                        throws InvocationTargetException, IllegalAccessException {
                    m.invoke(parent, (Object[]) (new String[] {value}));
                }
            };
        // char and Character get special treatment - take the first character
        } else if (java.lang.Character.class.equals(reflectedArg)) {
            return new AttributeSetter(m) {
                public void set(Project p, Object parent, String value)
                        throws InvocationTargetException, IllegalAccessException {
                    if (value.length() == 0) {
                        throw new BuildException("The value \"\" is not a "
                            + "legal value for attribute \"" + attrName + "\"");
                    }
                    m.invoke(parent, (Object[])
                        (new Character[] {new Character(value.charAt(0))}));
                }
            };
        // boolean and Boolean get special treatment because we
        // have a nice method in Project
        } else if (java.lang.Boolean.class.equals(reflectedArg)) {
            return new AttributeSetter(m) {
                public void set(Project p, Object parent, String value)
                        throws InvocationTargetException, IllegalAccessException {
                    m.invoke(parent, (Object[]) (
                         new Boolean[] {Project.toBoolean(value)
                                        ? Boolean.TRUE : Boolean.FALSE}));
                }
            };
        // Class doesn't have a String constructor but a decent factory method
        } else if (java.lang.Class.class.equals(reflectedArg)) {
            return new AttributeSetter(m) {
                public void set(Project p, Object parent, String value)
                        throws InvocationTargetException, IllegalAccessException, BuildException {
                    try {
                        m.invoke(parent, new Object[] {Class.forName(value)});
                    } catch (ClassNotFoundException ce) {
                        throw new BuildException(ce);
                    }
                }
            };
        // resolve relative paths through Project
        } else if (java.io.File.class.equals(reflectedArg)) {
            return new AttributeSetter(m) {
                public void set(Project p, Object parent, String value)
                        throws InvocationTargetException, IllegalAccessException {
                    m.invoke(parent, new Object[] {p.resolveFile(value)});
                }
            };
        // EnumeratedAttributes have their own helper class
        } else if (EnumeratedAttribute.class.isAssignableFrom(reflectedArg)) {
            return new AttributeSetter(m) {
                public void set(Project p, Object parent, String value)
                        throws InvocationTargetException, IllegalAccessException, BuildException {
                    try {
                        EnumeratedAttribute ea =
                            (EnumeratedAttribute) reflectedArg.newInstance();
                        ea.setValue(value);
                        m.invoke(parent, new Object[] {ea});
                    } catch (InstantiationException ie) {
                        throw new BuildException(ie);
                    }
                }
            };
        } else if (reflectedArg.getSuperclass() != null
                   && reflectedArg.getSuperclass().getName().equals("java.lang.Enum")) {
            return new AttributeSetter(m) {
                public void set(Project p, Object parent, String value)
                        throws InvocationTargetException, IllegalAccessException, BuildException {
                    try {
                        m.invoke(parent, new Object[] {
                            reflectedArg.getMethod("valueOf", new Class[] {String.class}).
                                    invoke(null, new Object[] {value})});
                    } catch (InvocationTargetException x) {
                        if (x.getTargetException() instanceof IllegalArgumentException) {
                            throw new BuildException(
                                "'" + value + "' is not a permitted value for "
                                + reflectedArg.getName());
                        } else {
                            throw new BuildException(x.getTargetException());
                        }
                    } catch (Exception x) {
                        throw new BuildException(x);
                    }
                }
            };
        // worst case. look for a public String constructor and use it
        // also supports new Whatever(Project, String) as for Path or Reference
        // This is used (deliberately) for all primitives/wrappers other than
        // char and boolean
        } else {
            boolean includeProject;
            Constructor c;
            try {
                // First try with Project.
                c = reflectedArg.getConstructor(new Class[] {Project.class, String.class});
                includeProject = true;
            } catch (NoSuchMethodException nme) {
                // OK, try without.
                try {
                    c = reflectedArg.getConstructor(new Class[] {String.class});
                    includeProject = false;
                } catch (NoSuchMethodException nme2) {
                    // Well, no matching constructor.
                    return null;
                }
            }
            final boolean finalIncludeProject = includeProject;
            final Constructor finalConstructor = c;

            return new AttributeSetter(m) {
                public void set(Project p, Object parent, String value)
                        throws InvocationTargetException, IllegalAccessException, BuildException {
                    try {
                        Object[] args = (finalIncludeProject)
                            ? new Object[] {p, value} : new Object[] {value};

                        Object attribute = finalConstructor.newInstance(args);
                        if (p != null) {
                            p.setProjectReference(attribute);
                        }
                        m.invoke(parent, new Object[] {attribute});
                    } catch (InstantiationException ie) {
                        throw new BuildException(ie);
                    }
                }
            };
        }
    
public java.lang.ObjectcreateElement(Project project, java.lang.Object parent, java.lang.String elementName)
Creates a named nested element. Depending on the results of the initial introspection, either a method in the given parent instance or a simple no-arg constructor is used to create an instance of the specified element type.

param
project Project to which the parent object belongs. Must not be null. If the resulting object is an instance of ProjectComponent, its Project reference is set to this parameter value.
param
parent Parent object used to create the instance. Must not be null.
param
elementName Name of the element to create an instance of. Must not be null.
return
an instance of the specified element type
deprecated
since 1.6.x. This is not a namespace aware method.
exception
BuildException if no method is available to create the element instance, or if the creating method fails.

        NestedCreator nc = getNestedCreator(project, "", parent, elementName, null);
        try {
            Object nestedElement = nc.create(project, parent, null);
            if (project != null) {
                project.setProjectReference(nestedElement);
            }
            return nestedElement;
        } catch (IllegalAccessException ie) {
            // impossible as getMethods should only return public methods
            throw new BuildException(ie);
        } catch (InstantiationException ine) {
            // impossible as getMethods should only return public methods
            throw new BuildException(ine);
        } catch (InvocationTargetException ite) {
            Throwable t = ite.getTargetException();
            if (t instanceof BuildException) {
                throw (BuildException) t;
            }
            throw new BuildException(t);
        }
    
private java.lang.reflect.MethodfindMatchingMethod(java.lang.Class paramClass, java.util.List methods)
Search the list of methods to find the first method that has a parameter that accepts the nested element object.

param
paramClass the Class type to search for.
param
methods the List of methods to search.
return
a matching Method; null if none found.

        Class matchedClass = null;
        Method matchedMethod = null;

        for (int i = 0; i < methods.size(); ++i) {
            Method method = (Method) methods.get(i);
            Class  methodClass = method.getParameterTypes()[0];
            if (methodClass.isAssignableFrom(paramClass)) {
                if (matchedClass == null) {
                    matchedClass = methodClass;
                    matchedMethod = method;
                } else {
                    if (!methodClass.isAssignableFrom(matchedClass)) {
                        throw new BuildException("ambiguous: types "
                            + matchedClass.getName() + " and "
                            + methodClass.getName() + " match "
                            + paramClass.getName());
                    }
                }
            }
        }
        return matchedMethod;
    
public java.lang.reflect.MethodgetAddTextMethod()
Returns the addText method when the introspected class supports nested text.

return
the method on this introspected class that adds nested text. Cannot be null.
throws
BuildException if the introspected class does not support the nested text.
since
Ant 1.6.3

        if (!supportsCharacters()) {
            throw new BuildException("Class " + bean.getName()
                + " doesn't support nested text data.");
        }
        return addText;
    
public java.util.MapgetAttributeMap()
Returns a read-only map of attributes supported by the introspected class.

return
an attribute name to attribute Class unmodifiable map. Can be empty, but never null.
since
Ant 1.6.3

        return (attributeTypes.size() < 1)
            ? EMPTY_MAP : Collections.unmodifiableMap(attributeTypes);
    
public java.lang.reflect.MethodgetAttributeMethod(java.lang.String attributeName)
Returns the setter method of a named attribute.

param
attributeName The name of the attribute to find the setter method of. Must not be null.
return
the method on this introspected class that sets this attribute. This will never be null.
throws
BuildException if the introspected class does not support the named attribute.
since
Ant 1.6.3

        Object setter = attributeSetters.get(attributeName);
        if (setter == null) {
            throw new UnsupportedAttributeException("Class "
                + bean.getName() + " doesn't support the \""
                + attributeName + "\" attribute.", attributeName);
        }
        return ((AttributeSetter) setter).method;
    
public java.lang.ClassgetAttributeType(java.lang.String attributeName)
Returns the type of a named attribute.

param
attributeName The name of the attribute to find the type of. Must not be null.
return
the type of the attribute with the specified name. This will never be null.
exception
BuildException if the introspected class does not support the named attribute.

        Class at = (Class) attributeTypes.get(attributeName);
        if (at == null) {
            throw new UnsupportedAttributeException("Class "
                + bean.getName() + " doesn't support the \""
                + attributeName + "\" attribute.", attributeName);
        }
        return at;
    
public java.util.EnumerationgetAttributes()
Returns an enumeration of the names of the attributes supported by the introspected class.

return
an enumeration of the names of the attributes supported by the introspected class.
see
#getAttributeMap

        return attributeSetters.keys();
    
public org.apache.tools.ant.IntrospectionHelper$CreatorgetElementCreator(Project project, java.lang.String parentUri, java.lang.Object parent, java.lang.String elementName, UnknownElement ue)
returns an object that creates and stores an object for an element of a parent.

param
project Project to which the parent object belongs.
param
parentUri The namespace uri of the parent object.
param
parent Parent object used to create the creator object to create and store and instance of a subelement.
param
elementName Name of the element to create an instance of.
param
ue The unknown element associated with the element.
return
a creator object to create and store the element instance.

        NestedCreator nc = getNestedCreator(
            project, parentUri, parent, elementName, ue);
        return new Creator(project, parent, nc);
    
public java.lang.reflect.MethodgetElementMethod(java.lang.String elementName)
Returns the adder or creator method of a named nested element.

param
elementName The name of the attribute to find the setter method of. Must not be null.
return
the method on this introspected class that adds or creates this nested element. Can be null when the introspected class is a dynamic configurator!
throws
BuildException if the introspected class does not support the named nested element.
since
Ant 1.6.3

        Object creator = nestedCreators.get(elementName);
        if (creator == null) {
            throw new UnsupportedElementException("Class "
                + bean.getName() + " doesn't support the nested \""
                + elementName + "\" element.", elementName);
        }
        return ((NestedCreator) creator).method;
    
protected java.lang.StringgetElementName(Project project, java.lang.Object element)
Returns a description of the type of the given element in relation to a given project. This is used for logging purposes when the element is asked to cope with some data it has no way of handling.

param
project The project the element is defined in. Must not be null.
param
element The element to describe. Must not be null.
return
a description of the element type

        return project.getElementName(element);
    
public java.lang.ClassgetElementType(java.lang.String elementName)
Returns the type of a named nested element.

param
elementName The name of the element to find the type of. Must not be null.
return
the type of the nested element with the specified name. This will never be null.
exception
BuildException if the introspected class does not support the named nested element.

        Class nt = (Class) nestedTypes.get(elementName);
        if (nt == null) {
            throw new UnsupportedElementException("Class "
                + bean.getName() + " doesn't support the nested \""
                + elementName + "\" element.", elementName);
        }
        return nt;
    
public java.util.ListgetExtensionPoints()
Returns a read-only list of extension points supported by the introspected class.

A task/type or nested element with void methods named add() or addConfigured(), taking a single class or interface argument, supports extensions point. This method returns the list of all these void add[Configured](type) methods.

return
a list of void, single argument add() or addConfigured() Methods of all supported extension points. These methods are sorted such that if the argument type of a method derives from another type also an argument of a method of this list, the method with the most derived argument will always appear first. Can be empty, but never null.
since
Ant 1.6.3

        return (addTypeMethods.size() < 1) ? Collections.EMPTY_LIST
            : Collections.unmodifiableList(addTypeMethods);
    
public static synchronized org.apache.tools.ant.IntrospectionHelpergetHelper(java.lang.Class c)
Returns a helper for the given class, either from the cache or by creating a new instance.

param
c The class for which a helper is required. Must not be null.
return
a helper for the specified class

        return getHelper(null, c);
    
public static org.apache.tools.ant.IntrospectionHelpergetHelper(Project p, java.lang.Class c)
Returns a helper for the given class, either from the cache or by creating a new instance. The method will make sure the helper will be cleaned up at the end of the project, and only one instance will be created for each class.

param
p the project instance.
param
c The class for which a helper is required. Must not be null.
return
a helper for the specified class

        IntrospectionHelper ih = (IntrospectionHelper) HELPERS.get(c.getName());
        // If a helper cannot be found, or if the helper is for another
        // classloader, create a new IH
        if (ih == null || ih.bean != c) {
            ih = new IntrospectionHelper(c);
            if (p != null) {
                // #30162: do *not* cache this if there is no project, as we
                // cannot guarantee that the cache will be cleared.
                HELPERS.put(c.getName(), ih);
            }
        }
        return ih;
    
private org.apache.tools.ant.IntrospectionHelper$NestedCreatorgetNestedCreator(Project project, java.lang.String parentUri, java.lang.Object parent, java.lang.String elementName, UnknownElement child)


        String uri = ProjectHelper.extractUriFromComponentName(elementName);
        String name = ProjectHelper.extractNameFromComponentName(elementName);

        if (uri.equals(ProjectHelper.ANT_CORE_URI)) {
            uri = "";
        }
        if (parentUri.equals(ProjectHelper.ANT_CORE_URI)) {
            parentUri = "";
        }
        NestedCreator nc = null;
        if (uri.equals(parentUri) || uri.equals("")) {
            nc = (NestedCreator) nestedCreators.get(
                name.toLowerCase(Locale.US));
        }
        if (nc == null) {
            nc = createAddTypeCreator(project, parent, elementName);
        }
        if (nc == null && parent instanceof DynamicElementNS) {
            DynamicElementNS dc = (DynamicElementNS) parent;
            String qName = (child == null ? name : child.getQName());
            final Object nestedElement =
                dc.createDynamicElement(
                    (child == null ? "" : child.getNamespace()),
                    name, qName);
            if (nestedElement != null) {
                nc = new NestedCreator(null) {
                    Object create(
                        Project project, Object parent, Object ignore) {
                        return nestedElement;
                    }
                };
            }
        }
        if (nc == null && parent instanceof DynamicElement) {
            DynamicElement dc = (DynamicElement) parent;
            final Object nestedElement =
                dc.createDynamicElement(name.toLowerCase(Locale.US));
            if (nestedElement != null) {
                nc = new NestedCreator(null) {
                    Object create(
                        Project project, Object parent, Object ignore) {
                        return nestedElement;
                    }
                };
            }
        }
        if (nc == null) {
            throwNotSupported(project, parent, elementName);
        }
        return nc;
    
public java.util.MapgetNestedElementMap()
Returns a read-only map of nested elements supported by the introspected class.

return
a nested-element name to nested-element Class unmodifiable map. Can be empty, but never null.
since
Ant 1.6.3

        return (nestedTypes.size() < 1)
            ? EMPTY_MAP : Collections.unmodifiableMap(nestedTypes);
    
public java.util.EnumerationgetNestedElements()
Returns an enumeration of the names of the nested elements supported by the introspected class.

return
an enumeration of the names of the nested elements supported by the introspected class.
see
#getNestedElementMap

        return nestedTypes.keys();
    
private java.lang.StringgetPropertyName(java.lang.String methodName, java.lang.String prefix)
Extracts the name of a property from a method name by subtracting a given prefix and converting into lower case. It is up to calling code to make sure the method name does actually begin with the specified prefix - no checking is done in this method.

param
methodName The name of the method in question. Must not be null.
param
prefix The prefix to remove. Must not be null.
return
the lower-cased method name with the prefix removed.

        return methodName.substring(prefix.length()).toLowerCase(Locale.US);
    
private voidinsertAddTypeMethod(java.lang.reflect.Method method)
Inserts an add or addConfigured method into the addTypeMethods array. The array is ordered so that the more derived classes are first. If both add and addConfigured are present, the addConfigured will take priority.

param
method the Method to insert.

        Class argClass = method.getParameterTypes()[0];
        for (int c = 0; c < addTypeMethods.size(); ++c) {
            Method current = (Method) addTypeMethods.get(c);
            if (current.getParameterTypes()[0].equals(argClass)) {
                if (method.getName().equals("addConfigured")) {
                    // add configured replaces the add method
                    addTypeMethods.set(c, method);
                }
                return; // Already present
            }
            if (current.getParameterTypes()[0].isAssignableFrom(
                            argClass)) {
                addTypeMethods.add(c, method);
                return; // higher derived
            }
        }
        addTypeMethods.add(method);
    
public booleanisContainer()
Indicates whether the introspected class is a task container, supporting arbitrary nested tasks/types.

return
true if the introspected class is a container; false otherwise.
since
Ant 1.6.3
see
TaskContainer

        return TaskContainer.class.isAssignableFrom(bean);
    
public booleanisDynamic()
Indicates whether the introspected class is a dynamic one, supporting arbitrary nested elements and/or attributes.

return
true if the introspected class is dynamic; false otherwise.
since
Ant 1.6.3
see
DynamicElement
see
DynamicElementNS

        return DynamicElement.class.isAssignableFrom(bean)
            || DynamicElementNS.class.isAssignableFrom(bean);
    
private booleanisHiddenSetMethod(java.lang.String name, java.lang.Class type)
Certain set methods are part of the Ant core interface to tasks and therefore not to be considered for introspection

param
name the name of the set method
param
type the type of the set method's parameter
return
true if the given set method is to be hidden.

        if ("setLocation".equals(name)
             && org.apache.tools.ant.Location.class.equals(type)) {
            return true;
        }

        if ("setTaskType".equals(name)
             && java.lang.String.class.equals(type)) {
            return true;
        }

        return false;
    
public voidsetAttribute(Project p, java.lang.Object element, java.lang.String attributeName, java.lang.String value)
Sets the named attribute in the given element, which is part of the given project.

param
p The project containing the element. This is used when files need to be resolved. Must not be null.
param
element The element to set the attribute in. Must not be null.
param
attributeName The name of the attribute to set. Must not be null.
param
value The value to set the attribute to. This may be interpreted or converted to the necessary type if the setter method doesn't just take a string. Must not be null.
exception
BuildException if the introspected class doesn't support the given attribute, or if the setting method fails.

        AttributeSetter as
            = (AttributeSetter) attributeSetters.get(
                attributeName.toLowerCase(Locale.US));
        if (as == null) {
            if (element instanceof DynamicAttributeNS) {
                DynamicAttributeNS dc = (DynamicAttributeNS) element;
                String uriPlusPrefix =
                    ProjectHelper.extractUriFromComponentName(attributeName);
                String uri =
                    ProjectHelper.extractUriFromComponentName(uriPlusPrefix);
                String localName =
                    ProjectHelper.extractNameFromComponentName(attributeName);
                String qName = ("".equals(uri)
                                ? localName : (uri + ":" + localName));

                dc.setDynamicAttribute(uri, localName, qName, value);
                return;
            } else if (element instanceof DynamicAttribute) {
                DynamicAttribute dc = (DynamicAttribute) element;
                dc.setDynamicAttribute(attributeName.toLowerCase(Locale.US), value);
                return;
            } else {
                if (attributeName.indexOf(':") != -1) {
                    return; // Ignore attribute from unknown uri's
                }
                String msg = getElementName(p, element)
                    + " doesn't support the \"" + attributeName
                    + "\" attribute.";
                throw new UnsupportedAttributeException(msg, attributeName);
            }
        }
        try {
            as.set(p, element, value);
        } catch (IllegalAccessException ie) {
            // impossible as getMethods should only return public methods
            throw new BuildException(ie);
        } catch (InvocationTargetException ite) {
            Throwable t = ite.getTargetException();
            if (t instanceof BuildException) {
                throw (BuildException) t;
            }
            throw new BuildException(t);
        }
    
public voidstoreElement(Project project, java.lang.Object parent, java.lang.Object child, java.lang.String elementName)
Stores a named nested element using a storage method determined by the initial introspection. If no appropriate storage method is available, this method returns immediately.

param
project Ignored in this implementation. May be null.
param
parent Parent instance to store the child in. Must not be null.
param
child Child instance to store in the parent. Should not be null.
param
elementName Name of the child element to store. May be null, in which case this method returns immediately.
exception
BuildException if the storage method fails.

        if (elementName == null) {
            return;
        }
        NestedCreator ns = (NestedCreator) nestedCreators.get(
            elementName.toLowerCase(Locale.US));
        if (ns == null) {
            return;
        }
        try {
            ns.store(parent, child);
        } catch (IllegalAccessException ie) {
            // impossible as getMethods should only return public methods
            throw new BuildException(ie);
        } catch (InstantiationException ine) {
            // impossible as getMethods should only return public methods
            throw new BuildException(ine);
        } catch (InvocationTargetException ite) {
            Throwable t = ite.getTargetException();
            if (t instanceof BuildException) {
                throw (BuildException) t;
            }
            throw new BuildException(t);
        }
    
public booleansupportsCharacters()
Returns whether or not the introspected class supports PCDATA.

return
whether or not the introspected class supports PCDATA.

        return addText != null;
    
public booleansupportsNestedElement(java.lang.String elementName)
Indicates if this element supports a nested element of the given name.

param
elementName the name of the nested element being checked
return
true if the given nested element is supported

        return nestedCreators.containsKey(elementName.toLowerCase(Locale.US))
            || isDynamic()
            || addTypeMethods.size() != 0;
    
public booleansupportsNestedElement(java.lang.String parentUri, java.lang.String elementName)
Indicate if this element supports a nested element of the given name.

param
parentUri the uri of the parent
param
elementName the name of the nested element being checked
return
true if the given nested element is supported

        if (parentUri.equals(ProjectHelper.ANT_CORE_URI)) {
            parentUri = "";
        }
        String uri = ProjectHelper.extractUriFromComponentName(elementName);
        if (uri.equals(ProjectHelper.ANT_CORE_URI)) {
            uri = "";
        }
        String name = ProjectHelper.extractNameFromComponentName(elementName);

        return (
            nestedCreators.containsKey(name.toLowerCase(Locale.US))
            && (uri.equals(parentUri) || "".equals(uri)))
            || isDynamic() || addTypeMethods.size() != 0;
    
public voidthrowNotSupported(Project project, java.lang.Object parent, java.lang.String elementName)
Utility method to throw a NotSupported exception

param
project the Project instance.
param
parent the object which doesn't support a requested element
param
elementName the name of the Element which is trying to be created.

        String msg = project.getElementName(parent)
            + " doesn't support the nested \"" + elementName + "\" element.";
        throw new UnsupportedElementException(msg, elementName);