Submitted by: "Adam Murdoch" <adammurdoch_ml@yahoo.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@270491 13f79535-47bb-0310-9956-ffa450edef68master
@@ -0,0 +1,35 @@ | |||||
/* | |||||
* 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.txt file. | |||||
*/ | |||||
package org.apache.myrmidon.components.configurer; | |||||
import org.apache.avalon.framework.configuration.ConfigurationException; | |||||
/** | |||||
* Used to set an attribute or text content of an object. | |||||
* | |||||
* @author <a href="mailto:adammurdoch_ml@yahoo.com">Adam Murdoch</a> | |||||
* @version $Revision$ $Date$ | |||||
*/ | |||||
public interface AttributeSetter | |||||
{ | |||||
/** | |||||
* Returns the attribute type. | |||||
*/ | |||||
Class getType(); | |||||
/** | |||||
* Sets the value of the attribute. | |||||
* | |||||
* @param object The object to set the attribute of. | |||||
* @param value The value of the attribute. Must be assignable to the class | |||||
* returned by {@link #getType}. | |||||
* @throw ConfigurationException If the value could not be set. | |||||
*/ | |||||
void setAttribute( Object object, Object value ) | |||||
throws ConfigurationException; | |||||
} |
@@ -0,0 +1,66 @@ | |||||
/* | |||||
* 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.txt file. | |||||
*/ | |||||
package org.apache.myrmidon.components.configurer; | |||||
import java.lang.reflect.InvocationTargetException; | |||||
import java.lang.reflect.Method; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
import org.apache.avalon.framework.configuration.ConfigurationException; | |||||
/** | |||||
* A default attribute setter implementation, which uses reflection to | |||||
* set the attribute value. | |||||
* | |||||
* @author <a href="mailto:adammurdoch_ml@yahoo.com">Adam Murdoch</a> | |||||
* @version $Revision$ $Date$ | |||||
*/ | |||||
class DefaultAttributeSetter | |||||
implements AttributeSetter | |||||
{ | |||||
private final Method m_method; | |||||
private final Class m_type; | |||||
private static final Resources REZ = | |||||
ResourceManager.getPackageResources( DefaultAttributeSetter.class ); | |||||
public DefaultAttributeSetter( final Method method ) | |||||
{ | |||||
m_method = method; | |||||
m_type = method.getParameterTypes()[ 0 ]; | |||||
} | |||||
/** | |||||
* Returns the attribute type. | |||||
*/ | |||||
public Class getType() | |||||
{ | |||||
return m_type; | |||||
} | |||||
/** | |||||
* Sets the value of the attribute. | |||||
*/ | |||||
public void setAttribute( final Object object, final Object value ) | |||||
throws ConfigurationException | |||||
{ | |||||
try | |||||
{ | |||||
m_method.invoke( object, new Object[]{value} ); | |||||
} | |||||
catch( final InvocationTargetException ite ) | |||||
{ | |||||
final Throwable cause = ite.getTargetException(); | |||||
throw new ConfigurationException( cause.getMessage(), cause ); | |||||
} | |||||
catch( final Exception e ) | |||||
{ | |||||
throw new ConfigurationException( e.getMessage(), e ); | |||||
} | |||||
} | |||||
} |
@@ -7,12 +7,10 @@ | |||||
*/ | */ | ||||
package org.apache.myrmidon.components.configurer; | package org.apache.myrmidon.components.configurer; | ||||
import java.lang.reflect.InvocationTargetException; | |||||
import java.lang.reflect.Method; | |||||
import java.util.ArrayList; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | import org.apache.avalon.excalibur.i18n.ResourceManager; | ||||
import org.apache.avalon.excalibur.i18n.Resources; | import org.apache.avalon.excalibur.i18n.Resources; | ||||
import org.apache.avalon.excalibur.property.PropertyException; | |||||
import org.apache.avalon.excalibur.property.PropertyUtil; | import org.apache.avalon.excalibur.property.PropertyUtil; | ||||
import org.apache.avalon.framework.component.ComponentException; | import org.apache.avalon.framework.component.ComponentException; | ||||
import org.apache.avalon.framework.component.ComponentManager; | import org.apache.avalon.framework.component.ComponentManager; | ||||
@@ -23,7 +21,6 @@ import org.apache.avalon.framework.configuration.ConfigurationException; | |||||
import org.apache.avalon.framework.context.Context; | import org.apache.avalon.framework.context.Context; | ||||
import org.apache.avalon.framework.logger.AbstractLogEnabled; | import org.apache.avalon.framework.logger.AbstractLogEnabled; | ||||
import org.apache.avalon.framework.logger.LogEnabled; | import org.apache.avalon.framework.logger.LogEnabled; | ||||
import org.apache.myrmidon.converter.ConverterException; | |||||
import org.apache.myrmidon.interfaces.configurer.Configurer; | import org.apache.myrmidon.interfaces.configurer.Configurer; | ||||
import org.apache.myrmidon.interfaces.converter.MasterConverter; | import org.apache.myrmidon.interfaces.converter.MasterConverter; | ||||
@@ -42,18 +39,13 @@ public class DefaultConfigurer | |||||
///Compile time constant to turn on extreme debugging | ///Compile time constant to turn on extreme debugging | ||||
private final static boolean DEBUG = false; | private final static boolean DEBUG = false; | ||||
/* | |||||
* TODO: Should reserved names be "configurable" ? | |||||
*/ | |||||
///Element names that are reserved | |||||
private final static String[] RESERVED_ELEMENTS = | |||||
{ | |||||
"content" | |||||
}; | |||||
///Converter to use for converting between values | ///Converter to use for converting between values | ||||
private MasterConverter m_converter; | private MasterConverter m_converter; | ||||
///Cached object configurers. This is a map from Class to the | |||||
///ObjectConfigurer for that class. | |||||
private Map m_configurerCache = new HashMap(); | |||||
public void compose( final ComponentManager componentManager ) | public void compose( final ComponentManager componentManager ) | ||||
throws ComponentException | throws ComponentException | ||||
{ | { | ||||
@@ -81,7 +73,7 @@ public class DefaultConfigurer | |||||
if( DEBUG ) | if( DEBUG ) | ||||
{ | { | ||||
final String message = REZ.getString( "configuring-object.notice", object ); | final String message = REZ.getString( "configuring-object.notice", object ); | ||||
getLogger().debug( "Configuring " + object ); | |||||
getLogger().debug( message ); | |||||
} | } | ||||
if( object instanceof Configurable ) | if( object instanceof Configurable ) | ||||
@@ -89,9 +81,10 @@ public class DefaultConfigurer | |||||
if( DEBUG ) | if( DEBUG ) | ||||
{ | { | ||||
final String message = REZ.getString( "configurable.notice" ); | final String message = REZ.getString( "configurable.notice" ); | ||||
getLogger().debug( "Configuring object via Configurable interface" ); | |||||
getLogger().debug( message ); | |||||
} | } | ||||
// Let the object configure itself | |||||
( (Configurable)object ).configure( configuration ); | ( (Configurable)object ).configure( configuration ); | ||||
} | } | ||||
else | else | ||||
@@ -102,408 +95,257 @@ public class DefaultConfigurer | |||||
getLogger().debug( message ); | getLogger().debug( message ); | ||||
} | } | ||||
// Locate the configurer for this object | |||||
final ObjectConfigurer configurer = getConfigurer( object.getClass() ); | |||||
// Set each of the attributes | |||||
final String[] attributes = configuration.getAttributeNames(); | final String[] attributes = configuration.getAttributeNames(); | ||||
for( int i = 0; i < attributes.length; i++ ) | for( int i = 0; i < attributes.length; i++ ) | ||||
{ | { | ||||
final String name = attributes[ i ]; | final String name = attributes[ i ]; | ||||
final String value = configuration.getAttribute( name ); | final String value = configuration.getAttribute( name ); | ||||
if( DEBUG ) | |||||
{ | |||||
final String message = REZ.getString( "configure-attribute.notice", name, value ); | |||||
getLogger().debug( message ); | |||||
} | |||||
configureAttribute( object, name, value, context ); | |||||
// Set the attribute | |||||
setAttribute( configurer, object, name, value, context ); | |||||
} | } | ||||
final Configuration[] children = configuration.getChildren(); | |||||
for( int i = 0; i < children.length; i++ ) | |||||
// Set the text content | |||||
final String content = configuration.getValue( null ); | |||||
if( null != content && content.length() > 0 ) | |||||
{ | { | ||||
final Configuration child = children[ i ]; | |||||
if( DEBUG ) | |||||
{ | |||||
final String message = | |||||
REZ.getString( "configure-subelement.notice", child.getName() ); | |||||
getLogger().debug( message ); | |||||
} | |||||
configureElement( object, child, context ); | |||||
setContent( configurer, object, content, context ); | |||||
} | } | ||||
final String content = configuration.getValue( null ); | |||||
if( null != content ) | |||||
// Create and configure each of the child elements | |||||
final Configuration[] children = configuration.getChildren(); | |||||
for( int i = 0; i < children.length; i++ ) | |||||
{ | { | ||||
if( !content.trim().equals( "" ) ) | |||||
{ | |||||
if( DEBUG ) | |||||
{ | |||||
final String message = | |||||
REZ.getString( "configure-content.notice", content ); | |||||
getLogger().debug( message ); | |||||
} | |||||
configureContent( object, content, context ); | |||||
} | |||||
final Configuration childConfig = children[ i ]; | |||||
configureElement( configurer, object, childConfig, context ); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Configure named attribute of object in a particular context. | |||||
* This configuring can be done in different ways for different | |||||
* configurers. | |||||
* | |||||
* @param object the object | |||||
* @param name the attribute name | |||||
* @param value the attribute value | |||||
* @param context the Context | |||||
* @exception ConfigurationException if an error occurs | |||||
* Sets the text content of an object. | |||||
*/ | */ | ||||
public void configure( final Object object, | |||||
final String name, | |||||
final String value, | |||||
final Context context ) | |||||
private void setContent( final ObjectConfigurer configurer, | |||||
final Object object, | |||||
final String content, | |||||
final Context context ) | |||||
throws ConfigurationException | throws ConfigurationException | ||||
{ | { | ||||
configureAttribute( object, name, value, context ); | |||||
if( DEBUG ) | |||||
{ | |||||
final String message = | |||||
REZ.getString( "configure-content.notice", content ); | |||||
getLogger().debug( message ); | |||||
} | |||||
// Set the content | |||||
final AttributeSetter setter = configurer.getContentSetter(); | |||||
if( null == setter ) | |||||
{ | |||||
final String message = REZ.getString( "content-not-supported.error" ); | |||||
throw new ConfigurationException( message ); | |||||
} | |||||
try | |||||
{ | |||||
setValue( setter, object, content, context ); | |||||
} | |||||
catch( final Exception e ) | |||||
{ | |||||
final String message = REZ.getString( "bad-set-content.error" ); | |||||
throw new ConfigurationException( message, e ); | |||||
} | |||||
} | } | ||||
/** | /** | ||||
* Try to configure content of an object. | |||||
* | |||||
* @param object the object | |||||
* @param content the content value to be set | |||||
* @param context the Context | |||||
* @exception ConfigurationException if an error occurs | |||||
* Creates and configures a nested element | |||||
*/ | */ | ||||
private void configureContent( final Object object, | |||||
final String content, | |||||
private void configureElement( final ObjectConfigurer configurer, | |||||
final Object object, | |||||
final Configuration childConfig, | |||||
final Context context ) | final Context context ) | ||||
throws ConfigurationException | throws ConfigurationException | ||||
{ | { | ||||
setValue( object, "addContent", content, context ); | |||||
} | |||||
final String childName = childConfig.getName(); | |||||
private void configureAttribute( final Object object, | |||||
final String name, | |||||
final String value, | |||||
final Context context ) | |||||
throws ConfigurationException | |||||
{ | |||||
final String methodName = getMethodNameFor( name ); | |||||
setValue( object, methodName, value, context ); | |||||
} | |||||
private void setValue( final Object object, | |||||
final String methodName, | |||||
final String value, | |||||
final Context context ) | |||||
throws ConfigurationException | |||||
{ | |||||
// OMFG the rest of this is soooooooooooooooooooooooooooooooo | |||||
// slow. Need to cache results per class etc. | |||||
final Class clazz = object.getClass(); | |||||
final Method[] methods = getMethodsFor( clazz, methodName ); | |||||
if( 0 == methods.length ) | |||||
if( DEBUG ) | |||||
{ | { | ||||
final String message = | final String message = | ||||
REZ.getString( "no-attribute-method.error", methodName ); | |||||
throw new ConfigurationException( message ); | |||||
REZ.getString( "configure-subelement.notice", childName ); | |||||
getLogger().debug( message ); | |||||
} | } | ||||
setValue( object, value, context, methods ); | |||||
} | |||||
// Locate the configurer for the child element | |||||
final ElementConfigurer childConfigurer = configurer.getElement( childName ); | |||||
if( null == childConfigurer ) | |||||
{ | |||||
final String message = REZ.getString( "unknown-subelement.error", childName ); | |||||
throw new ConfigurationException( message ); | |||||
} | |||||
private void setValue( final Object object, | |||||
final String value, | |||||
final Context context, | |||||
final Method methods[] ) | |||||
throws ConfigurationException | |||||
{ | |||||
try | try | ||||
{ | { | ||||
final Object objectValue = | |||||
PropertyUtil.resolveProperty( value, context, false ); | |||||
// Create the child element | |||||
final Object child = childConfigurer.createElement( object ); | |||||
setValue( object, objectValue, methods, context ); | |||||
// Configure the child element | |||||
configure( child, childConfig, context ); | |||||
// Set the child element | |||||
childConfigurer.addElement( object, child ); | |||||
} | } | ||||
catch( final PropertyException pe ) | |||||
catch( final ConfigurationException ce ) | |||||
{ | { | ||||
final String message = | final String message = | ||||
REZ.getString( "bad-property-resolve.error", value ); | |||||
throw new ConfigurationException( message, pe ); | |||||
REZ.getString( "bad-configure-subelement.error", childName ); | |||||
throw new ConfigurationException( message, ce ); | |||||
} | } | ||||
} | } | ||||
private void setValue( final Object object, | |||||
Object value, | |||||
final Method methods[], | |||||
/** | |||||
* Configure named attribute of object in a particular context. | |||||
* This configuring can be done in different ways for different | |||||
* configurers. | |||||
* | |||||
* @param object the object | |||||
* @param name the attribute name | |||||
* @param value the attribute value | |||||
* @param context the Context | |||||
* @exception ConfigurationException if an error occurs | |||||
*/ | |||||
public void configure( final Object object, | |||||
final String name, | |||||
final String value, | |||||
final Context context ) | final Context context ) | ||||
throws ConfigurationException | throws ConfigurationException | ||||
{ | { | ||||
final Class sourceClass = value.getClass(); | |||||
final String source = sourceClass.getName(); | |||||
// Locate the configurer for this object | |||||
final ObjectConfigurer configurer = getConfigurer( object.getClass() ); | |||||
for( int i = 0; i < methods.length; i++ ) | |||||
{ | |||||
if( setValue( object, value, methods[ i ], sourceClass, source, context ) ) | |||||
{ | |||||
return; | |||||
} | |||||
} | |||||
final String message = | |||||
REZ.getString( "no-can-convert.error", methods[ 0 ].getName(), source ); | |||||
throw new ConfigurationException( message ); | |||||
// Set the attribute value | |||||
setAttribute( configurer, object, name, value, context ); | |||||
} | } | ||||
private boolean setValue( final Object object, | |||||
Object value, | |||||
final Method method, | |||||
final Class sourceClass, | |||||
final String source, | |||||
final Context context ) | |||||
/** | |||||
* Sets an attribute value. | |||||
*/ | |||||
private void setAttribute( final ObjectConfigurer configurer, | |||||
final Object object, | |||||
final String name, | |||||
final String value, | |||||
final Context context ) | |||||
throws ConfigurationException | throws ConfigurationException | ||||
{ | { | ||||
Class parameterType = method.getParameterTypes()[ 0 ]; | |||||
if( parameterType.isPrimitive() ) | |||||
{ | |||||
parameterType = getComplexTypeFor( parameterType ); | |||||
} | |||||
try | |||||
if( DEBUG ) | |||||
{ | { | ||||
value = m_converter.convert( parameterType, value, context ); | |||||
final String message = REZ.getString( "configure-attribute.notice", | |||||
name, | |||||
value ); | |||||
getLogger().debug( message ); | |||||
} | } | ||||
catch( final ConverterException ce ) | |||||
{ | |||||
if( DEBUG ) | |||||
{ | |||||
final String message = REZ.getString( "no-converter.error" ); | |||||
getLogger().debug( message, ce ); | |||||
} | |||||
throw new ConfigurationException( ce.getMessage(), ce ); | |||||
} | |||||
catch( final Exception e ) | |||||
// Locate the setter for this attribute | |||||
final AttributeSetter setter = configurer.getAttributeSetter( name ); | |||||
if( null == setter ) | |||||
{ | { | ||||
final String message = | |||||
REZ.getString( "bad-convert-for-attribute.error", method.getName() ); | |||||
throw new ConfigurationException( message, e ); | |||||
} | |||||
if( null == value ) | |||||
{ | |||||
return false; | |||||
final String message = REZ.getString( "unknown-attribute.error", name ); | |||||
throw new ConfigurationException( message ); | |||||
} | } | ||||
// Set the value | |||||
try | try | ||||
{ | { | ||||
method.invoke( object, new Object[]{value} ); | |||||
} | |||||
catch( final IllegalAccessException iae ) | |||||
{ | |||||
//should never happen .... | |||||
final String message = REZ.getString( "illegal-access.error" ); | |||||
throw new ConfigurationException( message, iae ); | |||||
} | |||||
catch( final InvocationTargetException ite ) | |||||
{ | |||||
final String message = REZ.getString( "invoke-target.error", method.getName() ); | |||||
throw new ConfigurationException( message, ite ); | |||||
setValue( setter, object, value, context ); | |||||
} | } | ||||
return true; | |||||
} | |||||
private Class getComplexTypeFor( final Class clazz ) | |||||
{ | |||||
if( String.class == clazz ) | |||||
return String.class; | |||||
else if( Integer.TYPE.equals( clazz ) ) | |||||
return Integer.class; | |||||
else if( Long.TYPE.equals( clazz ) ) | |||||
return Long.class; | |||||
else if( Short.TYPE.equals( clazz ) ) | |||||
return Short.class; | |||||
else if( Byte.TYPE.equals( clazz ) ) | |||||
return Byte.class; | |||||
else if( Boolean.TYPE.equals( clazz ) ) | |||||
return Boolean.class; | |||||
else if( Float.TYPE.equals( clazz ) ) | |||||
return Float.class; | |||||
else if( Double.TYPE.equals( clazz ) ) | |||||
return Double.class; | |||||
else | |||||
catch( final Exception e ) | |||||
{ | { | ||||
final String message = REZ.getString( "no-complex-type.error", clazz.getName() ); | |||||
throw new IllegalArgumentException( message ); | |||||
final String message = REZ.getString( "bad-set-attribute.error", name ); | |||||
throw new ConfigurationException( message, e ); | |||||
} | } | ||||
} | } | ||||
private Method[] getMethodsFor( final Class clazz, final String methodName ) | |||||
/** | |||||
* Sets an attribute value, or an element's text content. | |||||
*/ | |||||
private void setValue( final AttributeSetter setter, | |||||
final Object object, | |||||
final String value, | |||||
final Context context ) | |||||
throws Exception | |||||
{ | { | ||||
final Method methods[] = clazz.getMethods(); | |||||
final ArrayList matches = new ArrayList(); | |||||
// Resolve property references in the attribute value | |||||
Object objValue = PropertyUtil.resolveProperty( value, context, false ); | |||||
for( int i = 0; i < methods.length; i++ ) | |||||
// Convert the value to the appropriate type | |||||
Class clazz = setter.getType(); | |||||
if( clazz.isPrimitive() ) | |||||
{ | { | ||||
final Method method = methods[ i ]; | |||||
if( methodName.equals( method.getName() ) && | |||||
Method.PUBLIC == ( method.getModifiers() & Method.PUBLIC ) ) | |||||
{ | |||||
if( method.getReturnType().equals( Void.TYPE ) ) | |||||
{ | |||||
final Class parameters[] = method.getParameterTypes(); | |||||
if( 1 == parameters.length ) | |||||
{ | |||||
matches.add( method ); | |||||
} | |||||
} | |||||
} | |||||
clazz = getComplexTypeFor( clazz ); | |||||
} | } | ||||
return (Method[])matches.toArray( new Method[ 0 ] ); | |||||
objValue = m_converter.convert( clazz, objValue, context ); | |||||
// Set the value | |||||
setter.setAttribute( object, objValue ); | |||||
} | } | ||||
private Method[] getCreateMethodsFor( final Class clazz, final String methodName ) | |||||
/** | |||||
* Locates the configurer for a particular class. | |||||
*/ | |||||
private ObjectConfigurer getConfigurer( final Class clazz ) | |||||
throws ConfigurationException | |||||
{ | { | ||||
final Method methods[] = clazz.getMethods(); | |||||
final ArrayList matches = new ArrayList(); | |||||
for( int i = 0; i < methods.length; i++ ) | |||||
ObjectConfigurer configurer = | |||||
(ObjectConfigurer)m_configurerCache.get( clazz ); | |||||
if( null == configurer ) | |||||
{ | { | ||||
final Method method = methods[ i ]; | |||||
if( methodName.equals( method.getName() ) && | |||||
Method.PUBLIC == ( method.getModifiers() & Method.PUBLIC ) ) | |||||
{ | |||||
final Class returnType = method.getReturnType(); | |||||
if( !returnType.equals( Void.TYPE ) && | |||||
!returnType.isPrimitive() ) | |||||
{ | |||||
final Class parameters[] = method.getParameterTypes(); | |||||
if( 0 == parameters.length ) | |||||
{ | |||||
matches.add( method ); | |||||
} | |||||
} | |||||
} | |||||
configurer = DefaultObjectConfigurer.getConfigurer( clazz ); | |||||
m_configurerCache.put( clazz, configurer ); | |||||
} | } | ||||
return (Method[])matches.toArray( new Method[ 0 ] ); | |||||
} | |||||
private String getMethodNameFor( final String attribute ) | |||||
{ | |||||
return "set" + getJavaNameFor( attribute.toLowerCase() ); | |||||
return configurer; | |||||
} | } | ||||
private String getJavaNameFor( final String name ) | |||||
private Class getComplexTypeFor( final Class clazz ) | |||||
{ | { | ||||
final StringBuffer sb = new StringBuffer(); | |||||
int index = name.indexOf( '-' ); | |||||
int last = 0; | |||||
while( -1 != index ) | |||||
if( String.class == clazz ) | |||||
{ | { | ||||
final String word = name.substring( last, index ).toLowerCase(); | |||||
sb.append( Character.toUpperCase( word.charAt( 0 ) ) ); | |||||
sb.append( word.substring( 1, word.length() ) ); | |||||
last = index + 1; | |||||
index = name.indexOf( '-', last ); | |||||
return String.class; | |||||
} | } | ||||
index = name.length(); | |||||
final String word = name.substring( last, index ).toLowerCase(); | |||||
sb.append( Character.toUpperCase( word.charAt( 0 ) ) ); | |||||
sb.append( word.substring( 1, word.length() ) ); | |||||
return sb.toString(); | |||||
} | |||||
private void configureElement( final Object object, | |||||
final Configuration configuration, | |||||
final Context context ) | |||||
throws ConfigurationException | |||||
{ | |||||
final String name = configuration.getName(); | |||||
final String javaName = getJavaNameFor( name ); | |||||
// OMFG the rest of this is soooooooooooooooooooooooooooooooo | |||||
// slow. Need to cache results per class etc. | |||||
final Class clazz = object.getClass(); | |||||
Method methods[] = getMethodsFor( clazz, "add" + javaName ); | |||||
if( 0 != methods.length ) | |||||
else if( Integer.TYPE.equals( clazz ) ) | |||||
{ | { | ||||
//guess it is first method ???? | |||||
addElement( object, methods[ 0 ], configuration, context ); | |||||
return Integer.class; | |||||
} | } | ||||
else | |||||
else if( Long.TYPE.equals( clazz ) ) | |||||
{ | { | ||||
methods = getCreateMethodsFor( clazz, "create" + javaName ); | |||||
if( 0 == methods.length ) | |||||
{ | |||||
final String message = | |||||
REZ.getString( "no-element-method.error", javaName ); | |||||
throw new ConfigurationException( message ); | |||||
} | |||||
//guess it is first method ???? | |||||
createElement( object, methods[ 0 ], configuration, context ); | |||||
return Long.class; | |||||
} | } | ||||
} | |||||
private void createElement( final Object object, | |||||
final Method method, | |||||
final Configuration configuration, | |||||
final Context context ) | |||||
throws ConfigurationException | |||||
{ | |||||
try | |||||
else if( Short.TYPE.equals( clazz ) ) | |||||
{ | { | ||||
final Object created = method.invoke( object, new Object[ 0 ] ); | |||||
configure( created, configuration, context ); | |||||
return Short.class; | |||||
} | } | ||||
catch( final ConfigurationException ce ) | |||||
else if( Byte.TYPE.equals( clazz ) ) | |||||
{ | { | ||||
throw ce; | |||||
return Byte.class; | |||||
} | } | ||||
catch( final Exception e ) | |||||
else if( Boolean.TYPE.equals( clazz ) ) | |||||
{ | { | ||||
final String message = REZ.getString( "subelement-create.error" ); | |||||
throw new ConfigurationException( message, e ); | |||||
return Boolean.class; | |||||
} | } | ||||
} | |||||
private void addElement( final Object object, | |||||
final Method method, | |||||
final Configuration configuration, | |||||
final Context context ) | |||||
throws ConfigurationException | |||||
{ | |||||
try | |||||
else if( Float.TYPE.equals( clazz ) ) | |||||
{ | { | ||||
final Class clazz = method.getParameterTypes()[ 0 ]; | |||||
final Object created = clazz.newInstance(); | |||||
configure( created, configuration, context ); | |||||
method.invoke( object, new Object[]{created} ); | |||||
return Float.class; | |||||
} | } | ||||
catch( final ConfigurationException ce ) | |||||
else if( Double.TYPE.equals( clazz ) ) | |||||
{ | { | ||||
throw ce; | |||||
return Double.class; | |||||
} | } | ||||
catch( final Exception e ) | |||||
else | |||||
{ | { | ||||
final String message = REZ.getString( "subelement-create.error" ); | |||||
throw new ConfigurationException( message, e ); | |||||
final String message = REZ.getString( "no-complex-type.error", clazz.getName() ); | |||||
throw new IllegalArgumentException( message ); | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -0,0 +1,101 @@ | |||||
/* | |||||
* 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.txt file. | |||||
*/ | |||||
package org.apache.myrmidon.components.configurer; | |||||
import java.lang.reflect.InvocationTargetException; | |||||
import java.lang.reflect.Method; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
import org.apache.avalon.framework.configuration.ConfigurationException; | |||||
/** | |||||
* The default element configurer implementation, which uses reflection to | |||||
* create and/or add nested elements. | |||||
* | |||||
* @author <a href="mailto:adammurdoch_ml@yahoo.com">Adam Murdoch</a> | |||||
* @version $Revision$ $Date$ | |||||
*/ | |||||
class DefaultElementConfigurer | |||||
implements ElementConfigurer | |||||
{ | |||||
private static final Resources REZ = | |||||
ResourceManager.getPackageResources( DefaultElementConfigurer.class ); | |||||
private final Class m_type; | |||||
private final Method m_createMethod; | |||||
private final Method m_addMethod; | |||||
public DefaultElementConfigurer( final Class type, | |||||
final Method createMethod, | |||||
final Method addMethod ) | |||||
{ | |||||
m_type = type; | |||||
m_createMethod = createMethod; | |||||
m_addMethod = addMethod; | |||||
} | |||||
/** | |||||
* Returns the type of the element. | |||||
*/ | |||||
public Class getType() | |||||
{ | |||||
return m_type; | |||||
} | |||||
/** | |||||
* Creates a nested element. | |||||
*/ | |||||
public Object createElement( final Object parent ) | |||||
throws ConfigurationException | |||||
{ | |||||
try | |||||
{ | |||||
if( null != m_createMethod ) | |||||
{ | |||||
return m_createMethod.invoke( parent, null ); | |||||
} | |||||
else | |||||
{ | |||||
return m_type.newInstance(); | |||||
} | |||||
} | |||||
catch( final InvocationTargetException ite ) | |||||
{ | |||||
final Throwable cause = ite.getTargetException(); | |||||
throw new ConfigurationException( cause.getMessage(), cause ); | |||||
} | |||||
catch( final Exception e ) | |||||
{ | |||||
throw new ConfigurationException( e.getMessage(), e ); | |||||
} | |||||
} | |||||
/** | |||||
* Sets the nested element, after it has been configured. | |||||
*/ | |||||
public void addElement( final Object parent, final Object child ) | |||||
throws ConfigurationException | |||||
{ | |||||
try | |||||
{ | |||||
if( null != m_addMethod ) | |||||
{ | |||||
m_addMethod.invoke( parent, new Object[]{child} ); | |||||
} | |||||
} | |||||
catch( final InvocationTargetException ite ) | |||||
{ | |||||
final Throwable cause = ite.getTargetException(); | |||||
throw new ConfigurationException( cause.getMessage(), cause ); | |||||
} | |||||
catch( final Exception e ) | |||||
{ | |||||
throw new ConfigurationException( e.getMessage(), e ); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,380 @@ | |||||
/* | |||||
* 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.txt file. | |||||
*/ | |||||
package org.apache.myrmidon.components.configurer; | |||||
import java.lang.reflect.Method; | |||||
import java.lang.reflect.Modifier; | |||||
import java.util.ArrayList; | |||||
import java.util.Collection; | |||||
import java.util.HashMap; | |||||
import java.util.HashSet; | |||||
import java.util.Iterator; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import java.util.Set; | |||||
import org.apache.avalon.excalibur.i18n.ResourceManager; | |||||
import org.apache.avalon.excalibur.i18n.Resources; | |||||
import org.apache.avalon.framework.configuration.ConfigurationException; | |||||
/** | |||||
* An object configurer which uses reflection to determine the attributes | |||||
* and elements of a class. | |||||
* | |||||
* @author <a href="mailto:adammurdoch_ml@yahoo.com">Adam Murdoch</a> | |||||
* @version $Revision$ $Date$ | |||||
*/ | |||||
public class DefaultObjectConfigurer | |||||
implements ObjectConfigurer | |||||
{ | |||||
private static final Resources REZ = | |||||
ResourceManager.getPackageResources( DefaultObjectConfigurer.class ); | |||||
private final Class m_class; | |||||
/** | |||||
* Map from lowercase attribute name -> AttributeSetter. | |||||
*/ | |||||
private final Map m_attrs = new HashMap(); | |||||
/** | |||||
* Map from lowercase element name -> ElementSetter. | |||||
*/ | |||||
private final Map m_elements = new HashMap(); | |||||
/** | |||||
* Content setter. | |||||
*/ | |||||
private AttributeSetter m_contentSetter; | |||||
/** | |||||
* Creates an object configurer for a particular class. The newly | |||||
* created configurer will not handle any attributes, elements, or content. | |||||
* Use the various <code>enable</code> methods to enable handling of these. | |||||
*/ | |||||
public DefaultObjectConfigurer( final Class classInfo ) | |||||
{ | |||||
m_class = classInfo; | |||||
} | |||||
/** | |||||
* Enables all attributes, elements and content handling. | |||||
*/ | |||||
public void enableAll() | |||||
throws ConfigurationException | |||||
{ | |||||
enableAttributes(); | |||||
enableElements(); | |||||
enableContent(); | |||||
} | |||||
/** | |||||
* Enables all attributes. | |||||
*/ | |||||
public void enableAttributes() | |||||
throws ConfigurationException | |||||
{ | |||||
// Find all 'set' methods which take a single parameter, and return void. | |||||
final List methods = new ArrayList(); | |||||
findMethodsWithPrefix( "set", methods ); | |||||
final Iterator iterator = methods.iterator(); | |||||
while( iterator.hasNext() ) | |||||
{ | |||||
final Method method = (Method)iterator.next(); | |||||
if( method.getReturnType() != Void.TYPE || | |||||
method.getParameterTypes().length != 1 ) | |||||
{ | |||||
continue; | |||||
} | |||||
// Extract the attribute name | |||||
final String attrName = extractName( "set", method.getName() ); | |||||
// Enable the attribute | |||||
enableAttribute( attrName, method ); | |||||
} | |||||
} | |||||
/** | |||||
* Enables all elements. | |||||
*/ | |||||
public void enableElements() | |||||
throws ConfigurationException | |||||
{ | |||||
final Map creators = findCreators(); | |||||
final Map adders = findAdders(); | |||||
// Add the elements | |||||
final Set elemNames = new HashSet(); | |||||
elemNames.addAll( creators.keySet() ); | |||||
elemNames.addAll( adders.keySet() ); | |||||
final Iterator iterator = elemNames.iterator(); | |||||
while( iterator.hasNext() ) | |||||
{ | |||||
final String elemName = (String)iterator.next(); | |||||
final Method createMethod = (Method)creators.get( elemName ); | |||||
final Method addMethod = (Method)adders.get( elemName ); | |||||
// Determine and check the return type | |||||
Class type; | |||||
if( createMethod != null && addMethod != null ) | |||||
{ | |||||
// Make sure the add method is more general than the create | |||||
// method | |||||
type = createMethod.getReturnType(); | |||||
final Class addType = addMethod.getParameterTypes()[ 0 ]; | |||||
if( !addType.isAssignableFrom( type ) ) | |||||
{ | |||||
final String message = | |||||
REZ.getString( "incompatible-element-types.error", | |||||
elemName, | |||||
m_class.getName() ); | |||||
throw new ConfigurationException( message ); | |||||
} | |||||
} | |||||
else if( createMethod != null ) | |||||
{ | |||||
type = createMethod.getReturnType(); | |||||
} | |||||
else | |||||
{ | |||||
type = addMethod.getParameterTypes()[ 0 ]; | |||||
} | |||||
final DefaultElementConfigurer configurer = | |||||
new DefaultElementConfigurer( type, createMethod, addMethod ); | |||||
m_elements.put( elemName, configurer ); | |||||
} | |||||
} | |||||
/** | |||||
* Locate all 'add' methods which return void, and take a non-primitive type | |||||
*/ | |||||
private Map findAdders() | |||||
throws ConfigurationException | |||||
{ | |||||
final Map adders = new HashMap(); | |||||
final List methodSet = new ArrayList(); | |||||
findMethodsWithPrefix( "add", methodSet ); | |||||
final Iterator iterator = methodSet.iterator(); | |||||
while( iterator.hasNext() ) | |||||
{ | |||||
final Method method = (Method)iterator.next(); | |||||
final String methodName = method.getName(); | |||||
if( method.getReturnType() != Void.TYPE || | |||||
method.getParameterTypes().length != 1 || | |||||
method.getParameterTypes()[ 0 ].isPrimitive() ) | |||||
{ | |||||
continue; | |||||
} | |||||
// TODO - un-hard-code this | |||||
if( methodName.equals( "addContent" ) ) | |||||
{ | |||||
continue; | |||||
} | |||||
// Extract element name | |||||
final String elemName = extractName( "add", methodName ); | |||||
// Add to the adders map | |||||
if( adders.containsKey( elemName ) ) | |||||
{ | |||||
final String message = | |||||
REZ.getString( "multiple-adder-methods-for-element.error", | |||||
m_class.getName(), | |||||
elemName ); | |||||
throw new ConfigurationException( message ); | |||||
} | |||||
adders.put( elemName, method ); | |||||
} | |||||
return adders; | |||||
} | |||||
/** | |||||
* Find all 'create' methods, which return a non-primitive type, | |||||
* and take no parameters. | |||||
*/ | |||||
private Map findCreators() | |||||
throws ConfigurationException | |||||
{ | |||||
final Map creators = new HashMap(); | |||||
final List methodSet = new ArrayList(); | |||||
findMethodsWithPrefix( "create", methodSet ); | |||||
final Iterator iterator = methodSet.iterator(); | |||||
while( iterator.hasNext() ) | |||||
{ | |||||
final Method method = (Method)iterator.next(); | |||||
final String methodName = method.getName(); | |||||
if( method.getReturnType().isPrimitive() || | |||||
method.getParameterTypes().length != 0 ) | |||||
{ | |||||
continue; | |||||
} | |||||
// Extract element name | |||||
final String elemName = extractName( "create", methodName ); | |||||
// Add to the creators map | |||||
if( creators.containsKey( elemName ) ) | |||||
{ | |||||
final String message = | |||||
REZ.getString( "multiple-creator-methods-for-element.error", | |||||
m_class.getName(), | |||||
elemName ); | |||||
throw new ConfigurationException( message ); | |||||
} | |||||
creators.put( elemName, method ); | |||||
} | |||||
return creators; | |||||
} | |||||
/** | |||||
* Enables content. | |||||
*/ | |||||
public void enableContent() | |||||
throws ConfigurationException | |||||
{ | |||||
// Locate any 'addContent' methods, which return void, and take | |||||
// a single parameter. | |||||
final Method[] methods = m_class.getMethods(); | |||||
for( int i = 0; i < methods.length; i++ ) | |||||
{ | |||||
final Method method = methods[ i ]; | |||||
final String methodName = method.getName(); | |||||
if( Modifier.isStatic( method.getModifiers() ) || | |||||
!methodName.equals( "addContent" ) || | |||||
method.getReturnType() != Void.TYPE || | |||||
method.getParameterTypes().length != 1 ) | |||||
{ | |||||
continue; | |||||
} | |||||
if( null != m_contentSetter ) | |||||
{ | |||||
final String message = | |||||
REZ.getString( "multiple-content-setter-methods.error", m_class.getName() ); | |||||
throw new ConfigurationException( message ); | |||||
} | |||||
m_contentSetter = new DefaultAttributeSetter( method ); | |||||
} | |||||
} | |||||
/** | |||||
* Locates the configurer for a particular class. | |||||
*/ | |||||
public static ObjectConfigurer getConfigurer( final Class classInfo ) | |||||
throws ConfigurationException | |||||
{ | |||||
final DefaultObjectConfigurer configurer = new DefaultObjectConfigurer( classInfo ); | |||||
configurer.enableAll(); | |||||
return configurer; | |||||
} | |||||
/** | |||||
* Returns the class. | |||||
*/ | |||||
public Class getType() | |||||
{ | |||||
return m_class; | |||||
} | |||||
/** | |||||
* Returns a configurer for an attribute of this class. | |||||
*/ | |||||
public AttributeSetter getAttributeSetter( final String name ) | |||||
{ | |||||
return (AttributeSetter)m_attrs.get( name ); | |||||
} | |||||
/** | |||||
* Returns a configurer for an element of this class. | |||||
*/ | |||||
public ElementConfigurer getElement( final String name ) | |||||
{ | |||||
return (ElementConfigurer)m_elements.get( name ); | |||||
} | |||||
/** | |||||
* Returns a configurer for the content of this class. | |||||
*/ | |||||
public AttributeSetter getContentSetter() | |||||
{ | |||||
return m_contentSetter; | |||||
} | |||||
/** | |||||
* Enables an attribute. | |||||
*/ | |||||
private void enableAttribute( final String attrName, | |||||
final Method method ) | |||||
throws ConfigurationException | |||||
{ | |||||
if( m_attrs.containsKey( attrName ) ) | |||||
{ | |||||
final String message = | |||||
REZ.getString( "multiple-setter-methods-for-attribute.error", | |||||
m_class.getName(), | |||||
attrName ); | |||||
throw new ConfigurationException( message ); | |||||
} | |||||
final DefaultAttributeSetter setter = new DefaultAttributeSetter( method ); | |||||
m_attrs.put( attrName, setter ); | |||||
} | |||||
/** | |||||
* Extracts an attribute/element name from a Java method name. | |||||
* Removes the prefix, inserts '-' before each uppercase character | |||||
* (except the first), then converts all to lowercase. | |||||
*/ | |||||
private String extractName( final String prefix, final String methodName ) | |||||
{ | |||||
final StringBuffer sb = new StringBuffer( methodName ); | |||||
sb.delete( 0, prefix.length() ); | |||||
for( int i = 0; i < sb.length(); i++ ) | |||||
{ | |||||
char ch = sb.charAt( i ); | |||||
if( Character.isUpperCase( ch ) ) | |||||
{ | |||||
if( i > 0 ) | |||||
{ | |||||
sb.insert( i, '-' ); | |||||
i++; | |||||
} | |||||
sb.setCharAt( i, Character.toLowerCase( ch ) ); | |||||
} | |||||
} | |||||
return sb.toString(); | |||||
} | |||||
/** | |||||
* Locates all non-static methods whose name starts with a particular | |||||
* prefix. | |||||
*/ | |||||
private void findMethodsWithPrefix( final String prefix, final Collection matches ) | |||||
{ | |||||
final int prefixLen = prefix.length(); | |||||
final Method[] methods = m_class.getMethods(); | |||||
for( int i = 0; i < methods.length; i++ ) | |||||
{ | |||||
final Method method = methods[ i ]; | |||||
final String methodName = method.getName(); | |||||
if( Modifier.isStatic( method.getModifiers() ) || | |||||
methodName.length() <= prefixLen || | |||||
!methodName.startsWith( prefix ) ) | |||||
{ | |||||
continue; | |||||
} | |||||
matches.add( method ); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,47 @@ | |||||
/* | |||||
* 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.txt file. | |||||
*/ | |||||
package org.apache.myrmidon.components.configurer; | |||||
import org.apache.avalon.framework.configuration.ConfigurationException; | |||||
/** | |||||
* Configures an element of an object. | |||||
* | |||||
* @author <a href="mailto:adammurdoch_ml@yahoo.com">Adam Murdoch</a> | |||||
* @version $Revision$ $Date$ | |||||
*/ | |||||
public interface ElementConfigurer | |||||
{ | |||||
/** | |||||
* Returns the type of the element. | |||||
*/ | |||||
Class getType(); | |||||
/** | |||||
* Creates an object for an element. | |||||
* | |||||
* @param parent The parent object. | |||||
* @return An object which is assignable to the type returned by | |||||
* {@link #getType}. | |||||
* @throws ConfigurationException If the object cannot be created. | |||||
*/ | |||||
Object createElement( Object parent ) | |||||
throws ConfigurationException; | |||||
/** | |||||
* Attaches an element object to its parent, after it has been configured. | |||||
* | |||||
* @param parent The parent object. | |||||
* | |||||
* @param child The element object. This must be assignable to the type | |||||
* returned by {@link #getType}. | |||||
* @throws ConfigurationException If the object cannot be attached. | |||||
*/ | |||||
void addElement( Object parent, Object child ) | |||||
throws ConfigurationException; | |||||
} |
@@ -0,0 +1,48 @@ | |||||
/* | |||||
* 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.txt file. | |||||
*/ | |||||
package org.apache.myrmidon.components.configurer; | |||||
/** | |||||
* Configures objects of a particular class. | |||||
* | |||||
* @author <a href="mailto:adammurdoch_ml@yahoo.com">Adam Murdoch</a> | |||||
* @version $Revision$ $Date$ | |||||
*/ | |||||
public interface ObjectConfigurer | |||||
{ | |||||
/** | |||||
* Returns the class. | |||||
*/ | |||||
Class getType(); | |||||
/** | |||||
* Returns a configurer for an attribute of this class. | |||||
* | |||||
* @param name The attribute name. | |||||
* @return A configurer for the attribute. Returns null if the attribute | |||||
* is not valid for this class. | |||||
*/ | |||||
AttributeSetter getAttributeSetter( String name ); | |||||
/** | |||||
* Returns a configurer for an element of this class. | |||||
* | |||||
* @param name The element name. | |||||
* @return A configurer for the element. Returns null if the element | |||||
* is not valid for this class. | |||||
*/ | |||||
ElementConfigurer getElement( String name ); | |||||
/** | |||||
* Returns a configurer for the content of this class. | |||||
* | |||||
* @return A configurer for the content. Returns null if the class does | |||||
* not allow text content. | |||||
*/ | |||||
AttributeSetter getContentSetter(); | |||||
} |
@@ -1,18 +1,20 @@ | |||||
configuring-object.notice=Configuring {0}. | configuring-object.notice=Configuring {0}. | ||||
configurable.notice=Configuring object via Configurable interface. | configurable.notice=Configuring object via Configurable interface. | ||||
reflection.notice=Configuring object via Configurable reflection. | |||||
configure-attribute.notice=Configuring attribute name={0} value={1}. | |||||
configure-subelement.notice=Configuring subelement name={0}. | |||||
configure-content.notice=Configuring content {0}. | |||||
reflection.notice=Configuring object via ObjectConfigurer. | |||||
configure-content.notice=Configuring content with "{0}". | |||||
configure-subelement.notice=Configuring subelement "{0}". | |||||
configure-attribute.notice=Configuring attribute name="{0}" value="{1}". | |||||
reserved-attribute.error=Can not specify reserved attribute {0}. | |||||
no-attribute-method.error=Unable to set attribute via {0} due to not finding any appropriate mutator method. | |||||
bad-property-resolve.error=Error resolving property {0}. | |||||
no-can-convert.error=Unable to set attribute via {0} as could not convert {1} to a matching type. | |||||
no-converter.error=Failed to find converter. | |||||
bad-convert-for-attribute.error=Error converting attribute for {0}. | |||||
no-element-method.error=Unable to set element {0} due to not finding any appropriate mutator method. | |||||
illegal-access.error=Error retrieving methods with correct access specifiers. | |||||
invoke-target.error=Error calling method attribute {0}. | |||||
content-not-supported.error=Text content is not supported for this element. | |||||
bad-set-content.error=Could not set text content. | |||||
unknown-subelement.error=Unknown element "{0}". | |||||
bad-configure-subelement.error=Could not configure element "{0}". | |||||
unknown-attribute.error=Unknown attribute "{0}". | |||||
bad-set-attribute.error=Could not set attribute "{0}". | |||||
no-complex-type.error=Can not get complex type for non-primitive type {0}. | no-complex-type.error=Can not get complex type for non-primitive type {0}. | ||||
subelement-create.error=Error creating sub-element. | |||||
multiple-creator-methods-for-element.error=Multiple creator methods found in class {0} for element "{0}". | |||||
multiple-adder-methods-for-element.error=Multiple adder methods found in class {0} for element "{0}". | |||||
incompatible-element-types.error=Incompatible creator and adder types for element "{0}" of class {1}. | |||||
multiple-content-setter-methods.error=Multiple content setter methods found in class {0}. | |||||
multiple-setter-methods-for-attribute.error=Multiple setter methods found in class {0} for attribute "{1}". |