@@ -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", metho dName );
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 c e )
{
final String message =
REZ.getString( "bad-property-resolve.error", valu e );
throw new ConfigurationException( message, p e );
REZ.getString( "bad-configure-subelement.error", childNam e );
throw new ConfigurationException( message, c e );
}
}
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( messag e );
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( messag e );
}
}
}