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]>

Reply via email to