Hi, This patch adds bunch of minor features to the configurer:
* Added max multiplicity checking. Properties with a setter method can only
be set once, whereas properties with an adder method can be set an unlimited
number of times.
* Resolves properties in reference ids. e.g
<javac classpath-ref="${my-classpath-id-name}"/>
* Ignores String adder and setter methods, if other methods exist. Longer
term, the type should be able to specify exactly which method to use.
* Moved all per-object state behind the ConfigurationState interface. The
ObjectConfigurer is now responsible for state-based validation.
* Tidied-up error messages. More context info is available in error
messages, to make figuring out the problem easier. Error messages still
need work.
* Created a unit test suite. Added some tests for DefaultConfigurer.
Adam
Index: proposal/myrmidon/build.xml
===================================================================
RCS file: /home/cvspublic/jakarta-ant/proposal/myrmidon/build.xml,v
retrieving revision 1.40
diff -u -r1.40 build.xml
--- proposal/myrmidon/build.xml 19 Jan 2002 07:41:47 -0000 1.40
+++ proposal/myrmidon/build.xml 22 Jan 2002 04:41:51 -0000
@@ -59,6 +59,8 @@
<property name="dist.lib" value="${dist.dir}/lib"/>
<property name="dist.ext" value="${dist.dir}/ext"/>
+ <property name="test.classes" value="${build.dir}/test/classes"/>
+
<property name="constants.file" value="org/apache/myrmidon/Constants.java"/>
<path id="project.class.path">
@@ -477,9 +479,31 @@
</delete>
</target>
+ <!-- Rebuilds the distribution -->
<target name="rebuild">
<antcall target="clean"/>
<antcall target="main"/>
+ </target>
+
+ <!-- Compiles and runs the unit tests -->
+ <target name="test" depends="compile" if="junit.present">
+ <mkdir dir="${test.classes}"/>
+ <javac srcdir="src/testcases"
+ destdir="${test.classes}"
+ debug="${debug}"
+ optimize="${optimize}"
+ deprecation="${deprecation}">
+ <classpath refid="project.class.path"/>
+ </javac>
+ <junit printsummary="on"
+ fork="false">
+ <formatter type="brief" usefile="false"/>
+ <classpath refid="project.class.path"/>
+ <classpath location="${test.classes}"/>
+ <batchtest>
+ <fileset dir="${test.classes}" includes="**/*Test.class"/>
+ </batchtest>
+ </junit>
</target>
</project>
Index:
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultConfigurer.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultConfigurer.java,v
retrieving revision 1.18
diff -u -r1.18 DefaultConfigurer.java
---
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultConfigurer.java
20 Jan 2002 17:32:57 -0000 1.18
+++
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultConfigurer.java
22 Jan 2002 04:41:54 -0000
@@ -7,11 +7,13 @@
*/
package org.apache.myrmidon.components.configurer;
+import java.lang.reflect.InvocationTargetException;
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.PropertyUtil;
+import org.apache.avalon.framework.CascadingException;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.Composable;
@@ -37,9 +39,6 @@
private final static Resources REZ =
ResourceManager.getPackageResources( DefaultConfigurer.class );
- ///Compile time constant to turn on extreme debugging
- private final static boolean DEBUG = false;
-
///Converter to use for converting between values
private MasterConverter m_converter;
@@ -71,50 +70,90 @@
final Context context )
throws ConfigurationException
{
- if( DEBUG )
+ try
{
- final String message = REZ.getString( "configuring-object.notice",
object );
- getLogger().debug( message );
+ configureObject( object, configuration, context );
}
+ catch( InvocationTargetException ite )
+ {
+ // A configuration exception thrown from a nested object. Unpack
+ // and re-throw
+ throw (ConfigurationException)ite.getTargetException();
+ }
+ }
+ /**
+ * Does the work of configuring an object.
+ */
+ private void configureObject( final Object object,
+ final Configuration configuration,
+ final Context context )
+ throws ConfigurationException, InvocationTargetException
+ {
if( object instanceof Configurable )
{
- if( DEBUG )
- {
- final String message = REZ.getString( "configurable.notice" );
- getLogger().debug( message );
- }
-
// Let the object configure itself
( (Configurable)object ).configure( configuration );
}
else
{
- if( DEBUG )
- {
- final String message = REZ.getString( "reflection.notice" );
- getLogger().debug( message );
- }
+ final String elemName = configuration.getName();
// Locate the configurer for this object
final ObjectConfigurer configurer = getConfigurer(
object.getClass() );
+ // Start configuring this object
+ final ConfigurationState state = configurer.startConfiguration(
object );
+
// 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 );
-
- // Set the attribute
- setAttribute( configurer, object, name, value, context );
+ try
+ {
+ // Set the attribute
+ final String value = configuration.getAttribute( name );
+ setAttribute( state, name, value, context );
+ }
+ catch( NoSuchPropertyException exc )
+ {
+ final String message = REZ.getString(
"no-such-attribute.error",
+ elemName,
+ name );
+ throw new ConfigurationException( message, exc );
+ }
+ catch( CascadingException exc )
+ {
+ final String message = REZ.getString(
"bad-set-attribute.error",
+ elemName,
+ name );
+ throw new ConfigurationException( message, exc );
+ }
}
// Set the text content
final String content = configuration.getValue( null );
if( null != content && content.length() > 0 )
{
- setContent( configurer, object, content, context );
+ try
+ {
+ // Set the content
+ final PropertyConfigurer contentConfigurer =
state.getConfigurer().getContentConfigurer();
+ setValue( contentConfigurer, state, content, context );
+ }
+ catch( NoSuchPropertyException exc )
+ {
+ final String message = REZ.getString( "no-content.error",
+ elemName );
+ throw new ConfigurationException( message, exc );
+ }
+ catch( CascadingException exc )
+ {
+ final String message = REZ.getString(
"bad-set-content.error",
+ elemName );
+ throw new ConfigurationException( message, exc );
+ }
}
// Create and configure each of the child elements
@@ -122,8 +161,28 @@
for( int i = 0; i < children.length; i++ )
{
final Configuration childConfig = children[ i ];
- configureElement( configurer, object, childConfig, context );
+ final String name = childConfig.getName();
+ try
+ {
+ configureElement( state, childConfig, context );
+ }
+ catch( NoSuchPropertyException exc )
+ {
+ final String message = REZ.getString(
"no-such-element.error",
+ elemName,
+ name );
+ throw new ConfigurationException( message, exc );
+ }
+ catch( CascadingException exc )
+ {
+ final String message = REZ.getString(
"bad-set-element.error",
+ name );
+ throw new ConfigurationException( message, exc );
+ }
}
+
+ // Finish configuring the object
+ configurer.finishConfiguration( state );
}
}
@@ -147,121 +206,100 @@
// 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.
- */
- private void setContent( final ObjectConfigurer configurer,
- final Object object,
- final String content,
- final Context context )
- throws ConfigurationException
- {
- if( DEBUG )
- {
- final String message =
- REZ.getString( "configure-content.notice", content );
- getLogger().debug( message );
- }
-
- // Set the content
- final PropertyConfigurer contentConfigurer =
configurer.getContentConfigurer();
- if( null == contentConfigurer )
- {
- final String message = REZ.getString(
"content-not-supported.error" );
- throw new ConfigurationException( message );
- }
+ // TODO - this ain't right, the validation is going to be screwed up
+ final ConfigurationState state = configurer.startConfiguration( object
);
+ // Set the attribute value
try
{
- setValue( contentConfigurer, object, content, context );
+ setAttribute( state, name, value, context );
}
- catch( final Exception e )
+ catch( CascadingException exc )
{
- final String message = REZ.getString( "bad-set-content.error" );
- throw new ConfigurationException( message, e );
+ final String message = REZ.getString(
"bad-set-class-attribute.error",
+ name,
+ object.getClass().getName()
);
+ throw new ConfigurationException( message, exc );
}
+
+ // Finish up
+ configurer.finishConfiguration( state );
}
/**
* Configures a property from a nested element.
*/
- private void configureElement( final ObjectConfigurer configurer,
- final Object object,
+ private void configureElement( final ConfigurationState state,
final Configuration element,
final Context context )
- throws ConfigurationException
+ throws CascadingException, InvocationTargetException
{
final String elementName = element.getName();
-
- if( DEBUG )
- {
- final String message =
- REZ.getString( "configure-subelement.notice", elementName );
- getLogger().debug( message );
- }
-
- if( elementName.endsWith( "-ref" ) )
+ if( elementName.toLowerCase().endsWith( "-ref" ) )
{
// A reference
- configureReference( configurer, object, element, context );
+ configureReference( state, element, context );
}
else
{
// An inline object
- configureInline( configurer, object, element, context );
+ configureInline( state, element, context );
}
}
/**
* Configure a property from an inline object.
*/
- private void configureInline( final ObjectConfigurer configurer,
- final Object object,
+ private void configureInline( final ConfigurationState state,
final Configuration element,
final Context context )
- throws ConfigurationException
+ throws CascadingException, InvocationTargetException
{
final String elementName = element.getName();
// Locate the configurer for the child element
- final PropertyConfigurer childConfigurer = configurer.getProperty(
elementName );
- if( null == childConfigurer )
+ final PropertyConfigurer childConfigurer =
state.getConfigurer().getProperty( elementName );
+
+ // Create the child element
+ Object child = childConfigurer.createValue( state );
+ if( child == null )
{
- final String message = REZ.getString( "unknown-property.error",
elementName );
- throw new ConfigurationException( message );
+ // Create an instance using the default constructor
+ try
+ {
+ child = childConfigurer.getType().newInstance();
+ }
+ catch( Exception exc )
+ {
+ final String message = REZ.getString( "create-object.error",
childConfigurer.getType().getName() );
+ throw new ConfigurationException( message, exc );
+ }
}
+ // Configure the child element
try
{
- // Create the child element
- final Object child = childConfigurer.createValue( object );
-
- // Configure the child element
- configure( child, element, context );
-
- // Set the child element
- childConfigurer.setValue( object, child );
+ configureObject( child, element, context );
}
- catch( final ConfigurationException ce )
+ catch( ConfigurationException exc )
{
- final String message =
- REZ.getString( "bad-set-property.error", elementName );
- throw new ConfigurationException( message, ce );
+ // Nasty hack-o-rama, used to get this exception up through
+ // the stack of doConfigure() calls. This is unpacked by the
+ // top-most configure() call, and rethrown.
+ throw new InvocationTargetException( exc );
}
+
+ // Set the child element
+ childConfigurer.addValue( state, child );
}
/**
* Configures a property from a reference.
*/
- private void configureReference( final ObjectConfigurer configurer,
- final Object object,
+ private void configureReference( final ConfigurationState state,
final Configuration element,
final Context context )
- throws ConfigurationException
+ throws CascadingException
{
// Adjust the name
final String elementName = element.getName();
@@ -277,33 +315,23 @@
}
// Set the property
- setReference( configurer, object, name, id, context );
+ setReference( state, name, id, context );
}
/**
* Sets a property using a reference.
*/
- private void setReference( final ObjectConfigurer configurer,
- final Object object,
+ private void setReference( final ConfigurationState state,
final String name,
- final String id,
+ final String unresolvedId,
final Context context )
- throws ConfigurationException
+ throws CascadingException
{
// 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 );
- }
+ final PropertyConfigurer childConfigurer =
state.getConfigurer().getProperty( name );
- // Check if the creator method must be used
- if( childConfigurer.useCreator() )
- {
- final String message = REZ.getString( "must-be-element.error" );
- throw new ConfigurationException( message );
- }
+ // Resolve any props in the id
+ Object id = PropertyUtil.resolveProperty( unresolvedId, context, false
);
// Locate the referenced object
Object ref = null;
@@ -311,77 +339,44 @@
{
ref = context.get( id );
}
- catch( final ContextException ce )
+ catch( final ContextException exc )
{
- final String message = REZ.getString( "get-ref.error", id, name );
- throw new ConfigurationException( message, ce );
+ final String message = REZ.getString( "get-ref.error", id );
+ throw new ConfigurationException( message, exc );
}
// Check the types
final Class type = childConfigurer.getType();
if( !type.isInstance( ref ) )
{
- final String message = REZ.getString( "mismatch-ref-types.error",
id, name );
+ final String message = REZ.getString( "mismatch-ref-types.error",
id, type.getName(), ref.getClass().getName() );
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 );
- }
+ childConfigurer.addValue( state, ref );
}
/**
* Sets an attribute value.
*/
- private void setAttribute( final ObjectConfigurer configurer,
- final Object object,
+ private void setAttribute( final ConfigurationState state,
final String name,
final String value,
final Context context )
- throws ConfigurationException
+ throws CascadingException
{
- if( DEBUG )
- {
- final String message = REZ.getString( "configure-attribute.notice",
- name,
- value );
- getLogger().debug( message );
- }
-
- if( name.endsWith( "-ref" ) )
+ if( name.toLowerCase().endsWith( "-ref" ) )
{
// A reference
final String refName = name.substring( 0, name.length() - 4 );
- setReference( configurer, object, refName, value, context );
+ setReference( state, refName, value, context );
}
else
{
- // 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 );
- }
+ final PropertyConfigurer propConfigurer =
state.getConfigurer().getProperty( name );
+ setValue( propConfigurer, state, value, context );
}
}
@@ -389,18 +384,11 @@
* Sets an attribute value, or an element's text content.
*/
private void setValue( final PropertyConfigurer setter,
- final Object object,
+ final ConfigurationState state,
final String value,
final Context context )
- throws Exception
+ throws CascadingException
{
- // 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
);
@@ -409,7 +397,7 @@
objValue = m_converter.convert( clazz, objValue, context );
// Set the value
- setter.setValue( object, objValue );
+ setter.addValue( state, objValue );
}
/**
Index:
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultObjectConfigurer.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultObjectConfigurer.java,v
retrieving revision 1.3
diff -u -r1.3 DefaultObjectConfigurer.java
---
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultObjectConfigurer.java
20 Jan 2002 17:32:57 -0000 1.3
+++
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultObjectConfigurer.java
22 Jan 2002 04:41:54 -0000
@@ -7,20 +7,19 @@
*/
package org.apache.myrmidon.components.configurer;
-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.Map;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
-import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.ArrayList;
-import java.util.Collection;
+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 properties
@@ -43,6 +42,11 @@
private final Map m_props = new HashMap();
/**
+ * All property configurers.
+ */
+ private final List m_allProps = new ArrayList();
+
+ /**
* Content configurer.
*/
private PropertyConfigurer m_contentConfigurer;
@@ -101,8 +105,8 @@
{
final String message =
REZ.getString( "incompatible-element-types.error",
- propName,
- m_class.getName() );
+ m_class.getName(),
+ propName );
throw new ConfigurationException( message );
}
}
@@ -115,9 +119,21 @@
type = addMethod.getParameterTypes()[ 0 ];
}
+ // Determine the max count for the property
+ int maxCount = Integer.MAX_VALUE;
+ if( addMethod != null && addMethod.getName().startsWith("set") )
+ {
+ maxCount = 1;
+ }
+
final DefaultPropertyConfigurer configurer =
- new DefaultPropertyConfigurer( type, createMethod, addMethod );
+ new DefaultPropertyConfigurer( m_allProps.size(),
+ type,
+ createMethod,
+ addMethod,
+ maxCount );
m_props.put( propName, configurer );
+ m_allProps.add( configurer );
}
}
@@ -138,8 +154,8 @@
{
final Method method = (Method)iterator.next();
final String methodName = method.getName();
- if( method.getReturnType() != Void.TYPE ||
- method.getParameterTypes().length != 1 )
+ if( method.getReturnType() != Void.TYPE
+ || method.getParameterTypes().length != 1 )
{
continue;
}
@@ -150,19 +166,37 @@
continue;
}
- // Extract element name
- final String elemName = extractName( 3, methodName );
+ // Extract property name
+ final String propName = extractName( 3, methodName );
+
+ final Class type = method.getParameterTypes()[0];
// Add to the adders map
- if( adders.containsKey( elemName ) )
+ if( adders.containsKey( propName ) )
{
- final String message =
- REZ.getString( "multiple-adder-methods-for-element.error",
- m_class.getName(),
- elemName );
- throw new ConfigurationException( message );
+ final Class currentType = ((Method)adders.get( propName
)).getParameterTypes()[0];
+
+ // Ditch the string version, if any
+ if( currentType != String.class && type == String.class )
+ {
+ // New type is string, and current type is not. Ignore
+ // the new method
+ continue;
+ }
+ if( currentType != String.class || type == String.class )
+ {
+ // Both are string, or both are not string
+ final String message =
+ REZ.getString(
"multiple-adder-methods-for-element.error",
+ m_class.getName(),
+ propName );
+ throw new ConfigurationException( message );
+ }
+
+ // Else, current type is string, and new type is not, so
+ // continue below, and overwrite the current method
}
- adders.put( elemName, method );
+ adders.put( propName, method );
}
return adders;
}
@@ -236,7 +270,12 @@
}
Class type = method.getParameterTypes()[0];
- m_contentConfigurer = new DefaultPropertyConfigurer( type, null,
method );
+ m_contentConfigurer = new DefaultPropertyConfigurer(
m_allProps.size(),
+ type,
+ null,
+ method,
+ 1 );
+ m_allProps.add( m_contentConfigurer );
}
}
@@ -252,27 +291,64 @@
}
/**
- * Returns the class.
+ * Starts the configuration of an object.
*/
- public Class getType()
+ public ConfigurationState startConfiguration( Object object )
+ throws ConfigurationException
{
- return m_class;
+ return new DefaultConfigurationState( this, object, m_allProps.size()
);
+ }
+
+ /**
+ * Finishes the configuration of an object, performing any final
+ * validation and type conversion.
+ */
+ public Object finishConfiguration( ConfigurationState state )
+ throws ConfigurationException
+ {
+ // Make sure there are no pending created objects
+ DefaultConfigurationState defState = (DefaultConfigurationState)state;
+ for( int i = 0; i < m_allProps.size(); i++ )
+ {
+ if( defState.getCreatedObject( i ) != null )
+ {
+ final String message = REZ.getString(
"pending-property-value.error" );
+ throw new ConfigurationException( message );
+ }
+ }
+
+ return defState.getObject();
}
/**
* Returns a configurer for an element of this class.
*/
- public PropertyConfigurer getProperty( final String name )
+ public PropertyConfigurer getProperty( final String name ) throws
NoSuchPropertyException
{
- return (PropertyConfigurer)m_props.get( name );
+ PropertyConfigurer prop = (PropertyConfigurer)m_props.get( name );
+ if( prop != null )
+ {
+ return prop;
+ }
+
+ // Unknown property
+ final String message = REZ.getString( "unknown-property.error",
m_class.getName(), name );
+ throw new NoSuchPropertyException( message );
}
/**
* Returns a configurer for the content of this class.
*/
- public PropertyConfigurer getContentConfigurer()
+ public PropertyConfigurer getContentConfigurer() throws
NoSuchPropertyException
{
- return m_contentConfigurer;
+ if( m_contentConfigurer != null )
+ {
+ return m_contentConfigurer;
+ }
+
+ // Does not handle content
+ final String message = REZ.getString( "content-unsupported.error",
m_class.getName() );
+ throw new NoSuchPropertyException( message );
}
/**
Index:
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultPropertyConfigurer.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultPropertyConfigurer.java,v
retrieving revision 1.2
diff -u -r1.2 DefaultPropertyConfigurer.java
---
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultPropertyConfigurer.java
20 Jan 2002 17:32:57 -0000 1.2
+++
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultPropertyConfigurer.java
22 Jan 2002 04:41:55 -0000
@@ -7,13 +7,12 @@
*/
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 property configurer implementation, which uses reflection to
* create and set property values.
@@ -27,21 +26,30 @@
private final static Resources REZ =
ResourceManager.getPackageResources( DefaultPropertyConfigurer.class );
+ private final int m_propIndex;
private final Class m_type;
private final Method m_createMethod;
private final Method m_addMethod;
+ private final int m_maxCount;
- public DefaultPropertyConfigurer( Class type,
- Method createMethod,
- Method addMethod )
+ public DefaultPropertyConfigurer( final int propIndex,
+ final Class type,
+ final Method createMethod,
+ final Method addMethod,
+ final int maxCount )
{
+ m_propIndex = propIndex;
if ( type.isPrimitive() )
{
- type = getComplexTypeFor(type);
+ m_type = getComplexTypeFor(type);
+ }
+ else
+ {
+ m_type = type;
}
- m_type = type;
m_createMethod = createMethod;
m_addMethod = addMethod;
+ m_maxCount = maxCount;
}
/**
@@ -53,29 +61,31 @@
}
/**
- * Determines if the property value must be created via [EMAIL PROTECTED]
#createValue}.
- */
- public boolean useCreator()
- {
- return (m_createMethod != null);
- }
-
- /**
- * Creates a nested element.
+ * Creates a default value for this property.
*/
- public Object createValue( final Object parent )
+ public Object createValue( ConfigurationState state )
throws ConfigurationException
{
+ if( null == m_createMethod )
+ {
+ return null;
+ }
+
+ final DefaultConfigurationState defState =
(DefaultConfigurationState)state;
+
+ // Make sure there isn't a pending object for this property
+ if( defState.getCreatedObject( m_propIndex ) != null )
+ {
+ final String message = REZ.getString(
"pending-property-value.error" );
+ throw new ConfigurationException( message );
+ }
+
try
{
- if( null != m_createMethod )
- {
- return m_createMethod.invoke( parent, null );
- }
- else
- {
- return m_type.newInstance();
- }
+ // Create the value
+ final Object object = m_createMethod.invoke( defState.getObject(),
null );
+ defState.setCreatedObject( m_propIndex, object );
+ return object;
}
catch( final InvocationTargetException ite )
{
@@ -89,16 +99,42 @@
}
/**
- * Sets the nested element, after it has been configured.
+ * Adds a value for this property, to an object.
*/
- public void setValue( final Object parent, final Object child )
+ public void addValue( ConfigurationState state, Object value )
throws ConfigurationException
{
+ final DefaultConfigurationState defState =
(DefaultConfigurationState)state;
+
+ // Make sure the supplied object is the pending object
+ final Object pending = defState.getCreatedObject( m_propIndex );
+ if( pending != null && pending != value )
+ {
+ }
+
+ // Make sure the creator method was called, if necessary
+ if( pending == null && m_createMethod != null )
+ {
+ final String message = REZ.getString( "must-be-element.error" );
+ throw new ConfigurationException( message );
+ }
+
+ defState.setCreatedObject( m_propIndex, null );
+
+ // Check the property count
+ if( defState.getPropCount( m_propIndex ) >= m_maxCount )
+ {
+ final String message = REZ.getString( "too-many-values.error" );
+ throw new ConfigurationException( message );
+ }
+ defState.incPropCount( m_propIndex );
+
try
{
+ // Add the value
if( null != m_addMethod )
{
- m_addMethod.invoke( parent, new Object[]{child} );
+ m_addMethod.invoke( defState.getObject(), new Object[]{ value
} );
}
}
catch( final InvocationTargetException ite )
Index:
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/ObjectConfigurer.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/ObjectConfigurer.java,v
retrieving revision 1.2
diff -u -r1.2 ObjectConfigurer.java
---
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/ObjectConfigurer.java
14 Jan 2002 09:31:22 -0000 1.2
+++
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/ObjectConfigurer.java
22 Jan 2002 04:41:55 -0000
@@ -7,6 +7,8 @@
*/
package org.apache.myrmidon.components.configurer;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+
/**
* Configures objects of a particular class.
*
@@ -16,24 +18,53 @@
public interface ObjectConfigurer
{
/**
- * Returns the class.
+ * Starts the configuration of an object.
+ *
+ * @param object
+ * The object about to be configured.
+ *
+ * @return
+ * The state object, used to track type-specific state during
+ * configuration.
+ *
+ * @throws ConfigurationException
+ * On error starting the configuration.
+ *
+ */
+ ConfigurationState startConfiguration( Object object )
+ throws ConfigurationException;
+
+ /**
+ * Finishes the configuration of an object, performing any final
+ * validation and type conversion.
+ *
+ * @param state
+ * The state object.
+ *
+ * @return
+ * The configured object.
+ *
+ * @throws ConfigurationException
+ * On error finishing the configurtion.
*/
- Class getType();
+ Object finishConfiguration( ConfigurationState state )
+ throws ConfigurationException;
/**
* Returns a configurer for a property of this class.
*
- * @param name The element name.
- * @return A configurer for the property. Returns null if the property
- * is not valid for this class.
+ * @param name The element name. Property names are case-insensitive.
+ * @return A configurer for the property.
+ * @throws NoSuchPropertyException If the property is not valid for this
+ * class
*/
- PropertyConfigurer getProperty( String name );
+ PropertyConfigurer getProperty( String name ) throws
NoSuchPropertyException;
/**
* 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.
+ * @return A configurer for the content.
+ * @throws NoSuchPropertyException If the class does not handle content.
*/
- PropertyConfigurer getContentConfigurer();
+ PropertyConfigurer getContentConfigurer() throws NoSuchPropertyException;
}
Index:
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/PropertyConfigurer.java
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/PropertyConfigurer.java,v
retrieving revision 1.1
diff -u -r1.1 PropertyConfigurer.java
---
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/PropertyConfigurer.java
14 Jan 2002 09:31:22 -0000 1.1
+++
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/PropertyConfigurer.java
22 Jan 2002 04:41:55 -0000
@@ -19,36 +19,32 @@
public interface PropertyConfigurer
{
/**
- * Returns the type of the property.
+ * Returns the type of this property.
*/
Class getType();
/**
- * Determines if the property value must be created via [EMAIL PROTECTED]
#createValue}.
- */
- boolean useCreator();
-
- /**
- * Creates a default value for the property. This value must be
configured,
+ * Creates a default value for this property. This value must be
configured,
* and then attached to the object using [EMAIL PROTECTED] #setValue}.
This
* method must be called if [EMAIL PROTECTED] #useCreator} returns true.
*
- * @param parent The parent object.
+ * @param state The state object, representing the object being configured.
* @return An object which is assignable to the type returned by
- * [EMAIL PROTECTED] #getType}.
+ * [EMAIL PROTECTED] #getType}. Returns null if this property
does not
+ * need a default value.
* @throws ConfigurationException If the object cannot be created.
*/
- Object createValue( Object parent )
+ Object createValue( ConfigurationState state )
throws ConfigurationException;
/**
- * Sets a property value for an object.
+ * Adds a value for this property, to an object.
*
- * @param object The object to set the property of.
+ * @param state The state object, representing the object being configured.
* @param value The property value. This must be assignable to the type
* returned by [EMAIL PROTECTED] #getType}.
* @throws ConfigurationException If the property cannot be set.
*/
- void setValue( Object object, Object value )
+ void addValue( ConfigurationState state, Object value )
throws ConfigurationException;
}
Index:
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/Resources.properties
===================================================================
RCS file:
/home/cvspublic/jakarta-ant/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/Resources.properties,v
retrieving revision 1.4
diff -u -r1.4 Resources.properties
---
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/Resources.properties
14 Jan 2002 09:31:22 -0000 1.4
+++
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/Resources.properties
22 Jan 2002 04:41:55 -0000
@@ -1,20 +1,21 @@
-configuring-object.notice=Configuring {0}.
-configurable.notice=Configuring object via Configurable interface.
-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}".
-
-content-not-supported.error=Text content is not supported for this element.
-bad-set-content.error=Could not set text content.
-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}.
+create-object.error=Could not create an object of class {0}.
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}.
+get-ref.error=Could not locate reference "{0}".
+mismatch-ref-types.error=Mismatched type for reference "{0}". Was expecting
an object of type {1}, instead found an object of type {2}.
+incompatible-element-types.error=Incompatible creator and adder/setter methods
found in class {0} for property "{1}".
+multiple-adder-methods-for-element.error=Multiple adder/setter methods found
in class {0} for property "{1}".
+multiple-creator-methods-for-element.error=Multiple creator methods found in
class {0} for property "{1}".
multiple-content-setter-methods.error=Multiple content setter methods found in
class {0}.
-must-be-element.error=This property must be configured using a nested element.
\ No newline at end of file
+pending-property-value.error=An object created using the creator method has
not been set using the adder/setter method.
+unknown-property.error=Class {0} does not have a "{1}" property.
+content-not-supported.error=Class {0} does not support text content.
+must-be-element.error=This property must be configured using a nested element.
+too-many-values.error=Too many values for this property.
+no-complex-type.error=Can not get complex type for non-primitive type {0}.
+no-such-attribute.error=Attribute "{1}" is not allowed for element <{0}>.
+bad-set-attribute.error=Could not set attribute "{1}" for element <{0}>.
+bad-set-class-attribute.error=Could not set attribute "{0}" for object of
class {1}.
+no-such-element.error=Nested <{1}> elements are not allowed for element <{0}>.
+bad-set-element.error=Could not handle element <{1}>, nested in element <{0}>.
+no-content.error=Text content is not allowed for element <{0}>.
+bad-set-content.error=Could not set text content for element <{0}>.
<<attachment: newfiles.zip>>
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>
