Browse Source

Provide access to extension points and attribute/element/extensions/text methods in

IntrospectionHelper
PR: 30794
Obtained from: Dominique Devienne


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@276796 13f79535-47bb-0310-9956-ffa450edef68
master
Peter Reilly 21 years ago
parent
commit
d0fe01180d
3 changed files with 490 additions and 170 deletions
  1. +2
    -0
      WHATSNEW
  2. +221
    -119
      src/main/org/apache/tools/ant/IntrospectionHelper.java
  3. +267
    -51
      src/testcases/org/apache/tools/ant/IntrospectionHelperTest.java

+ 2
- 0
WHATSNEW View File

@@ -61,6 +61,8 @@ Other changes:
* Enable to choose the regexp implementation without system property.
Bugzilla Report 15390.

* Expose objects and methods in IntrospectionHelper. Bugzilla Report 30794.

Fixed bugs:
-----------



+ 221
- 119
src/main/org/apache/tools/ant/IntrospectionHelper.java View File

@@ -22,10 +22,12 @@ import java.lang.reflect.Constructor;
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 @@ import org.apache.tools.ant.taskdefs.PreSetDef;
* 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 @@ public final class IntrospectionHelper implements BuildListener {
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 @@ public final class IntrospectionHelper implements BuildListener {
}

// 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 @@ public final class IntrospectionHelper implements BuildListener {
// 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 @@ public final class IntrospectionHelper implements BuildListener {
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 @@ public final class IntrospectionHelper implements BuildListener {
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 @@ public final class IntrospectionHelper implements BuildListener {
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 @@ public final class IntrospectionHelper implements BuildListener {
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 @@ public final class IntrospectionHelper implements BuildListener {
*
* @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 @@ public final class IntrospectionHelper implements BuildListener {
throw new BuildException(t);
}
}

/**
* Adds PCDATA to an element, using the element's
@@ -619,24 +588,11 @@ public final class IntrospectionHelper implements BuildListener {
(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 @@ public final class IntrospectionHelper implements BuildListener {
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 @@ public final class IntrospectionHelper implements BuildListener {
* @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 @@ public final class IntrospectionHelper implements BuildListener {
}

/**
* 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 final class IntrospectionHelper implements BuildListener {
*/
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 @@ public final class IntrospectionHelper implements BuildListener {
return (
nestedCreators.containsKey(name.toLowerCase(Locale.US))
&& (uri.equals(parentUri) || uri.equals("")))
|| DynamicElement.class.isAssignableFrom(bean)
|| DynamicElementNS.class.isAssignableFrom(bean)
|| isDynamic()
|| addTypeMethods.size() != 0;
}

@@ -875,6 +845,72 @@ public final class IntrospectionHelper implements BuildListener {
return at;
}

/**
* 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.
*
@@ -890,22 +926,78 @@ public final class IntrospectionHelper implements BuildListener {
*
* @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
@@ -947,7 +1039,7 @@ public final class IntrospectionHelper implements BuildListener {

// 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 @@ public final class IntrospectionHelper implements BuildListener {

// 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 @@ public final class IntrospectionHelper implements BuildListener {
// 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 @@ public final class IntrospectionHelper implements BuildListener {

// 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 @@ public final class IntrospectionHelper implements BuildListener {

// 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 @@ public final class IntrospectionHelper implements BuildListener {

// 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 @@ public final class IntrospectionHelper implements BuildListener {
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 @@ public final class IntrospectionHelper implements BuildListener {
* 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 final class IntrospectionHelper implements BuildListener {
public void messageLogged(BuildEvent event) {
}


/**
*
*/
@@ -1353,15 +1464,8 @@ public final class IntrospectionHelper implements BuildListener {
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 @@ public final class IntrospectionHelper implements BuildListener {
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 @@ public final class IntrospectionHelper implements BuildListener {
* 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 @@ public final class IntrospectionHelper implements BuildListener {
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;


+ 267
- 51
src/testcases/org/apache/tools/ant/IntrospectionHelperTest.java View File

@@ -20,7 +20,15 @@ package org.apache.tools.ant;
import junit.framework.TestCase;
import junit.framework.AssertionFailedError;
import java.io.File;
import java.util.*;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.apache.tools.ant.taskdefs.condition.Os;

/**
@@ -31,6 +39,7 @@ import org.apache.tools.ant.taskdefs.condition.Os;
public class IntrospectionHelperTest extends TestCase {

private Project p;
private IntrospectionHelper ih;
private static final String projectBasedir = File.separator;

public IntrospectionHelperTest(String name) {
@@ -40,17 +49,18 @@ public class IntrospectionHelperTest extends TestCase {
public void setUp() {
p = new Project();
p.setBasedir(projectBasedir);
ih = IntrospectionHelper.getHelper(getClass());
}

public void testAddText() throws BuildException {
IntrospectionHelper ih = IntrospectionHelper.getHelper(java.lang.String.class);
try {
ih.addText(p, "", "test");
fail("String doesn\'t support addText");
} catch (BuildException be) {
}
public void testIsDynamic() {
assertTrue("Not dynamic", false == ih.isDynamic());
}

ih = IntrospectionHelper.getHelper(getClass());
public void testIsContainer() {
assertTrue("Not a container", false == ih.isContainer());
}

public void testAddText() throws BuildException {
ih.addText(p, this, "test");
try {
ih.addText(p, this, "test2");
@@ -58,14 +68,31 @@ public class IntrospectionHelperTest extends TestCase {
} catch (BuildException be) {
assertTrue(be.getException() instanceof AssertionFailedError);
}

ih = IntrospectionHelper.getHelper(String.class);
try {
ih.addText(p, "", "test");
fail("String doesn\'t support addText");
} catch (BuildException be) {
}
}

public void testGetAddTextMethod() {
Method m = ih.getAddTextMethod();
assertMethod(m, "addText", String.class, "test", "bing!");

ih = IntrospectionHelper.getHelper(String.class);
try {
m = ih.getAddTextMethod();
} catch (BuildException e) {}
}

public void testSupportsCharacters() {
IntrospectionHelper ih = IntrospectionHelper.getHelper(java.lang.String.class);
assertTrue("String doesn\'t support addText", !ih.supportsCharacters());
ih = IntrospectionHelper.getHelper(getClass());
assertTrue("IntrospectionHelperTest supports addText",
ih.supportsCharacters());
ih.supportsCharacters());

ih = IntrospectionHelper.getHelper(String.class);
assertTrue("String doesn\'t support addText", !ih.supportsCharacters());
}

public void addText(String text) {
@@ -73,7 +100,6 @@ public class IntrospectionHelperTest extends TestCase {
}

public void testElementCreators() throws BuildException {
IntrospectionHelper ih = IntrospectionHelper.getHelper(getClass());
try {
ih.getElementType("one");
fail("don't have element type one");
@@ -99,7 +125,7 @@ public class IntrospectionHelperTest extends TestCase {
fail("createFive returns primitive type");
} catch (BuildException be) {
}
assertEquals(java.lang.String.class, ih.getElementType("six"));
assertEquals(String.class, ih.getElementType("six"));
assertEquals("test", ih.createElement(p, this, "six"));

try {
@@ -132,7 +158,7 @@ public class IntrospectionHelperTest extends TestCase {
fail("no primitive constructor for java.lang.Class");
} catch (BuildException be) {
}
assertEquals(java.lang.StringBuffer.class, ih.getElementType("thirteen"));
assertEquals(StringBuffer.class, ih.getElementType("thirteen"));
assertEquals("test", ih.createElement(p, this, "thirteen").toString());

try {
@@ -150,23 +176,69 @@ public class IntrospectionHelperTest extends TestCase {
}
}

private Map getExpectedNestedElements() {
Map elemMap = new Hashtable();
elemMap.put("six", String.class);
elemMap.put("thirteen", StringBuffer.class);
elemMap.put("fourteen", StringBuffer.class);
elemMap.put("fifteen", StringBuffer.class);
return elemMap;
}

public void testGetNestedElements() {
Hashtable h = new Hashtable();
h.put("six", java.lang.String.class);
h.put("thirteen", java.lang.StringBuffer.class);
h.put("fourteen", java.lang.StringBuffer.class);
h.put("fifteen", java.lang.StringBuffer.class);
IntrospectionHelper ih = IntrospectionHelper.getHelper(getClass());
Map elemMap = getExpectedNestedElements();
Enumeration e = ih.getNestedElements();
while (e.hasMoreElements()) {
String name = (String) e.nextElement();
Class expect = (Class) h.get(name);
Class expect = (Class) elemMap.get(name);
assertNotNull("Support for "+name+" in IntrospectioNHelperTest?",
expect);
assertEquals("Return type of "+name, expect, ih.getElementType(name));
h.remove(name);
elemMap.remove(name);
}
assertTrue("Found all", elemMap.isEmpty());
}

public void testGetNestedElementMap() {
Map elemMap = getExpectedNestedElements();
Map actualMap = ih.getNestedElementMap();
for (Iterator i = actualMap.entrySet().iterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
String elemName = (String) entry.getKey();
Class elemClass = (Class) elemMap.get(elemName);
assertNotNull("Support for " + elemName +
" in IntrospectionHelperTest?", elemClass);
assertEquals("Type of " + elemName, elemClass, entry.getValue());
elemMap.remove(elemName);
}
assertTrue("Found all", elemMap.isEmpty());

// Check it's a read-only map.
try {
actualMap.clear();
} catch (UnsupportedOperationException e) {}
}

public void testGetElementMethod() {
assertElemMethod("six", "createSix", String.class, null);
assertElemMethod("thirteen", "addThirteen", null, StringBuffer.class);
assertElemMethod("fourteen", "addFourteen", null, StringBuffer.class);
assertElemMethod("fifteen", "createFifteen", StringBuffer.class, null);
}

private void assertElemMethod(String elemName, String methodName,
Class returnType, Class methodArg) {
Method m = ih.getElementMethod(elemName);
assertEquals("Method name", methodName, m.getName());
Class expectedReturnType = (returnType == null)? Void.TYPE: returnType;
assertEquals("Return type", expectedReturnType, m.getReturnType());
Class[] args = m.getParameterTypes();
if (methodArg != null) {
assertEquals("Arg Count", 1, args.length);
assertEquals("Arg Type", methodArg, args[0]);
} else {
assertEquals("Arg Count", 0, args.length);
}
assertTrue("Found all", h.isEmpty());
}

public Object createTwo(String s) {
@@ -214,7 +286,6 @@ public class IntrospectionHelperTest extends TestCase {
}

public void testAttributeSetters() throws BuildException {
IntrospectionHelper ih = IntrospectionHelper.getHelper(getClass());
try {
ih.setAttribute(p, this, "one", "test");
fail("setOne doesn't exist");
@@ -344,21 +415,21 @@ public class IntrospectionHelperTest extends TestCase {
}
}

public void testGetAttributes() {
Hashtable h = new Hashtable();
h.put("seven", java.lang.String.class);
h.put("eight", java.lang.Integer.TYPE);
h.put("nine", java.lang.Integer.class);
h.put("ten", java.io.File.class);
h.put("eleven", java.lang.Boolean.TYPE);
h.put("twelve", java.lang.Boolean.class);
h.put("thirteen", java.lang.Class.class);
h.put("fourteen", java.lang.StringBuffer.class);
h.put("fifteen", java.lang.Character.TYPE);
h.put("sixteen", java.lang.Character.class);
h.put("seventeen", java.lang.Byte.TYPE);
h.put("eightteen", java.lang.Short.TYPE);
h.put("nineteen", java.lang.Double.TYPE);
private Map getExpectedAttributes() {
Map attrMap = new Hashtable();
attrMap.put("seven", String.class);
attrMap.put("eight", Integer.TYPE);
attrMap.put("nine", Integer.class);
attrMap.put("ten", File.class);
attrMap.put("eleven", Boolean.TYPE);
attrMap.put("twelve", Boolean.class);
attrMap.put("thirteen", Class.class);
attrMap.put("fourteen", StringBuffer.class);
attrMap.put("fifteen", Character.TYPE);
attrMap.put("sixteen", Character.class);
attrMap.put("seventeen", Byte.TYPE);
attrMap.put("eightteen", Short.TYPE);
attrMap.put("nineteen", Double.TYPE);

/*
* JUnit 3.7 adds a getName method to TestCase - so we now
@@ -367,20 +438,85 @@ public class IntrospectionHelperTest extends TestCase {
*
* Simply add it here and remove it after the tests.
*/
h.put("name", java.lang.String.class);
attrMap.put("name", String.class);

return attrMap;
}

IntrospectionHelper ih = IntrospectionHelper.getHelper(getClass());
public void testGetAttributes() {
Map attrMap = getExpectedAttributes();
Enumeration e = ih.getAttributes();
while (e.hasMoreElements()) {
String name = (String) e.nextElement();
Class expect = (Class) h.get(name);
Class expect = (Class) attrMap.get(name);
assertNotNull("Support for "+name+" in IntrospectionHelperTest?",
expect);
assertEquals("Type of "+name, expect, ih.getAttributeType(name));
h.remove(name);
attrMap.remove(name);
}
h.remove("name");
assertTrue("Found all", h.isEmpty());
attrMap.remove("name");
assertTrue("Found all", attrMap.isEmpty());
}

public void testGetAttributeMap() {
Map attrMap = getExpectedAttributes();
Map actualMap = ih.getAttributeMap();
for (Iterator i = actualMap.entrySet().iterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
String attrName = (String) entry.getKey();
Class attrClass = (Class) attrMap.get(attrName);
assertNotNull("Support for " + attrName +
" in IntrospectionHelperTest?", attrClass);
assertEquals("Type of " + attrName, attrClass, entry.getValue());
attrMap.remove(attrName);
}
attrMap.remove("name");
assertTrue("Found all", attrMap.isEmpty());

// Check it's a read-only map.
try {
actualMap.clear();
} catch (UnsupportedOperationException e) {}
}

public void testGetAttributeMethod() {
assertAttrMethod("seven", "setSeven", String.class,
"2", "3");
assertAttrMethod("eight", "setEight", Integer.TYPE,
new Integer(2), new Integer(3));
assertAttrMethod("nine", "setNine", Integer.class,
new Integer(2), new Integer(3));
assertAttrMethod("ten", "setTen", File.class,
new File(projectBasedir + 2), new File("toto"));
assertAttrMethod("eleven", "setEleven", Boolean.TYPE,
Boolean.FALSE, Boolean.TRUE);
assertAttrMethod("twelve", "setTwelve", Boolean.class,
Boolean.FALSE, Boolean.TRUE);
assertAttrMethod("thirteen", "setThirteen", Class.class,
Project.class, Map.class);
assertAttrMethod("fourteen", "setFourteen", StringBuffer.class,
new StringBuffer("2"), new StringBuffer("3"));
assertAttrMethod("fifteen", "setFifteen", Character.TYPE,
new Character('a'), new Character('b'));
assertAttrMethod("sixteen", "setSixteen", Character.class,
new Character('a'), new Character('b'));
assertAttrMethod("seventeen", "setSeventeen", Byte.TYPE,
new Byte((byte)17), new Byte((byte)10));
assertAttrMethod("eightteen", "setEightteen", Short.TYPE,
new Short((short)18), new Short((short)10));
assertAttrMethod("nineteen", "setNineteen", Double.TYPE,
new Double(19), new Double((short)10));

try {
assertAttrMethod("onehundred", null, null, null, null);
fail("Should have raised a BuildException!");
} catch (BuildException e) {}
}

private void assertAttrMethod(String attrName, String methodName,
Class methodArg, Object arg, Object badArg) {
Method m = ih.getAttributeMethod(attrName);
assertMethod(m, methodName, methodArg, arg, badArg);
}

public int setTwo(String s) {
@@ -408,12 +544,14 @@ public class IntrospectionHelperTest extends TestCase {
}

public void setTen(File f) {
String path = f.getAbsolutePath();
if (Os.isFamily("unix") || Os.isFamily("openvms")) {
assertEquals(projectBasedir+"2", f.getAbsolutePath());
assertEquals(projectBasedir+"2", path);
} else if (Os.isFamily("netware")) {
assertEquals(projectBasedir+"2", f.getAbsolutePath().toLowerCase(Locale.US));
assertEquals(projectBasedir+"2", path.toLowerCase(Locale.US));
} else {
assertEquals(":"+projectBasedir+"2", f.getAbsolutePath().toLowerCase(Locale.US).substring(1));
assertEquals(":"+projectBasedir+"2",
path.toLowerCase(Locale.US).substring(1));
}
}

@@ -453,4 +591,82 @@ public class IntrospectionHelperTest extends TestCase {
assertEquals(19, d, 1e-6);
}

}// IntrospectionHelperTest
public void testGetExtensionPoints() {
List extensions = ih.getExtensionPoints();
assertEquals("extension count", 3, extensions.size());

assertExtMethod(extensions.get(0), "add", Number.class,
new Integer(2), new Integer(3));

// addConfigured(Hashtable) should come before addConfigured(Map)
assertExtMethod(extensions.get(1), "addConfigured", Hashtable.class,
makeTable("key", "value"), makeTable("1", "2"));

assertExtMethod(extensions.get(2), "addConfigured", Map.class,
Collections.EMPTY_MAP, makeTable("1", "2"));
}

private void assertExtMethod(Object mo, String methodName, Class methodArg,
Object arg, Object badArg) {
assertMethod((Method) mo, methodName, methodArg, arg, badArg);
}

private void assertMethod(Method m, String methodName, Class methodArg,
Object arg, Object badArg) {
assertEquals("Method name", methodName, m.getName());
assertEquals("Return type", Void.TYPE, m.getReturnType());
Class[] args = m.getParameterTypes();
assertEquals("Arg Count", 1, args.length);
assertEquals("Arg Type", methodArg, args[0]);

try {
m.invoke(this, new Object[] { arg });
} catch (IllegalAccessException e) {
throw new BuildException(e);
} catch (InvocationTargetException e) {
throw new BuildException(e);
}

try {
m.invoke(this, new Object[] { badArg });
fail("Should have raised an assertion exception");
} catch (IllegalAccessException e) {
throw new BuildException(e);
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
assertTrue(t instanceof junit.framework.AssertionFailedError);
}
}

public List add(List l) {
// INVALID extension point
return null;
}

public void add(Number n) {
// Valid extension point
assertEquals(2, n.intValue());
}

public void add(List l, int i) {
// INVALID extension point
}

public void addConfigured(Map m) {
// Valid extension point
assertTrue(Collections.EMPTY_MAP == m);
}

public void addConfigured(Hashtable h) {
// Valid extension point, more derived than Map above, but *after* it!
assertEquals(makeTable("key", "value"), h);
}

private Hashtable makeTable(Object key, Object value) {
Hashtable table = new Hashtable();
table.put(key, value);
return table;
}

} // IntrospectionHelperTest


Loading…
Cancel
Save