git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@270194 13f79535-47bb-0310-9956-ffa450edef68master
@@ -1,854 +0,0 @@ | |||||
/* | |||||
* Copyright (C) The Apache Software Foundation. All rights reserved. | |||||
* | |||||
* This software is published under the terms of the Apache Software License | |||||
* version 1.1, a copy of which has been included with this distribution in | |||||
* the LICENSE file. | |||||
*/ | |||||
package org.apache.tools.ant; | |||||
import java.io.File; | |||||
import java.lang.reflect.Constructor; | |||||
import java.lang.reflect.InvocationTargetException; | |||||
import java.lang.reflect.Method; | |||||
import java.util.Enumeration; | |||||
import java.util.Hashtable; | |||||
import java.util.Locale; | |||||
import org.apache.myrmidon.api.TaskException; | |||||
import org.apache.tools.ant.types.EnumeratedAttribute; | |||||
import org.apache.tools.ant.types.Path; | |||||
import org.apache.tools.ant.util.FileUtils; | |||||
/** | |||||
* Helper class that collects the methods a task or nested element holds to set | |||||
* attributes, create nested elements or hold PCDATA elements. | |||||
* | |||||
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||||
*/ | |||||
public class IntrospectionHelper implements BuildListener | |||||
{ | |||||
/** | |||||
* instances we've already created | |||||
*/ | |||||
private static Hashtable helpers = new Hashtable(); | |||||
/** | |||||
* The method to add PCDATA stuff. | |||||
*/ | |||||
private Method addText = null; | |||||
/** | |||||
* holds the attribute setter methods. | |||||
*/ | |||||
private Hashtable attributeSetters; | |||||
/** | |||||
* holds the types of the attributes that could be set. | |||||
*/ | |||||
private Hashtable attributeTypes; | |||||
/** | |||||
* The Class that's been introspected. | |||||
*/ | |||||
private Class bean; | |||||
/** | |||||
* Holds methods to create nested elements. | |||||
*/ | |||||
private Hashtable nestedCreators; | |||||
/** | |||||
* Holds methods to store configured nested elements. | |||||
*/ | |||||
private Hashtable nestedStorers; | |||||
/** | |||||
* Holds the types of nested elements that could be created. | |||||
*/ | |||||
private Hashtable nestedTypes; | |||||
private IntrospectionHelper( final Class bean ) | |||||
throws TaskException | |||||
{ | |||||
attributeTypes = new Hashtable(); | |||||
attributeSetters = new Hashtable(); | |||||
nestedTypes = new Hashtable(); | |||||
nestedCreators = new Hashtable(); | |||||
nestedStorers = new Hashtable(); | |||||
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(); | |||||
// hide addTask for TaskContainers | |||||
if( org.apache.tools.ant.TaskContainer.class.isAssignableFrom( bean ) | |||||
&& 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, 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 ] ); | |||||
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" ); | |||||
nestedTypes.put( propName, returnType ); | |||||
nestedCreators.put( propName, | |||||
new NestedCreator() | |||||
{ | |||||
public Object create( Object parent ) | |||||
throws InvocationTargetException, | |||||
IllegalAccessException | |||||
{ | |||||
return m.invoke( parent, new Object[]{} ); | |||||
} | |||||
} ); | |||||
} | |||||
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 | |||||
{ | |||||
final Constructor c = | |||||
args[ 0 ].getConstructor( new Class[]{} ); | |||||
String propName = getPropertyName( name, "addConfigured" ); | |||||
nestedTypes.put( propName, args[ 0 ] ); | |||||
nestedCreators.put( propName, | |||||
new NestedCreator() | |||||
{ | |||||
public Object create( Object parent ) | |||||
throws InvocationTargetException, IllegalAccessException, InstantiationException | |||||
{ | |||||
Object o = c.newInstance( new Object[]{} ); | |||||
return o; | |||||
} | |||||
} ); | |||||
nestedStorers.put( propName, | |||||
new NestedStorer() | |||||
{ | |||||
public void store( Object parent, Object child ) | |||||
throws InvocationTargetException, IllegalAccessException, InstantiationException | |||||
{ | |||||
m.invoke( parent, new Object[]{child} ); | |||||
} | |||||
} ); | |||||
} | |||||
catch( NoSuchMethodException nse ) | |||||
{ | |||||
} | |||||
} | |||||
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 | |||||
{ | |||||
final Constructor c = | |||||
args[ 0 ].getConstructor( new Class[]{} ); | |||||
String propName = getPropertyName( name, "add" ); | |||||
nestedTypes.put( propName, args[ 0 ] ); | |||||
nestedCreators.put( propName, | |||||
new NestedCreator() | |||||
{ | |||||
public Object create( Object parent ) | |||||
throws InvocationTargetException, IllegalAccessException, InstantiationException | |||||
{ | |||||
Object o = c.newInstance( new Object[]{} ); | |||||
m.invoke( parent, new Object[]{o} ); | |||||
return o; | |||||
} | |||||
} ); | |||||
} | |||||
catch( NoSuchMethodException nse ) | |||||
{ | |||||
} | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Factory method for helper objects. | |||||
* | |||||
* @param c Description of Parameter | |||||
* @return The Helper value | |||||
*/ | |||||
public static synchronized IntrospectionHelper getHelper( Class c ) | |||||
{ | |||||
IntrospectionHelper ih = (IntrospectionHelper)helpers.get( c ); | |||||
if( ih == null ) | |||||
{ | |||||
ih = new IntrospectionHelper( c ); | |||||
helpers.put( c, ih ); | |||||
} | |||||
return ih; | |||||
} | |||||
/** | |||||
* Sets the named attribute. | |||||
* | |||||
* @param p The new Attribute value | |||||
* @param element The new Attribute value | |||||
* @param attributeName The new Attribute value | |||||
* @param value The new Attribute value | |||||
* @exception BuildException Description of Exception | |||||
*/ | |||||
public void setAttribute( Project p, Object element, String attributeName, | |||||
String value ) | |||||
throws TaskException | |||||
{ | |||||
AttributeSetter as = (AttributeSetter)attributeSetters.get( attributeName ); | |||||
if( as == null ) | |||||
{ | |||||
String msg = getElementName( p, element ) + | |||||
//String msg = "Class " + element.getClass().getName() + | |||||
" doesn't support the \"" + attributeName + "\" attribute."; | |||||
throw new TaskException( msg ); | |||||
} | |||||
try | |||||
{ | |||||
as.set( p, element, value ); | |||||
} | |||||
catch( IllegalAccessException ie ) | |||||
{ | |||||
// impossible as getMethods should only return public methods | |||||
throw new TaskException( ie.toString(), ie ); | |||||
} | |||||
catch( InvocationTargetException ite ) | |||||
{ | |||||
Throwable t = ite.getTargetException(); | |||||
if( t instanceof TaskException ) | |||||
{ | |||||
throw (TaskException)t; | |||||
} | |||||
throw new TaskException( t.toString(), t ); | |||||
} | |||||
} | |||||
/** | |||||
* returns the type of a named attribute. | |||||
* | |||||
* @param attributeName Description of Parameter | |||||
* @return The AttributeType value | |||||
* @exception TaskException Description of Exception | |||||
*/ | |||||
public Class getAttributeType( String attributeName ) | |||||
throws TaskException | |||||
{ | |||||
Class at = (Class)attributeTypes.get( attributeName ); | |||||
if( at == null ) | |||||
{ | |||||
String msg = "Class " + bean.getName() + | |||||
" doesn't support the \"" + attributeName + "\" attribute."; | |||||
throw new TaskException( msg ); | |||||
} | |||||
return at; | |||||
} | |||||
/** | |||||
* Return all attribues supported by the introspected class. | |||||
* | |||||
* @return The Attributes value | |||||
*/ | |||||
public Enumeration getAttributes() | |||||
{ | |||||
return attributeSetters.keys(); | |||||
} | |||||
/** | |||||
* returns the type of a named nested element. | |||||
* | |||||
* @param elementName Description of Parameter | |||||
* @return The ElementType value | |||||
* @exception TaskException Description of Exception | |||||
*/ | |||||
public Class getElementType( String elementName ) | |||||
throws TaskException | |||||
{ | |||||
Class nt = (Class)nestedTypes.get( elementName ); | |||||
if( nt == null ) | |||||
{ | |||||
String msg = "Class " + bean.getName() + | |||||
" doesn't support the nested \"" + elementName + "\" element."; | |||||
throw new TaskException( msg ); | |||||
} | |||||
return nt; | |||||
} | |||||
/** | |||||
* Return all nested elements supported by the introspected class. | |||||
* | |||||
* @return The NestedElements value | |||||
*/ | |||||
public Enumeration getNestedElements() | |||||
{ | |||||
return nestedTypes.keys(); | |||||
} | |||||
/** | |||||
* Adds PCDATA areas. | |||||
* | |||||
* @param project The feature to be added to the Text attribute | |||||
* @param element The feature to be added to the Text attribute | |||||
* @param text The feature to be added to the Text attribute | |||||
*/ | |||||
public void addText( Project project, Object element, String text ) | |||||
throws TaskException | |||||
{ | |||||
if( addText == null ) | |||||
{ | |||||
String msg = getElementName( project, element ) + | |||||
//String msg = "Class " + element.getClass().getName() + | |||||
" doesn't support nested text data."; | |||||
throw new TaskException( msg ); | |||||
} | |||||
try | |||||
{ | |||||
addText.invoke( element, new String[]{text} ); | |||||
} | |||||
catch( IllegalAccessException ie ) | |||||
{ | |||||
// impossible as getMethods should only return public methods | |||||
throw new TaskException( ie.getMessage(), ie ); | |||||
} | |||||
catch( InvocationTargetException ite ) | |||||
{ | |||||
Throwable t = ite.getTargetException(); | |||||
if( t instanceof TaskException ) | |||||
{ | |||||
throw (TaskException)t; | |||||
} | |||||
throw new TaskException( t.getMessage(), t ); | |||||
} | |||||
} | |||||
public void buildFinished( BuildEvent event ) | |||||
{ | |||||
attributeTypes.clear(); | |||||
attributeSetters.clear(); | |||||
nestedTypes.clear(); | |||||
nestedCreators.clear(); | |||||
addText = null; | |||||
helpers.clear(); | |||||
} | |||||
public void buildStarted( BuildEvent event ) | |||||
{ | |||||
} | |||||
/** | |||||
* Creates a named nested element. | |||||
* | |||||
* @param project Description of Parameter | |||||
* @param element Description of Parameter | |||||
* @param elementName Description of Parameter | |||||
* @return Description of the Returned Value | |||||
* @exception TaskException Description of Exception | |||||
*/ | |||||
public Object createElement( Project project, Object element, String elementName ) | |||||
throws TaskException | |||||
{ | |||||
NestedCreator nc = (NestedCreator)nestedCreators.get( elementName ); | |||||
if( nc == null ) | |||||
{ | |||||
String msg = getElementName( project, element ) + | |||||
" doesn't support the nested \"" + elementName + "\" element."; | |||||
throw new TaskException( msg ); | |||||
} | |||||
try | |||||
{ | |||||
Object nestedElement = nc.create( element ); | |||||
if( nestedElement instanceof ProjectComponent ) | |||||
{ | |||||
( (ProjectComponent)nestedElement ).setProject( project ); | |||||
} | |||||
return nestedElement; | |||||
} | |||||
catch( IllegalAccessException ie ) | |||||
{ | |||||
// impossible as getMethods should only return public methods | |||||
throw new TaskException( ie.getMessage(), ie ); | |||||
} | |||||
catch( InstantiationException ine ) | |||||
{ | |||||
// impossible as getMethods should only return public methods | |||||
throw new TaskException( ine.getMessage(), ine ); | |||||
} | |||||
catch( InvocationTargetException ite ) | |||||
{ | |||||
Throwable t = ite.getTargetException(); | |||||
if( t instanceof TaskException ) | |||||
{ | |||||
throw (TaskException)t; | |||||
} | |||||
throw new TaskException( t.getMessage(), t ); | |||||
} | |||||
} | |||||
public void messageLogged( BuildEvent event ) | |||||
{ | |||||
} | |||||
/** | |||||
* Creates a named nested element. | |||||
* | |||||
* @param project Description of Parameter | |||||
* @param element Description of Parameter | |||||
* @param child Description of Parameter | |||||
* @param elementName Description of Parameter | |||||
* @exception TaskException Description of Exception | |||||
*/ | |||||
public void storeElement( Project project, Object element, Object child, String elementName ) | |||||
throws TaskException | |||||
{ | |||||
if( elementName == null ) | |||||
{ | |||||
return; | |||||
} | |||||
NestedStorer ns = (NestedStorer)nestedStorers.get( elementName ); | |||||
if( ns == null ) | |||||
{ | |||||
return; | |||||
} | |||||
try | |||||
{ | |||||
ns.store( element, child ); | |||||
} | |||||
catch( IllegalAccessException ie ) | |||||
{ | |||||
// impossible as getMethods should only return public methods | |||||
throw new TaskException( ie.getMessage(), ie ); | |||||
} | |||||
catch( InstantiationException ine ) | |||||
{ | |||||
// impossible as getMethods should only return public methods | |||||
throw new TaskException( ine.getMessage(), ine ); | |||||
} | |||||
catch( InvocationTargetException ite ) | |||||
{ | |||||
Throwable t = ite.getTargetException(); | |||||
if( t instanceof TaskException ) | |||||
{ | |||||
throw (TaskException)t; | |||||
} | |||||
throw new TaskException( t.getMessage(), t ); | |||||
} | |||||
} | |||||
/** | |||||
* Does the introspected class support PCDATA? | |||||
* | |||||
* @return Description of the Returned Value | |||||
*/ | |||||
public boolean supportsCharacters() | |||||
{ | |||||
return addText != null; | |||||
} | |||||
public void targetFinished( BuildEvent event ) | |||||
{ | |||||
} | |||||
public void targetStarted( BuildEvent event ) | |||||
{ | |||||
} | |||||
public void taskFinished( BuildEvent event ) | |||||
{ | |||||
} | |||||
public void taskStarted( BuildEvent event ) | |||||
{ | |||||
} | |||||
protected String getElementName( Project project, Object element ) | |||||
{ | |||||
Hashtable elements = project.getTaskDefinitions(); | |||||
String typeName = "task"; | |||||
if( !elements.contains( element.getClass() ) ) | |||||
{ | |||||
elements = project.getDataTypeDefinitions(); | |||||
typeName = "data type"; | |||||
if( !elements.contains( element.getClass() ) ) | |||||
{ | |||||
elements = null; | |||||
} | |||||
} | |||||
if( elements != null ) | |||||
{ | |||||
Enumeration e = elements.keys(); | |||||
while( e.hasMoreElements() ) | |||||
{ | |||||
String elementName = (String)e.nextElement(); | |||||
Class elementClass = (Class)elements.get( elementName ); | |||||
if( element.getClass().equals( elementClass ) ) | |||||
{ | |||||
return "The <" + elementName + "> " + typeName; | |||||
} | |||||
} | |||||
} | |||||
return "Class " + element.getClass().getName(); | |||||
} | |||||
/** | |||||
* extract the name of a property from a method name - subtracting a given | |||||
* prefix. | |||||
* | |||||
* @param methodName Description of Parameter | |||||
* @param prefix Description of Parameter | |||||
* @return The PropertyName value | |||||
*/ | |||||
private String getPropertyName( String methodName, String prefix ) | |||||
{ | |||||
int start = prefix.length(); | |||||
return methodName.substring( start ).toLowerCase( Locale.US ); | |||||
} | |||||
/** | |||||
* Create a proper implementation of AttributeSetter for the given attribute | |||||
* type. | |||||
* | |||||
* @param m Description of Parameter | |||||
* @param arg Description of Parameter | |||||
* @return Description of the Returned Value | |||||
*/ | |||||
private AttributeSetter createAttributeSetter( final Method m, | |||||
final Class arg ) | |||||
throws TaskException | |||||
{ | |||||
// simplest case - setAttribute expects String | |||||
if( java.lang.String.class.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new String[]{value} ); | |||||
} | |||||
}; | |||||
// now for the primitive types, use their wrappers | |||||
} | |||||
else if( java.lang.Character.class.equals( arg ) | |||||
|| java.lang.Character.TYPE.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new Character[]{new Character( value.charAt( 0 ) )} ); | |||||
} | |||||
}; | |||||
} | |||||
else if( java.lang.Byte.TYPE.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new Byte[]{new Byte( value )} ); | |||||
} | |||||
}; | |||||
} | |||||
else if( java.lang.Short.TYPE.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new Short[]{new Short( value )} ); | |||||
} | |||||
}; | |||||
} | |||||
else if( java.lang.Integer.TYPE.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new Integer[]{new Integer( value )} ); | |||||
} | |||||
}; | |||||
} | |||||
else if( java.lang.Long.TYPE.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new Long[]{new Long( value )} ); | |||||
} | |||||
}; | |||||
} | |||||
else if( java.lang.Float.TYPE.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new Float[]{new Float( value )} ); | |||||
} | |||||
}; | |||||
} | |||||
else if( java.lang.Double.TYPE.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new Double[]{new Double( value )} ); | |||||
} | |||||
}; | |||||
// boolean gets an extra treatment, because we have a nice method | |||||
// in Project | |||||
} | |||||
else if( java.lang.Boolean.class.equals( arg ) | |||||
|| java.lang.Boolean.TYPE.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, | |||||
new Boolean[]{new Boolean( Project.toBoolean( value ) )} ); | |||||
} | |||||
}; | |||||
// Class doesn't have a String constructor but a decent factory method | |||||
} | |||||
else if( java.lang.Class.class.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException, TaskException | |||||
{ | |||||
try | |||||
{ | |||||
m.invoke( parent, new Class[]{Class.forName( value )} ); | |||||
} | |||||
catch( ClassNotFoundException ce ) | |||||
{ | |||||
throw new TaskException( ce.toString(), ce ); | |||||
} | |||||
} | |||||
}; | |||||
// resolve relative paths through Project | |||||
} | |||||
else if( java.io.File.class.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
final File file = | |||||
FileUtils.newFileUtils().resolveFile( p.getBaseDir(), value ); | |||||
m.invoke( parent, new File[]{file} ); | |||||
} | |||||
}; | |||||
// resolve relative paths through Project | |||||
} | |||||
else if( org.apache.tools.ant.types.Path.class.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new Path[]{new Path( p, value )} ); | |||||
} | |||||
}; | |||||
// EnumeratedAttributes have their own helper class | |||||
} | |||||
else if( org.apache.tools.ant.types.EnumeratedAttribute.class.isAssignableFrom( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException, TaskException | |||||
{ | |||||
try | |||||
{ | |||||
org.apache.tools.ant.types.EnumeratedAttribute ea = (org.apache.tools.ant.types.EnumeratedAttribute)arg.newInstance(); | |||||
ea.setValue( value ); | |||||
m.invoke( parent, new EnumeratedAttribute[]{ea} ); | |||||
} | |||||
catch( InstantiationException ie ) | |||||
{ | |||||
throw new TaskException( ie.getMessage(), ie ); | |||||
} | |||||
} | |||||
}; | |||||
// worst case. look for a public String constructor and use it | |||||
} | |||||
else | |||||
{ | |||||
try | |||||
{ | |||||
final Constructor c = | |||||
arg.getConstructor( new Class[]{java.lang.String.class} ); | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, | |||||
String value ) | |||||
throws InvocationTargetException, IllegalAccessException, TaskException | |||||
{ | |||||
try | |||||
{ | |||||
Object attribute = c.newInstance( new String[]{value} ); | |||||
if( attribute instanceof ProjectComponent ) | |||||
{ | |||||
( (ProjectComponent)attribute ).setProject( p ); | |||||
} | |||||
m.invoke( parent, new Object[]{attribute} ); | |||||
} | |||||
catch( InstantiationException ie ) | |||||
{ | |||||
throw new TaskException( ie.getMessage(), ie ); | |||||
} | |||||
} | |||||
}; | |||||
} | |||||
catch( NoSuchMethodException nme ) | |||||
{ | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
private interface AttributeSetter | |||||
{ | |||||
void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException, | |||||
TaskException; | |||||
} | |||||
private interface NestedCreator | |||||
{ | |||||
Object create( Object parent ) | |||||
throws InvocationTargetException, IllegalAccessException, InstantiationException; | |||||
} | |||||
private interface NestedStorer | |||||
{ | |||||
void store( Object parent, Object child ) | |||||
throws InvocationTargetException, IllegalAccessException, InstantiationException; | |||||
} | |||||
} |
@@ -1,854 +0,0 @@ | |||||
/* | |||||
* Copyright (C) The Apache Software Foundation. All rights reserved. | |||||
* | |||||
* This software is published under the terms of the Apache Software License | |||||
* version 1.1, a copy of which has been included with this distribution in | |||||
* the LICENSE file. | |||||
*/ | |||||
package org.apache.tools.ant; | |||||
import java.io.File; | |||||
import java.lang.reflect.Constructor; | |||||
import java.lang.reflect.InvocationTargetException; | |||||
import java.lang.reflect.Method; | |||||
import java.util.Enumeration; | |||||
import java.util.Hashtable; | |||||
import java.util.Locale; | |||||
import org.apache.myrmidon.api.TaskException; | |||||
import org.apache.tools.ant.types.EnumeratedAttribute; | |||||
import org.apache.tools.ant.types.Path; | |||||
import org.apache.tools.ant.util.FileUtils; | |||||
/** | |||||
* Helper class that collects the methods a task or nested element holds to set | |||||
* attributes, create nested elements or hold PCDATA elements. | |||||
* | |||||
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a> | |||||
*/ | |||||
public class IntrospectionHelper implements BuildListener | |||||
{ | |||||
/** | |||||
* instances we've already created | |||||
*/ | |||||
private static Hashtable helpers = new Hashtable(); | |||||
/** | |||||
* The method to add PCDATA stuff. | |||||
*/ | |||||
private Method addText = null; | |||||
/** | |||||
* holds the attribute setter methods. | |||||
*/ | |||||
private Hashtable attributeSetters; | |||||
/** | |||||
* holds the types of the attributes that could be set. | |||||
*/ | |||||
private Hashtable attributeTypes; | |||||
/** | |||||
* The Class that's been introspected. | |||||
*/ | |||||
private Class bean; | |||||
/** | |||||
* Holds methods to create nested elements. | |||||
*/ | |||||
private Hashtable nestedCreators; | |||||
/** | |||||
* Holds methods to store configured nested elements. | |||||
*/ | |||||
private Hashtable nestedStorers; | |||||
/** | |||||
* Holds the types of nested elements that could be created. | |||||
*/ | |||||
private Hashtable nestedTypes; | |||||
private IntrospectionHelper( final Class bean ) | |||||
throws TaskException | |||||
{ | |||||
attributeTypes = new Hashtable(); | |||||
attributeSetters = new Hashtable(); | |||||
nestedTypes = new Hashtable(); | |||||
nestedCreators = new Hashtable(); | |||||
nestedStorers = new Hashtable(); | |||||
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(); | |||||
// hide addTask for TaskContainers | |||||
if( org.apache.tools.ant.TaskContainer.class.isAssignableFrom( bean ) | |||||
&& 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, 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 ] ); | |||||
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" ); | |||||
nestedTypes.put( propName, returnType ); | |||||
nestedCreators.put( propName, | |||||
new NestedCreator() | |||||
{ | |||||
public Object create( Object parent ) | |||||
throws InvocationTargetException, | |||||
IllegalAccessException | |||||
{ | |||||
return m.invoke( parent, new Object[]{} ); | |||||
} | |||||
} ); | |||||
} | |||||
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 | |||||
{ | |||||
final Constructor c = | |||||
args[ 0 ].getConstructor( new Class[]{} ); | |||||
String propName = getPropertyName( name, "addConfigured" ); | |||||
nestedTypes.put( propName, args[ 0 ] ); | |||||
nestedCreators.put( propName, | |||||
new NestedCreator() | |||||
{ | |||||
public Object create( Object parent ) | |||||
throws InvocationTargetException, IllegalAccessException, InstantiationException | |||||
{ | |||||
Object o = c.newInstance( new Object[]{} ); | |||||
return o; | |||||
} | |||||
} ); | |||||
nestedStorers.put( propName, | |||||
new NestedStorer() | |||||
{ | |||||
public void store( Object parent, Object child ) | |||||
throws InvocationTargetException, IllegalAccessException, InstantiationException | |||||
{ | |||||
m.invoke( parent, new Object[]{child} ); | |||||
} | |||||
} ); | |||||
} | |||||
catch( NoSuchMethodException nse ) | |||||
{ | |||||
} | |||||
} | |||||
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 | |||||
{ | |||||
final Constructor c = | |||||
args[ 0 ].getConstructor( new Class[]{} ); | |||||
String propName = getPropertyName( name, "add" ); | |||||
nestedTypes.put( propName, args[ 0 ] ); | |||||
nestedCreators.put( propName, | |||||
new NestedCreator() | |||||
{ | |||||
public Object create( Object parent ) | |||||
throws InvocationTargetException, IllegalAccessException, InstantiationException | |||||
{ | |||||
Object o = c.newInstance( new Object[]{} ); | |||||
m.invoke( parent, new Object[]{o} ); | |||||
return o; | |||||
} | |||||
} ); | |||||
} | |||||
catch( NoSuchMethodException nse ) | |||||
{ | |||||
} | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* Factory method for helper objects. | |||||
* | |||||
* @param c Description of Parameter | |||||
* @return The Helper value | |||||
*/ | |||||
public static synchronized IntrospectionHelper getHelper( Class c ) | |||||
{ | |||||
IntrospectionHelper ih = (IntrospectionHelper)helpers.get( c ); | |||||
if( ih == null ) | |||||
{ | |||||
ih = new IntrospectionHelper( c ); | |||||
helpers.put( c, ih ); | |||||
} | |||||
return ih; | |||||
} | |||||
/** | |||||
* Sets the named attribute. | |||||
* | |||||
* @param p The new Attribute value | |||||
* @param element The new Attribute value | |||||
* @param attributeName The new Attribute value | |||||
* @param value The new Attribute value | |||||
* @exception BuildException Description of Exception | |||||
*/ | |||||
public void setAttribute( Project p, Object element, String attributeName, | |||||
String value ) | |||||
throws TaskException | |||||
{ | |||||
AttributeSetter as = (AttributeSetter)attributeSetters.get( attributeName ); | |||||
if( as == null ) | |||||
{ | |||||
String msg = getElementName( p, element ) + | |||||
//String msg = "Class " + element.getClass().getName() + | |||||
" doesn't support the \"" + attributeName + "\" attribute."; | |||||
throw new TaskException( msg ); | |||||
} | |||||
try | |||||
{ | |||||
as.set( p, element, value ); | |||||
} | |||||
catch( IllegalAccessException ie ) | |||||
{ | |||||
// impossible as getMethods should only return public methods | |||||
throw new TaskException( ie.toString(), ie ); | |||||
} | |||||
catch( InvocationTargetException ite ) | |||||
{ | |||||
Throwable t = ite.getTargetException(); | |||||
if( t instanceof TaskException ) | |||||
{ | |||||
throw (TaskException)t; | |||||
} | |||||
throw new TaskException( t.toString(), t ); | |||||
} | |||||
} | |||||
/** | |||||
* returns the type of a named attribute. | |||||
* | |||||
* @param attributeName Description of Parameter | |||||
* @return The AttributeType value | |||||
* @exception TaskException Description of Exception | |||||
*/ | |||||
public Class getAttributeType( String attributeName ) | |||||
throws TaskException | |||||
{ | |||||
Class at = (Class)attributeTypes.get( attributeName ); | |||||
if( at == null ) | |||||
{ | |||||
String msg = "Class " + bean.getName() + | |||||
" doesn't support the \"" + attributeName + "\" attribute."; | |||||
throw new TaskException( msg ); | |||||
} | |||||
return at; | |||||
} | |||||
/** | |||||
* Return all attribues supported by the introspected class. | |||||
* | |||||
* @return The Attributes value | |||||
*/ | |||||
public Enumeration getAttributes() | |||||
{ | |||||
return attributeSetters.keys(); | |||||
} | |||||
/** | |||||
* returns the type of a named nested element. | |||||
* | |||||
* @param elementName Description of Parameter | |||||
* @return The ElementType value | |||||
* @exception TaskException Description of Exception | |||||
*/ | |||||
public Class getElementType( String elementName ) | |||||
throws TaskException | |||||
{ | |||||
Class nt = (Class)nestedTypes.get( elementName ); | |||||
if( nt == null ) | |||||
{ | |||||
String msg = "Class " + bean.getName() + | |||||
" doesn't support the nested \"" + elementName + "\" element."; | |||||
throw new TaskException( msg ); | |||||
} | |||||
return nt; | |||||
} | |||||
/** | |||||
* Return all nested elements supported by the introspected class. | |||||
* | |||||
* @return The NestedElements value | |||||
*/ | |||||
public Enumeration getNestedElements() | |||||
{ | |||||
return nestedTypes.keys(); | |||||
} | |||||
/** | |||||
* Adds PCDATA areas. | |||||
* | |||||
* @param project The feature to be added to the Text attribute | |||||
* @param element The feature to be added to the Text attribute | |||||
* @param text The feature to be added to the Text attribute | |||||
*/ | |||||
public void addText( Project project, Object element, String text ) | |||||
throws TaskException | |||||
{ | |||||
if( addText == null ) | |||||
{ | |||||
String msg = getElementName( project, element ) + | |||||
//String msg = "Class " + element.getClass().getName() + | |||||
" doesn't support nested text data."; | |||||
throw new TaskException( msg ); | |||||
} | |||||
try | |||||
{ | |||||
addText.invoke( element, new String[]{text} ); | |||||
} | |||||
catch( IllegalAccessException ie ) | |||||
{ | |||||
// impossible as getMethods should only return public methods | |||||
throw new TaskException( ie.getMessage(), ie ); | |||||
} | |||||
catch( InvocationTargetException ite ) | |||||
{ | |||||
Throwable t = ite.getTargetException(); | |||||
if( t instanceof TaskException ) | |||||
{ | |||||
throw (TaskException)t; | |||||
} | |||||
throw new TaskException( t.getMessage(), t ); | |||||
} | |||||
} | |||||
public void buildFinished( BuildEvent event ) | |||||
{ | |||||
attributeTypes.clear(); | |||||
attributeSetters.clear(); | |||||
nestedTypes.clear(); | |||||
nestedCreators.clear(); | |||||
addText = null; | |||||
helpers.clear(); | |||||
} | |||||
public void buildStarted( BuildEvent event ) | |||||
{ | |||||
} | |||||
/** | |||||
* Creates a named nested element. | |||||
* | |||||
* @param project Description of Parameter | |||||
* @param element Description of Parameter | |||||
* @param elementName Description of Parameter | |||||
* @return Description of the Returned Value | |||||
* @exception TaskException Description of Exception | |||||
*/ | |||||
public Object createElement( Project project, Object element, String elementName ) | |||||
throws TaskException | |||||
{ | |||||
NestedCreator nc = (NestedCreator)nestedCreators.get( elementName ); | |||||
if( nc == null ) | |||||
{ | |||||
String msg = getElementName( project, element ) + | |||||
" doesn't support the nested \"" + elementName + "\" element."; | |||||
throw new TaskException( msg ); | |||||
} | |||||
try | |||||
{ | |||||
Object nestedElement = nc.create( element ); | |||||
if( nestedElement instanceof ProjectComponent ) | |||||
{ | |||||
( (ProjectComponent)nestedElement ).setProject( project ); | |||||
} | |||||
return nestedElement; | |||||
} | |||||
catch( IllegalAccessException ie ) | |||||
{ | |||||
// impossible as getMethods should only return public methods | |||||
throw new TaskException( ie.getMessage(), ie ); | |||||
} | |||||
catch( InstantiationException ine ) | |||||
{ | |||||
// impossible as getMethods should only return public methods | |||||
throw new TaskException( ine.getMessage(), ine ); | |||||
} | |||||
catch( InvocationTargetException ite ) | |||||
{ | |||||
Throwable t = ite.getTargetException(); | |||||
if( t instanceof TaskException ) | |||||
{ | |||||
throw (TaskException)t; | |||||
} | |||||
throw new TaskException( t.getMessage(), t ); | |||||
} | |||||
} | |||||
public void messageLogged( BuildEvent event ) | |||||
{ | |||||
} | |||||
/** | |||||
* Creates a named nested element. | |||||
* | |||||
* @param project Description of Parameter | |||||
* @param element Description of Parameter | |||||
* @param child Description of Parameter | |||||
* @param elementName Description of Parameter | |||||
* @exception TaskException Description of Exception | |||||
*/ | |||||
public void storeElement( Project project, Object element, Object child, String elementName ) | |||||
throws TaskException | |||||
{ | |||||
if( elementName == null ) | |||||
{ | |||||
return; | |||||
} | |||||
NestedStorer ns = (NestedStorer)nestedStorers.get( elementName ); | |||||
if( ns == null ) | |||||
{ | |||||
return; | |||||
} | |||||
try | |||||
{ | |||||
ns.store( element, child ); | |||||
} | |||||
catch( IllegalAccessException ie ) | |||||
{ | |||||
// impossible as getMethods should only return public methods | |||||
throw new TaskException( ie.getMessage(), ie ); | |||||
} | |||||
catch( InstantiationException ine ) | |||||
{ | |||||
// impossible as getMethods should only return public methods | |||||
throw new TaskException( ine.getMessage(), ine ); | |||||
} | |||||
catch( InvocationTargetException ite ) | |||||
{ | |||||
Throwable t = ite.getTargetException(); | |||||
if( t instanceof TaskException ) | |||||
{ | |||||
throw (TaskException)t; | |||||
} | |||||
throw new TaskException( t.getMessage(), t ); | |||||
} | |||||
} | |||||
/** | |||||
* Does the introspected class support PCDATA? | |||||
* | |||||
* @return Description of the Returned Value | |||||
*/ | |||||
public boolean supportsCharacters() | |||||
{ | |||||
return addText != null; | |||||
} | |||||
public void targetFinished( BuildEvent event ) | |||||
{ | |||||
} | |||||
public void targetStarted( BuildEvent event ) | |||||
{ | |||||
} | |||||
public void taskFinished( BuildEvent event ) | |||||
{ | |||||
} | |||||
public void taskStarted( BuildEvent event ) | |||||
{ | |||||
} | |||||
protected String getElementName( Project project, Object element ) | |||||
{ | |||||
Hashtable elements = project.getTaskDefinitions(); | |||||
String typeName = "task"; | |||||
if( !elements.contains( element.getClass() ) ) | |||||
{ | |||||
elements = project.getDataTypeDefinitions(); | |||||
typeName = "data type"; | |||||
if( !elements.contains( element.getClass() ) ) | |||||
{ | |||||
elements = null; | |||||
} | |||||
} | |||||
if( elements != null ) | |||||
{ | |||||
Enumeration e = elements.keys(); | |||||
while( e.hasMoreElements() ) | |||||
{ | |||||
String elementName = (String)e.nextElement(); | |||||
Class elementClass = (Class)elements.get( elementName ); | |||||
if( element.getClass().equals( elementClass ) ) | |||||
{ | |||||
return "The <" + elementName + "> " + typeName; | |||||
} | |||||
} | |||||
} | |||||
return "Class " + element.getClass().getName(); | |||||
} | |||||
/** | |||||
* extract the name of a property from a method name - subtracting a given | |||||
* prefix. | |||||
* | |||||
* @param methodName Description of Parameter | |||||
* @param prefix Description of Parameter | |||||
* @return The PropertyName value | |||||
*/ | |||||
private String getPropertyName( String methodName, String prefix ) | |||||
{ | |||||
int start = prefix.length(); | |||||
return methodName.substring( start ).toLowerCase( Locale.US ); | |||||
} | |||||
/** | |||||
* Create a proper implementation of AttributeSetter for the given attribute | |||||
* type. | |||||
* | |||||
* @param m Description of Parameter | |||||
* @param arg Description of Parameter | |||||
* @return Description of the Returned Value | |||||
*/ | |||||
private AttributeSetter createAttributeSetter( final Method m, | |||||
final Class arg ) | |||||
throws TaskException | |||||
{ | |||||
// simplest case - setAttribute expects String | |||||
if( java.lang.String.class.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new String[]{value} ); | |||||
} | |||||
}; | |||||
// now for the primitive types, use their wrappers | |||||
} | |||||
else if( java.lang.Character.class.equals( arg ) | |||||
|| java.lang.Character.TYPE.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new Character[]{new Character( value.charAt( 0 ) )} ); | |||||
} | |||||
}; | |||||
} | |||||
else if( java.lang.Byte.TYPE.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new Byte[]{new Byte( value )} ); | |||||
} | |||||
}; | |||||
} | |||||
else if( java.lang.Short.TYPE.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new Short[]{new Short( value )} ); | |||||
} | |||||
}; | |||||
} | |||||
else if( java.lang.Integer.TYPE.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new Integer[]{new Integer( value )} ); | |||||
} | |||||
}; | |||||
} | |||||
else if( java.lang.Long.TYPE.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new Long[]{new Long( value )} ); | |||||
} | |||||
}; | |||||
} | |||||
else if( java.lang.Float.TYPE.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new Float[]{new Float( value )} ); | |||||
} | |||||
}; | |||||
} | |||||
else if( java.lang.Double.TYPE.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new Double[]{new Double( value )} ); | |||||
} | |||||
}; | |||||
// boolean gets an extra treatment, because we have a nice method | |||||
// in Project | |||||
} | |||||
else if( java.lang.Boolean.class.equals( arg ) | |||||
|| java.lang.Boolean.TYPE.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, | |||||
new Boolean[]{new Boolean( Project.toBoolean( value ) )} ); | |||||
} | |||||
}; | |||||
// Class doesn't have a String constructor but a decent factory method | |||||
} | |||||
else if( java.lang.Class.class.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException, TaskException | |||||
{ | |||||
try | |||||
{ | |||||
m.invoke( parent, new Class[]{Class.forName( value )} ); | |||||
} | |||||
catch( ClassNotFoundException ce ) | |||||
{ | |||||
throw new TaskException( ce.toString(), ce ); | |||||
} | |||||
} | |||||
}; | |||||
// resolve relative paths through Project | |||||
} | |||||
else if( java.io.File.class.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
final File file = | |||||
FileUtils.newFileUtils().resolveFile( p.getBaseDir(), value ); | |||||
m.invoke( parent, new File[]{file} ); | |||||
} | |||||
}; | |||||
// resolve relative paths through Project | |||||
} | |||||
else if( org.apache.tools.ant.types.Path.class.equals( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException | |||||
{ | |||||
m.invoke( parent, new Path[]{new Path( p, value )} ); | |||||
} | |||||
}; | |||||
// EnumeratedAttributes have their own helper class | |||||
} | |||||
else if( org.apache.tools.ant.types.EnumeratedAttribute.class.isAssignableFrom( arg ) ) | |||||
{ | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException, TaskException | |||||
{ | |||||
try | |||||
{ | |||||
org.apache.tools.ant.types.EnumeratedAttribute ea = (org.apache.tools.ant.types.EnumeratedAttribute)arg.newInstance(); | |||||
ea.setValue( value ); | |||||
m.invoke( parent, new EnumeratedAttribute[]{ea} ); | |||||
} | |||||
catch( InstantiationException ie ) | |||||
{ | |||||
throw new TaskException( ie.getMessage(), ie ); | |||||
} | |||||
} | |||||
}; | |||||
// worst case. look for a public String constructor and use it | |||||
} | |||||
else | |||||
{ | |||||
try | |||||
{ | |||||
final Constructor c = | |||||
arg.getConstructor( new Class[]{java.lang.String.class} ); | |||||
return | |||||
new AttributeSetter() | |||||
{ | |||||
public void set( Project p, Object parent, | |||||
String value ) | |||||
throws InvocationTargetException, IllegalAccessException, TaskException | |||||
{ | |||||
try | |||||
{ | |||||
Object attribute = c.newInstance( new String[]{value} ); | |||||
if( attribute instanceof ProjectComponent ) | |||||
{ | |||||
( (ProjectComponent)attribute ).setProject( p ); | |||||
} | |||||
m.invoke( parent, new Object[]{attribute} ); | |||||
} | |||||
catch( InstantiationException ie ) | |||||
{ | |||||
throw new TaskException( ie.getMessage(), ie ); | |||||
} | |||||
} | |||||
}; | |||||
} | |||||
catch( NoSuchMethodException nme ) | |||||
{ | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
private interface AttributeSetter | |||||
{ | |||||
void set( Project p, Object parent, String value ) | |||||
throws InvocationTargetException, IllegalAccessException, | |||||
TaskException; | |||||
} | |||||
private interface NestedCreator | |||||
{ | |||||
Object create( Object parent ) | |||||
throws InvocationTargetException, IllegalAccessException, InstantiationException; | |||||
} | |||||
private interface NestedStorer | |||||
{ | |||||
void store( Object parent, Object child ) | |||||
throws InvocationTargetException, IllegalAccessException, InstantiationException; | |||||
} | |||||
} |