You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by pe...@apache.org on 2004/08/23 15:42:20 UTC
cvs commit: ant/src/main/org/apache/tools/ant IntrospectionHelper.java
peterreilly 2004/08/23 06:42:20
Modified: src/main/org/apache/tools/ant IntrospectionHelper.java
Log:
Provide access to extension points and attribute/element/extensions/text methods in
IntrospectionHelper
PR: 30794
Obtained from: Dominique Devienne
Revision Changes Path
1.86 +221 -119 ant/src/main/org/apache/tools/ant/IntrospectionHelper.java
Index: IntrospectionHelper.java
===================================================================
RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/IntrospectionHelper.java,v
retrieving revision 1.85
retrieving revision 1.86
diff -u -r1.85 -r1.86
--- IntrospectionHelper.java 12 Jun 2004 16:51:09 -0000 1.85
+++ IntrospectionHelper.java 23 Aug 2004 13:42:19 -0000 1.86
@@ -22,10 +22,12 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import org.apache.tools.ant.types.EnumeratedAttribute;
import org.apache.tools.ant.taskdefs.PreSetDef;
@@ -34,7 +36,6 @@
* holds to set attributes, create nested elements or hold PCDATA
* elements.
* The class is final as it has a private constructor.
- *
*/
public final class IntrospectionHelper implements BuildListener {
@@ -63,9 +64,9 @@
private Hashtable nestedCreators;
/**
- * Vector of methods matching add[Configured](Class) pattern
+ * Vector of methods matching add[Configured](Class) pattern.
*/
- private List addTypeMethods;
+ private List addTypeMethods;
/**
* The method to invoke to add PCDATA.
@@ -188,8 +189,7 @@
}
// hide addTask for TaskContainers
- if (org.apache.tools.ant.TaskContainer.class.isAssignableFrom(bean)
- && args.length == 1 && "addTask".equals(name)
+ if (isContainer() && args.length == 1 && "addTask".equals(name)
&& org.apache.tools.ant.Task.class.equals(args[0])) {
continue;
}
@@ -246,28 +246,13 @@
// add takes preference over create for CB purposes
if (nestedCreators.get(propName) == null) {
nestedTypes.put(propName, returnType);
- nestedCreators.put(propName, new NestedCreator() {
- public boolean isPolyMorphic() {
- return false;
- }
-
- public Object getRealObject() {
- return null;
- }
-
- public Class getElementClass() {
- return null;
- }
-
- public Object create(
+ nestedCreators.put(propName, new NestedCreator(m) {
+ Object create(
Project project, Object parent, Object ignore)
throws InvocationTargetException,
IllegalAccessException {
return m.invoke(parent, new Object[] {});
}
-
- public void store(Object parent, Object child) {
- }
});
}
} else if (name.startsWith("addConfigured")
@@ -290,21 +275,16 @@
final Constructor c = constructor;
String propName = getPropertyName(name, "addConfigured");
nestedTypes.put(propName, args[0]);
- nestedCreators.put(propName, new NestedCreator() {
-
- public boolean isPolyMorphic() {
+ nestedCreators.put(propName, new NestedCreator(m) {
+ boolean isPolyMorphic() {
return true;
}
- public Object getRealObject() {
- return null;
- }
-
- public Class getElementClass() {
+ Class getElementClass() {
return c.getDeclaringClass();
}
- public Object create(
+ Object create(
Project project, Object parent, Object child)
throws InvocationTargetException,
IllegalAccessException, InstantiationException {
@@ -323,7 +303,7 @@
return child;
}
- public void store(Object parent, Object child)
+ void store(Object parent, Object child)
throws InvocationTargetException,
IllegalAccessException, InstantiationException {
m.invoke(parent, new Object[] {child});
@@ -353,21 +333,16 @@
final Constructor c = constructor;
String propName = getPropertyName(name, "add");
nestedTypes.put(propName, args[0]);
- nestedCreators.put(propName, new NestedCreator() {
-
- public boolean isPolyMorphic() {
+ nestedCreators.put(propName, new NestedCreator(m) {
+ boolean isPolyMorphic() {
return true;
}
- public Object getRealObject() {
- return null;
- }
-
- public Class getElementClass() {
+ Class getElementClass() {
return c.getDeclaringClass();
}
- public Object create(
+ Object create(
Project project, Object parent, Object child)
throws InvocationTargetException,
IllegalAccessException, InstantiationException {
@@ -386,12 +361,6 @@
m.invoke(parent, new Object[] {child});
return child;
}
- public void store(Object parent, Object child)
- throws InvocationTargetException,
- IllegalAccessException, InstantiationException {
-
- }
-
});
} catch (NoSuchMethodException nse) {
// ignore
@@ -453,7 +422,7 @@
*
* @return a helper for the specified class
*/
- public static synchronized IntrospectionHelper getHelper(Project p,
+ public static synchronized IntrospectionHelper getHelper(Project p,
Class c) {
IntrospectionHelper ih = (IntrospectionHelper) helpers.get(c);
if (ih == null) {
@@ -529,7 +498,7 @@
throw new BuildException(t);
}
}
-
+
/**
* Adds PCDATA to an element, using the element's
@@ -619,24 +588,11 @@
(child == null ? "" : child.getNamespace()),
name, qName);
if (nestedElement != null) {
- nc = new NestedCreator() {
- public boolean isPolyMorphic() {
- return false;
- }
- public Class getElementClass() {
- return null;
- }
-
- public Object getRealObject() {
- return null;
- }
-
- public Object create(
+ nc = new NestedCreator(null) {
+ Object create(
Project project, Object parent, Object ignore) {
return nestedElement;
}
- public void store(Object parent, Object child) {
- }
};
}
}
@@ -645,24 +601,11 @@
final Object nestedElement =
dc.createDynamicElement(name.toLowerCase(Locale.US));
if (nestedElement != null) {
- nc = new NestedCreator() {
- public boolean isPolyMorphic() {
- return false;
- }
- public Class getElementClass() {
- return null;
- }
-
- public Object getRealObject() {
- return null;
- }
-
- public Object create(
+ nc = new NestedCreator(null) {
+ Object create(
Project project, Object parent, Object ignore) {
return nestedElement;
}
- public void store(Object parent, Object child) {
- }
};
}
}
@@ -730,7 +673,6 @@
* @param ue The unknown element associated with the element.
* @return a creator object to create and store the element instance.
*/
-
public Creator getElementCreator(
Project project, String parentUri, Object parent, String elementName,
UnknownElement ue) {
@@ -740,7 +682,37 @@
}
/**
- * Indicate if this element supports a nested element of the
+ * Indicates whether the introspected class is a dynamic one,
+ * supporting arbitrary nested elements and/or attributes.
+ *
+ * @return <code>true<code> if the introspected class is dynamic;
+ * <code>false<code> otherwise.
+ * @since Ant 1.6.3
+ *
+ * @see DynamicElement
+ * @see DynamicElementNS
+ */
+ public boolean isDynamic() {
+ return DynamicElement.class.isAssignableFrom(bean)
+ || DynamicElementNS.class.isAssignableFrom(bean);
+ }
+
+ /**
+ * Indicates whether the introspected class is a task container,
+ * supporting arbitrary nested tasks/types.
+ *
+ * @return <code>true<code> if the introspected class is a container;
+ * <code>false<code> otherwise.
+ * @since Ant 1.6.3
+ *
+ * @see TaskContainer
+ */
+ public boolean isContainer() {
+ return TaskContainer.class.isAssignableFrom(bean);
+ }
+
+ /**
+ * Indicates if this element supports a nested element of the
* given name.
*
* @param elementName the name of the nested element being checked
@@ -749,8 +721,7 @@
*/
public boolean supportsNestedElement(String elementName) {
return nestedCreators.containsKey(elementName.toLowerCase(Locale.US))
- || DynamicElement.class.isAssignableFrom(bean)
- || DynamicElementNS.class.isAssignableFrom(bean)
+ || isDynamic()
|| addTypeMethods.size() != 0;
}
@@ -776,8 +747,7 @@
return (
nestedCreators.containsKey(name.toLowerCase(Locale.US))
&& (uri.equals(parentUri) || uri.equals("")))
- || DynamicElement.class.isAssignableFrom(bean)
- || DynamicElementNS.class.isAssignableFrom(bean)
+ || isDynamic()
|| addTypeMethods.size() != 0;
}
@@ -876,6 +846,72 @@
}
/**
+ * Returns the addText method when the introspected
+ * class supports nested text.
+ *
+ * @return the method on this introspected class that adds nested text.
+ * Cannot be <code>null</code>.
+ * @throws BuildException if the introspected class does not
+ * support the nested text.
+ * @since Ant 1.6.3
+ */
+ public Method getAddTextMethod()
+ throws BuildException {
+ if (!supportsCharacters()) {
+ String msg = "Class " + bean.getName()
+ + " doesn't support nested text data.";
+ throw new BuildException(msg);
+ }
+ return addText;
+ }
+
+ /**
+ * 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 <code>null</code>.
+ * @return the method on this introspected class that adds or creates this
+ * nested element. Can be <code>null</code> 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
+ */
+ public Method getElementMethod(String elementName)
+ throws BuildException {
+ Object creator = nestedCreators.get(elementName);
+ if (creator == null) {
+ String msg = "Class " + bean.getName()
+ + " doesn't support the nested \"" + elementName
+ + "\" element.";
+ throw new BuildException(msg);
+ }
+ return ((NestedCreator) creator).method;
+ }
+
+ /**
+ * Returns the setter method of a named attribute.
+ *
+ * @param attributeName The name of the attribute to find the setter
+ * method of. Must not be <code>null</code>.
+ * @return the method on this introspected class that sets this attribute.
+ * This will never be <code>null</code>.
+ * @throws BuildException if the introspected class does not
+ * support the named attribute.
+ * @since Ant 1.6.3
+ */
+ public Method getAttributeMethod(String attributeName)
+ throws BuildException {
+ Object setter = attributeSetters.get(attributeName);
+ if (setter == null) {
+ String msg = "Class " + bean.getName()
+ + " doesn't support the \"" + attributeName + "\" attribute.";
+ throw new BuildException(msg);
+ }
+ return ((AttributeSetter) setter).method;
+ }
+
+ /**
* Returns whether or not the introspected class supports PCDATA.
*
* @return whether or not the introspected class supports PCDATA.
@@ -890,23 +926,79 @@
*
* @return an enumeration of the names of the attributes supported
* by the introspected class.
+ * @see #getAttributeMap
*/
public Enumeration getAttributes() {
return attributeSetters.keys();
}
/**
+ * Returns a read-only map of attributes supported
+ * by the introspected class.
+ *
+ * @return an attribute name to attribute <code>Class</code>
+ * unmodifiable map. Can be empty, but never <code>null</code>.
+ * @since Ant 1.6.3
+ */
+ public Map getAttributeMap() {
+ if (attributeTypes.size() < 1) {
+ return Collections.EMPTY_MAP;
+ }
+ return Collections.unmodifiableMap(attributeTypes);
+ }
+
+ /**
* 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
*/
public Enumeration getNestedElements() {
return nestedTypes.keys();
}
/**
+ * Returns a read-only map of nested elements supported
+ * by the introspected class.
+ *
+ * @return a nested-element name to nested-element <code>Class</code>
+ * unmodifiable map. Can be empty, but never <code>null</code>.
+ * @since Ant 1.6.3
+ */
+ public Map getNestedElementMap() {
+ if (nestedTypes.size() < 1) {
+ return Collections.EMPTY_MAP;
+ }
+ return Collections.unmodifiableMap(nestedTypes);
+ }
+
+ /**
+ * Returns a read-only list of extension points supported
+ * by the introspected class.
+ * <p>
+ * A task/type or nested element with void methods named <code>add()<code>
+ * or <code>addConfigured()</code>, taking a single class or interface
+ * argument, supports extensions point. This method returns the list of
+ * all these <em>void add[Configured](type)</em> methods.
+ *
+ * @return a list of void, single argument add() or addConfigured()
+ * <code>Method<code>s 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 <code>null</code>.
+ * @since Ant 1.6.3
+ */
+ public List getExtensionPoints() {
+ if (addTypeMethods.size() < 1) {
+ return Collections.EMPTY_LIST;
+ }
+ return Collections.unmodifiableList(addTypeMethods);
+ }
+
+ /**
* Creates an implementation of AttributeSetter for the given
* attribute type. Conversions (where necessary) are automatically
* made for the following types:
@@ -947,7 +1039,7 @@
// simplest case - setAttribute expects String
if (java.lang.String.class.equals(reflectedArg)) {
- return new AttributeSetter() {
+ return new AttributeSetter(m) {
public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException {
m.invoke(parent, new String[] {value});
@@ -956,7 +1048,7 @@
// char and Character get special treatment - take the first character
} else if (java.lang.Character.class.equals(reflectedArg)) {
- return new AttributeSetter() {
+ return new AttributeSetter(m) {
public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException {
if (value.length() == 0) {
@@ -971,7 +1063,7 @@
// 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() {
+ return new AttributeSetter(m) {
public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException {
m.invoke(parent,
@@ -983,7 +1075,7 @@
// Class doesn't have a String constructor but a decent factory method
} else if (java.lang.Class.class.equals(reflectedArg)) {
- return new AttributeSetter() {
+ return new AttributeSetter(m) {
public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException, BuildException {
try {
@@ -996,7 +1088,7 @@
// resolve relative paths through Project
} else if (java.io.File.class.equals(reflectedArg)) {
- return new AttributeSetter() {
+ return new AttributeSetter(m) {
public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException {
m.invoke(parent, new File[] {p.resolveFile(value)});
@@ -1006,7 +1098,7 @@
// EnumeratedAttributes have their own helper class
} else if (EnumeratedAttribute.class.isAssignableFrom(reflectedArg)) {
- return new AttributeSetter() {
+ return new AttributeSetter(m) {
public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException, BuildException {
try {
@@ -1045,7 +1137,7 @@
final boolean finalIncludeProject = includeProject;
final Constructor finalConstructor = c;
- return new AttributeSetter() {
+ return new AttributeSetter(m) {
public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException, BuildException {
try {
@@ -1235,25 +1327,45 @@
* Internal interface used to create nested elements. Not documented
* in detail for reasons of source code readability.
*/
- private interface NestedCreator {
- boolean isPolyMorphic();
- Class getElementClass();
- Object getRealObject();
- Object create(Project project, Object parent, Object child)
- throws InvocationTargetException, IllegalAccessException, InstantiationException;
+ private abstract class NestedCreator {
+ Method method; // the method called to add/create the nested element
+ NestedCreator(Method m) {
+ this.method = m;
+ }
+ boolean isPolyMorphic() {
+ return false;
+ }
+ Class getElementClass() {
+ return null;
+ }
+ Object getRealObject() {
+ return null;
+ }
+ abstract Object create(Project project, Object parent, Object child)
+ throws InvocationTargetException,
+ IllegalAccessException,
+ InstantiationException;
void store(Object parent, Object child)
- throws InvocationTargetException, IllegalAccessException, InstantiationException;
+ throws InvocationTargetException,
+ IllegalAccessException,
+ InstantiationException {
+ // DO NOTHING
+ }
}
-
/**
* Internal interface used to setting element attributes. Not documented
* in detail for reasons of source code readability.
*/
- private interface AttributeSetter {
- void set(Project p, Object parent, String value)
- throws InvocationTargetException, IllegalAccessException,
- BuildException;
+ private abstract class AttributeSetter {
+ Method method; // the method called to set the attribute
+ AttributeSetter(Method m) {
+ this.method = m;
+ }
+ abstract void set(Project p, Object parent, String value)
+ throws InvocationTargetException,
+ IllegalAccessException,
+ BuildException;
}
/**
@@ -1318,7 +1430,6 @@
public void messageLogged(BuildEvent event) {
}
-
/**
*
*/
@@ -1353,15 +1464,8 @@
final Object nestedObject = addedObject;
final Object realObject = rObject;
- return new NestedCreator() {
- public boolean isPolyMorphic() {
- return false;
- }
-
- public Class getElementClass() {
- return null;
- }
- public Object create(Project project, Object parent, Object ignore)
+ return new NestedCreator(method) {
+ Object create(Project project, Object parent, Object ignore)
throws InvocationTargetException, IllegalAccessException {
if (!method.getName().endsWith("Configured")) {
method.invoke(parent, new Object[]{realObject});
@@ -1369,11 +1473,11 @@
return nestedObject;
}
- public Object getRealObject() {
+ Object getRealObject() {
return realObject;
}
- public void store(Object parent, Object child)
+ void store(Object parent, Object child)
throws InvocationTargetException, IllegalAccessException,
InstantiationException {
if (method.getName().endsWith("Configured")) {
@@ -1389,7 +1493,6 @@
* ordered so that the more derived classes
* are first.
*/
-
private void insertAddTypeMethod(Method method) {
Class argClass = method.getParameterTypes()[0];
for (int c = 0; c < addTypeMethods.size(); ++c) {
@@ -1406,10 +1509,9 @@
addTypeMethods.add(method);
}
-
/**
* Search the list of methods to find the first method
- * that has a parameter that accepts the nested element object
+ * that has a parameter that accepts the nested element object.
*/
private Method findMatchingMethod(Class paramClass, List methods) {
Class matchedClass = null;
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org