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; | |||
| 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.Resources; | |||
| import org.apache.avalon.excalibur.property.PropertyException; | |||
| import org.apache.avalon.excalibur.property.PropertyUtil; | |||
| import org.apache.avalon.framework.component.ComponentException; | |||
| 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.logger.AbstractLogEnabled; | |||
| 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.converter.MasterConverter; | |||
| @@ -42,18 +39,13 @@ public class DefaultConfigurer | |||
| ///Compile time constant to turn on extreme debugging | |||
| 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 | |||
| 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 ) | |||
| throws ComponentException | |||
| { | |||
| @@ -81,7 +73,7 @@ public class DefaultConfigurer | |||
| if( DEBUG ) | |||
| { | |||
| final String message = REZ.getString( "configuring-object.notice", object ); | |||
| getLogger().debug( "Configuring " + object ); | |||
| getLogger().debug( message ); | |||
| } | |||
| if( object instanceof Configurable ) | |||
| @@ -89,9 +81,10 @@ public class DefaultConfigurer | |||
| if( DEBUG ) | |||
| { | |||
| 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 ); | |||
| } | |||
| else | |||
| @@ -102,408 +95,257 @@ public class DefaultConfigurer | |||
| 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(); | |||
| for( int i = 0; i < attributes.length; i++ ) | |||
| { | |||
| final String name = attributes[ i ]; | |||
| 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 | |||
| { | |||
| 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 ) | |||
| 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 = | |||
| 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 | |||
| { | |||
| 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 = | |||
| 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 ) | |||
| 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 | |||
| { | |||
| 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 | |||
| { | |||
| 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}. | |||
| 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}. | |||
| 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}". | |||