* Handle references. References can appear as either an attribute or a nested element of an object: As an attribute: <javac classpath-ref="some-classpath"> As a nested element: <javac> <classpath-ref id="some-classpath"/> </javac> * Unify attributes and elements at the task interface. This patch changes the configurer so that the addX() and setX() methods have the same semantics. Each addX() or setX() method defines a property X, which can appear as either an attribute x or as nested <x> elements (or both). There may also be createX() method, which is used to create the property value to be configured. A property with a createX() method may only appear as a nested element. A quick summary of how the configurer configures an object: - For each attribute x-ref="id": - looks up the object using "id". - sets the value using setX()/addX(). - this cannot be used if the object has a createX() method. - For each attribute x="value": - resolves property references in the value. - converts the string value into the correct type. - sets the value using setX()/addX(). - this cannot be used if the object has a createX() method. - For each nested element <x-ref id="id"/>: - handled the same as attribute x-ref="id". - For each nested element <x>: - creates the value using the createX() method (if present) or the no-args constructor. - configures the value using the nested element. - sets the value using setX()/addX(). This is really only intended to be a temporary solution. I'd like to go through and standardise on either addX() or setX(), and possibly look at doing away with the createX() method. And there's plenty more stuff yet to be implemented. Submitted By: "Adam Murdoch" <adammurdoch@yahoo.com> git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@270761 13f79535-47bb-0310-9956-ffa450edef68master
@@ -1,35 +0,0 @@ | |||
/* | |||
* Copyright (C) The Apache Software Foundation. All rights reserved. | |||
* | |||
* This software is published under the terms of the Apache Software License | |||
* version 1.1, a copy of which has been included with this distribution in | |||
* the LICENSE.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; | |||
} |
@@ -1,66 +0,0 @@ | |||
/* | |||
* Copyright (C) The Apache Software Foundation. All rights reserved. | |||
* | |||
* This software is published under the terms of the Apache Software License | |||
* version 1.1, a copy of which has been included with this distribution in | |||
* the LICENSE.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 ); | |||
} | |||
} | |||
} |
@@ -19,6 +19,7 @@ import org.apache.avalon.framework.configuration.Configurable; | |||
import org.apache.avalon.framework.configuration.Configuration; | |||
import org.apache.avalon.framework.configuration.ConfigurationException; | |||
import org.apache.avalon.framework.context.Context; | |||
import org.apache.avalon.framework.context.ContextException; | |||
import org.apache.avalon.framework.logger.AbstractLogEnabled; | |||
import org.apache.avalon.framework.logger.LogEnabled; | |||
import org.apache.myrmidon.interfaces.configurer.Configurer; | |||
@@ -126,6 +127,30 @@ public class DefaultConfigurer | |||
} | |||
} | |||
/** | |||
* 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 | |||
{ | |||
// Locate the configurer for this object | |||
final ObjectConfigurer configurer = getConfigurer( object.getClass() ); | |||
// Set the attribute value | |||
setAttribute( configurer, object, name, value, context ); | |||
} | |||
/** | |||
* Sets the text content of an object. | |||
*/ | |||
@@ -143,15 +168,16 @@ public class DefaultConfigurer | |||
} | |||
// Set the content | |||
final AttributeSetter setter = configurer.getContentSetter(); | |||
if( null == setter ) | |||
final PropertyConfigurer contentConfigurer = configurer.getContentConfigurer(); | |||
if( null == contentConfigurer ) | |||
{ | |||
final String message = REZ.getString( "content-not-supported.error" ); | |||
throw new ConfigurationException( message ); | |||
} | |||
try | |||
{ | |||
setValue( setter, object, content, context ); | |||
setValue( contentConfigurer, object, content, context ); | |||
} | |||
catch( final Exception e ) | |||
{ | |||
@@ -161,72 +187,155 @@ public class DefaultConfigurer | |||
} | |||
/** | |||
* Creates and configures a nested element | |||
* Configures a property from a nested element. | |||
*/ | |||
private void configureElement( final ObjectConfigurer configurer, | |||
final Object object, | |||
final Configuration childConfig, | |||
final Configuration element, | |||
final Context context ) | |||
throws ConfigurationException | |||
{ | |||
final String childName = childConfig.getName(); | |||
final String elementName = element.getName(); | |||
if( DEBUG ) | |||
{ | |||
final String message = | |||
REZ.getString( "configure-subelement.notice", childName ); | |||
REZ.getString( "configure-subelement.notice", elementName ); | |||
getLogger().debug( message ); | |||
} | |||
if( elementName.endsWith( "-ref" ) ) | |||
{ | |||
// A reference | |||
configureReference( configurer, object, element, context ); | |||
} | |||
else | |||
{ | |||
// An inline object | |||
configureInline( configurer, object, element, context ); | |||
} | |||
} | |||
/** | |||
* Configure a property from an inline object. | |||
*/ | |||
private void configureInline( final ObjectConfigurer configurer, | |||
final Object object, | |||
final Configuration element, | |||
final Context context ) | |||
throws ConfigurationException | |||
{ | |||
final String elementName = element.getName(); | |||
// Locate the configurer for the child element | |||
final ElementConfigurer childConfigurer = configurer.getElement( childName ); | |||
final PropertyConfigurer childConfigurer = configurer.getProperty( elementName ); | |||
if( null == childConfigurer ) | |||
{ | |||
final String message = REZ.getString( "unknown-subelement.error", childName ); | |||
final String message = REZ.getString( "unknown-property.error", elementName ); | |||
throw new ConfigurationException( message ); | |||
} | |||
try | |||
{ | |||
// Create the child element | |||
final Object child = childConfigurer.createElement( object ); | |||
final Object child = childConfigurer.createValue( object ); | |||
// Configure the child element | |||
configure( child, childConfig, context ); | |||
configure( child, element, context ); | |||
// Set the child element | |||
childConfigurer.addElement( object, child ); | |||
childConfigurer.setValue( object, child ); | |||
} | |||
catch( final ConfigurationException ce ) | |||
{ | |||
final String message = | |||
REZ.getString( "bad-configure-subelement.error", childName ); | |||
REZ.getString( "bad-set-property.error", elementName ); | |||
throw new ConfigurationException( message, ce ); | |||
} | |||
} | |||
/** | |||
* 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 | |||
* Configures a property from a reference. | |||
*/ | |||
public void configure( final Object object, | |||
final String name, | |||
final String value, | |||
final Context context ) | |||
private void configureReference( final ObjectConfigurer configurer, | |||
final Object object, | |||
final Configuration element, | |||
final Context context ) | |||
throws ConfigurationException | |||
{ | |||
// Locate the configurer for this object | |||
final ObjectConfigurer configurer = getConfigurer( object.getClass() ); | |||
// Adjust the name | |||
final String elementName = element.getName(); | |||
final String name = elementName.substring( 0, elementName.length() - 4 ); | |||
// Extract the id | |||
final String id = element.getAttribute( "id" ); | |||
if( 1 != element.getAttributeNames().length || | |||
0 != element.getChildren().length ) | |||
{ | |||
final String message = REZ.getString( "extra-config-for-ref.error" ); | |||
throw new ConfigurationException( message ); | |||
} | |||
// Set the attribute value | |||
setAttribute( configurer, object, name, value, context ); | |||
// Set the property | |||
setReference( configurer, object, name, id, context ); | |||
} | |||
/** | |||
* Sets a property using a reference. | |||
*/ | |||
private void setReference( final ObjectConfigurer configurer, | |||
final Object object, | |||
final String name, | |||
final String id, | |||
final Context context ) | |||
throws ConfigurationException | |||
{ | |||
// Locate the configurer for the child element | |||
final PropertyConfigurer childConfigurer = configurer.getProperty( name ); | |||
if( null == childConfigurer ) | |||
{ | |||
final String message = REZ.getString( "unknown-property.error", name ); | |||
throw new ConfigurationException( message ); | |||
} | |||
// Check if the creator method must be used | |||
if( childConfigurer.useCreator() ) | |||
{ | |||
final String message = REZ.getString( "must-be-element.error" ); | |||
throw new ConfigurationException( message ); | |||
} | |||
// Locate the referenced object | |||
Object ref = null; | |||
try | |||
{ | |||
ref = context.get( id ); | |||
} | |||
catch( final ContextException ce ) | |||
{ | |||
final String message = REZ.getString( "get-ref.error", id, name ); | |||
throw new ConfigurationException( message, ce ); | |||
} | |||
// Check the types | |||
final Class type = childConfigurer.getType(); | |||
if( !type.isInstance( ref ) ) | |||
{ | |||
final String message = REZ.getString( "mismatch-ref-types.error", id, name ); | |||
throw new ConfigurationException( message ); | |||
} | |||
// Set the child element | |||
try | |||
{ | |||
childConfigurer.setValue( object, ref ); | |||
} | |||
catch( final ConfigurationException ce ) | |||
{ | |||
final String message = | |||
REZ.getString( "bad-set-property.error", name ); | |||
throw new ConfigurationException( message, ce ); | |||
} | |||
} | |||
/** | |||
@@ -247,49 +356,60 @@ public class DefaultConfigurer | |||
getLogger().debug( message ); | |||
} | |||
// Locate the setter for this attribute | |||
final AttributeSetter setter = configurer.getAttributeSetter( name ); | |||
if( null == setter ) | |||
if( name.endsWith( "-ref" ) ) | |||
{ | |||
final String message = REZ.getString( "unknown-attribute.error", name ); | |||
throw new ConfigurationException( message ); | |||
// A reference | |||
final String refName = name.substring( 0, name.length() - 4 ); | |||
setReference( configurer, object, refName, value, context ); | |||
} | |||
// Set the value | |||
try | |||
{ | |||
setValue( setter, object, value, context ); | |||
} | |||
catch( final Exception e ) | |||
else | |||
{ | |||
final String message = REZ.getString( "bad-set-attribute.error", name ); | |||
throw new ConfigurationException( message, e ); | |||
// Locate the configurer for this attribute | |||
final PropertyConfigurer propConfigurer = configurer.getProperty( name ); | |||
if( null == propConfigurer ) | |||
{ | |||
final String message = REZ.getString( "unknown-property.error", name ); | |||
throw new ConfigurationException( message ); | |||
} | |||
// Set the value | |||
try | |||
{ | |||
setValue( propConfigurer, object, value, context ); | |||
} | |||
catch( final Exception e ) | |||
{ | |||
final String message = REZ.getString( "bad-set-property.error", name ); | |||
throw new ConfigurationException( message, e ); | |||
} | |||
} | |||
} | |||
/** | |||
* Sets an attribute value, or an element's text content. | |||
*/ | |||
private void setValue( final AttributeSetter setter, | |||
private void setValue( final PropertyConfigurer setter, | |||
final Object object, | |||
final String value, | |||
final Context context ) | |||
throws Exception | |||
{ | |||
// Check if the creator method must be used | |||
if( setter.useCreator() ) | |||
{ | |||
final String message = REZ.getString( "must-be-element.error" ); | |||
throw new ConfigurationException( message ); | |||
} | |||
// Resolve property references in the attribute value | |||
Object objValue = PropertyUtil.resolveProperty( value, context, false ); | |||
// Convert the value to the appropriate type | |||
Class clazz = setter.getType(); | |||
if( clazz.isPrimitive() ) | |||
{ | |||
clazz = getComplexTypeFor( clazz ); | |||
} | |||
final Class clazz = setter.getType(); | |||
objValue = m_converter.convert( clazz, objValue, context ); | |||
// Set the value | |||
setter.setAttribute( object, objValue ); | |||
setter.setValue( object, objValue ); | |||
} | |||
/** | |||
@@ -307,45 +427,4 @@ public class DefaultConfigurer | |||
} | |||
return configurer; | |||
} | |||
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 | |||
{ | |||
final String message = REZ.getString( "no-complex-type.error", clazz.getName() ); | |||
throw new IllegalArgumentException( message ); | |||
} | |||
} | |||
} |
@@ -7,23 +7,17 @@ | |||
*/ | |||
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; | |||
import java.lang.reflect.Method; | |||
import java.lang.reflect.Modifier; | |||
import java.util.*; | |||
/** | |||
* An object configurer which uses reflection to determine the attributes | |||
* and elements of a class. | |||
* An object configurer which uses reflection to determine the properties | |||
* of a class. | |||
* | |||
* @author <a href="mailto:adammurdoch_ml@yahoo.com">Adam Murdoch</a> | |||
* @version $Revision$ $Date$ | |||
@@ -37,19 +31,14 @@ public class DefaultObjectConfigurer | |||
private final Class m_class; | |||
/** | |||
* Map from lowercase attribute name -> AttributeSetter. | |||
* Map from lowercase property name -> PropertyConfigurer. | |||
*/ | |||
private final Map m_attrs = new HashMap(); | |||
private final Map m_props = new HashMap(); | |||
/** | |||
* Map from lowercase element name -> ElementSetter. | |||
* Content configurer. | |||
*/ | |||
private final Map m_elements = new HashMap(); | |||
/** | |||
* Content setter. | |||
*/ | |||
private AttributeSetter m_contentSetter; | |||
private PropertyConfigurer m_contentConfigurer; | |||
/** | |||
* Creates an object configurer for a particular class. The newly | |||
@@ -62,47 +51,20 @@ public class DefaultObjectConfigurer | |||
} | |||
/** | |||
* Enables all attributes, elements and content handling. | |||
* Enables all properties and content handling. | |||
*/ | |||
public void enableAll() | |||
throws ConfigurationException | |||
{ | |||
enableAttributes(); | |||
enableElements(); | |||
// TODO - get rid of creators, and either setter or adders | |||
enableAdders(); | |||
enableContent(); | |||
} | |||
/** | |||
* Enables all attributes. | |||
* Enables all creators + adders. | |||
*/ | |||
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() | |||
public void enableAdders() | |||
throws ConfigurationException | |||
{ | |||
final Map creators = findCreators(); | |||
@@ -116,9 +78,9 @@ public class DefaultObjectConfigurer | |||
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 ); | |||
final String propName = (String)iterator.next(); | |||
final Method createMethod = (Method)creators.get( propName ); | |||
final Method addMethod = (Method)adders.get( propName ); | |||
// Determine and check the return type | |||
Class type; | |||
@@ -132,7 +94,7 @@ public class DefaultObjectConfigurer | |||
{ | |||
final String message = | |||
REZ.getString( "incompatible-element-types.error", | |||
elemName, | |||
propName, | |||
m_class.getName() ); | |||
throw new ConfigurationException( message ); | |||
} | |||
@@ -146,14 +108,15 @@ public class DefaultObjectConfigurer | |||
type = addMethod.getParameterTypes()[ 0 ]; | |||
} | |||
final DefaultElementConfigurer configurer = | |||
new DefaultElementConfigurer( type, createMethod, addMethod ); | |||
m_elements.put( elemName, configurer ); | |||
final DefaultPropertyConfigurer configurer = | |||
new DefaultPropertyConfigurer( type, createMethod, addMethod ); | |||
m_props.put( propName, configurer ); | |||
} | |||
} | |||
/** | |||
* Locate all 'add' methods which return void, and take a non-primitive type | |||
* Locate all 'add' and 'set' methods which return void, and take a | |||
* single parameter. | |||
*/ | |||
private Map findAdders() | |||
throws ConfigurationException | |||
@@ -161,6 +124,7 @@ public class DefaultObjectConfigurer | |||
final Map adders = new HashMap(); | |||
final List methodSet = new ArrayList(); | |||
findMethodsWithPrefix( "add", methodSet ); | |||
findMethodsWithPrefix( "set", methodSet ); | |||
final Iterator iterator = methodSet.iterator(); | |||
while( iterator.hasNext() ) | |||
@@ -168,8 +132,7 @@ public class DefaultObjectConfigurer | |||
final Method method = (Method)iterator.next(); | |||
final String methodName = method.getName(); | |||
if( method.getReturnType() != Void.TYPE || | |||
method.getParameterTypes().length != 1 || | |||
method.getParameterTypes()[ 0 ].isPrimitive() ) | |||
method.getParameterTypes().length != 1 ) | |||
{ | |||
continue; | |||
} | |||
@@ -181,7 +144,7 @@ public class DefaultObjectConfigurer | |||
} | |||
// Extract element name | |||
final String elemName = extractName( "add", methodName ); | |||
final String elemName = extractName( 3, methodName ); | |||
// Add to the adders map | |||
if( adders.containsKey( elemName ) ) | |||
@@ -220,7 +183,7 @@ public class DefaultObjectConfigurer | |||
} | |||
// Extract element name | |||
final String elemName = extractName( "create", methodName ); | |||
final String elemName = extractName( 6, methodName ); | |||
// Add to the creators map | |||
if( creators.containsKey( elemName ) ) | |||
@@ -257,14 +220,16 @@ public class DefaultObjectConfigurer | |||
continue; | |||
} | |||
if( null != m_contentSetter ) | |||
// Check for multiple content setters | |||
if( null != m_contentConfigurer ) | |||
{ | |||
final String message = | |||
REZ.getString( "multiple-content-setter-methods.error", m_class.getName() ); | |||
throw new ConfigurationException( message ); | |||
} | |||
m_contentSetter = new DefaultAttributeSetter( method ); | |||
Class type = method.getParameterTypes()[0]; | |||
m_contentConfigurer = new DefaultPropertyConfigurer( type, null, method ); | |||
} | |||
} | |||
@@ -287,58 +252,32 @@ public class DefaultObjectConfigurer | |||
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 ) | |||
public PropertyConfigurer getProperty( final String name ) | |||
{ | |||
return (ElementConfigurer)m_elements.get( name ); | |||
return (PropertyConfigurer)m_props.get( name ); | |||
} | |||
/** | |||
* Returns a configurer for the content of this class. | |||
*/ | |||
public AttributeSetter getContentSetter() | |||
public PropertyConfigurer getContentConfigurer() | |||
{ | |||
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 ); | |||
return m_contentConfigurer; | |||
} | |||
/** | |||
* Extracts an attribute/element name from a Java method name. | |||
* Removes the prefix, inserts '-' before each uppercase character | |||
* Extracts a property name from a Java method name. | |||
* | |||
* <p>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 ) | |||
private String extractName( final int prefixLen, final String methodName ) | |||
{ | |||
final StringBuffer sb = new StringBuffer( methodName ); | |||
sb.delete( 0, prefix.length() ); | |||
sb.delete( 0, prefixLen ); | |||
for( int i = 0; i < sb.length(); i++ ) | |||
{ | |||
char ch = sb.charAt( i ); | |||
@@ -7,33 +7,38 @@ | |||
*/ | |||
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; | |||
import java.lang.reflect.InvocationTargetException; | |||
import java.lang.reflect.Method; | |||
/** | |||
* The default element configurer implementation, which uses reflection to | |||
* create and/or add nested elements. | |||
* The default property configurer implementation, which uses reflection to | |||
* create and set property values. | |||
* | |||
* @author <a href="mailto:adammurdoch_ml@yahoo.com">Adam Murdoch</a> | |||
* @version $Revision$ $Date$ | |||
*/ | |||
class DefaultElementConfigurer | |||
implements ElementConfigurer | |||
class DefaultPropertyConfigurer | |||
implements PropertyConfigurer | |||
{ | |||
private static final Resources REZ = | |||
ResourceManager.getPackageResources( DefaultElementConfigurer.class ); | |||
ResourceManager.getPackageResources( DefaultPropertyConfigurer.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 ) | |||
public DefaultPropertyConfigurer( Class type, | |||
Method createMethod, | |||
Method addMethod ) | |||
{ | |||
if ( type.isPrimitive() ) | |||
{ | |||
type = getComplexTypeFor(type); | |||
} | |||
m_type = type; | |||
m_createMethod = createMethod; | |||
m_addMethod = addMethod; | |||
@@ -47,10 +52,18 @@ class DefaultElementConfigurer | |||
return m_type; | |||
} | |||
/** | |||
* Determines if the property value must be created via {@link #createValue}. | |||
*/ | |||
public boolean useCreator() | |||
{ | |||
return (m_createMethod != null); | |||
} | |||
/** | |||
* Creates a nested element. | |||
*/ | |||
public Object createElement( final Object parent ) | |||
public Object createValue( final Object parent ) | |||
throws ConfigurationException | |||
{ | |||
try | |||
@@ -78,7 +91,7 @@ class DefaultElementConfigurer | |||
/** | |||
* Sets the nested element, after it has been configured. | |||
*/ | |||
public void addElement( final Object parent, final Object child ) | |||
public void setValue( final Object parent, final Object child ) | |||
throws ConfigurationException | |||
{ | |||
try | |||
@@ -98,4 +111,48 @@ class DefaultElementConfigurer | |||
throw new ConfigurationException( e.getMessage(), e ); | |||
} | |||
} | |||
/** | |||
* Determines the complex type for a prmitive type. | |||
*/ | |||
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 | |||
{ | |||
final String message = REZ.getString( "no-complex-type.error", clazz.getName() ); | |||
throw new IllegalArgumentException( message ); | |||
} | |||
} | |||
} |
@@ -21,22 +21,13 @@ public interface ObjectConfigurer | |||
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. | |||
* Returns a configurer for a property of this class. | |||
* | |||
* @param name The element name. | |||
* @return A configurer for the element. Returns null if the element | |||
* @return A configurer for the property. Returns null if the property | |||
* is not valid for this class. | |||
*/ | |||
ElementConfigurer getElement( String name ); | |||
PropertyConfigurer getProperty( String name ); | |||
/** | |||
* Returns a configurer for the content of this class. | |||
@@ -44,5 +35,5 @@ public interface ObjectConfigurer | |||
* @return A configurer for the content. Returns null if the class does | |||
* not allow text content. | |||
*/ | |||
AttributeSetter getContentSetter(); | |||
PropertyConfigurer getContentConfigurer(); | |||
} |
@@ -10,38 +10,45 @@ package org.apache.myrmidon.components.configurer; | |||
import org.apache.avalon.framework.configuration.ConfigurationException; | |||
/** | |||
* Configures an element of an object. | |||
* Configures a property of an object. | |||
* TODO - axe useCreator() and createValue(). | |||
* | |||
* @author <a href="mailto:adammurdoch_ml@yahoo.com">Adam Murdoch</a> | |||
* @version $Revision$ $Date$ | |||
*/ | |||
public interface ElementConfigurer | |||
public interface PropertyConfigurer | |||
{ | |||
/** | |||
* Returns the type of the element. | |||
* Returns the type of the property. | |||
*/ | |||
Class getType(); | |||
/** | |||
* Creates an object for an element. | |||
* Determines if the property value must be created via {@link #createValue}. | |||
*/ | |||
boolean useCreator(); | |||
/** | |||
* Creates a default value for the property. This value must be configured, | |||
* and then attached to the object using {@link #setValue}. This | |||
* method must be called if {@link #useCreator} returns true. | |||
* | |||
* @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 ) | |||
Object createValue( Object parent ) | |||
throws ConfigurationException; | |||
/** | |||
* Attaches an element object to its parent, after it has been configured. | |||
* | |||
* @param parent The parent object. | |||
* Sets a property value for an object. | |||
* | |||
* @param child The element object. This must be assignable to the type | |||
* @param object The object to set the property of. | |||
* @param value The property value. This must be assignable to the type | |||
* returned by {@link #getType}. | |||
* @throws ConfigurationException If the object cannot be attached. | |||
* @throws ConfigurationException If the property cannot be set. | |||
*/ | |||
void addElement( Object parent, Object child ) | |||
void setValue( Object object, Object value ) | |||
throws ConfigurationException; | |||
} |
@@ -7,14 +7,14 @@ configure-attribute.notice=Configuring attribute name="{0}" value="{1}". | |||
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}". | |||
unknown-property.error=Unknown property "{0}". | |||
bad-set-property.error=Could not set property "{0}". | |||
no-complex-type.error=Can not get complex type for non-primitive type {0}. | |||
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}. | |||
extra-config-for-ref.error=A reference element can only include an "id" attribute. | |||
get-ref.error=Could not locate reference "{0}" for element "{1}". | |||
mismatch-ref-types.error=Mismatched type for reference "{0}" for element "{1}". | |||
multiple-creator-methods-for-element.error=Multiple creator methods found in class {0} for property "{0}". | |||
multiple-adder-methods-for-element.error=Multiple adder/setter methods found in class {0} for property "{0}". | |||
incompatible-element-types.error=Incompatible creator and adder/setter types for property "{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}". | |||
must-be-element.error=This property must be configured using a nested element. |