Browse Source

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.


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@270822 13f79535-47bb-0310-9956-ffa450edef68
master
Peter Donald 23 years ago
parent
commit
f52a170159
9 changed files with 502 additions and 260 deletions
  1. +22
    -0
      proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/ConfigurationState.java
  2. +73
    -0
      proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultConfigurationState.java
  3. +149
    -160
      proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultConfigurer.java
  4. +108
    -31
      proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultObjectConfigurer.java
  5. +65
    -29
      proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultPropertyConfigurer.java
  6. +26
    -0
      proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/NoSuchPropertyException.java
  7. +31
    -9
      proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/ObjectConfigurer.java
  8. +9
    -13
      proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/PropertyConfigurer.java
  9. +19
    -18
      proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/Resources.properties

+ 22
- 0
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/ConfigurationState.java View File

@@ -0,0 +1,22 @@
/*
* 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;

/**
* A marker interface that represents the state of an object while it is being
* configured.
*
* @author Adam Murdoch
*/
public interface ConfigurationState
{
/**
* Returns the configurer being used to configure the object.
*/
ObjectConfigurer getConfigurer();
}

+ 73
- 0
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultConfigurationState.java View File

@@ -0,0 +1,73 @@
/*
* 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;

/**
* A default configuration state implementation. Keeps track of which
* of the object's properties have been set. Also keeps track of the
* objects created by the creator methods, but not yet set by the adder
* methods.
*
* @author Adam Murdoch
*/
public class DefaultConfigurationState
implements ConfigurationState
{
final private int[] m_propCount;
final private Object[] m_createdObjects;
final private ObjectConfigurer m_configurer;
final private Object m_object;

public DefaultConfigurationState( final ObjectConfigurer configurer,
final Object object,
final int numProps )
{
m_configurer = configurer;
m_object = object;
m_propCount = new int[ numProps ];
m_createdObjects = new Object[ numProps ];
}

/**
* Returns the configurer being used to configure the object.
*/
public ObjectConfigurer getConfigurer()
{
return m_configurer;
}

/** Returns the object being configured. */
public Object getObject()
{
return m_object;
}

/** Returns a property count. */
public int getPropCount( final int propIndex )
{
return m_propCount[ propIndex ];
}

/** Increments a property count. */
public void incPropCount( final int propIndex )
{
m_propCount[ propIndex ]++;
}

/** Returns a property's pending objects. */
public Object getCreatedObject( final int propIndex )
{
return m_createdObjects[ propIndex ];
}

/** Sets a property's pending objects. */
public void setCreatedObject( final int propIndex, final Object object )
{
m_createdObjects[ propIndex ] = object;
}
}

+ 149
- 160
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultConfigurer.java View File

@@ -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 @@ public class DefaultConfigurer
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,88 @@ public class DefaultConfigurer
final Context context )
throws ConfigurationException
{
if( DEBUG )
try
{
configureObject( object, configuration, context );
}
catch( InvocationTargetException ite )
{
final String message = REZ.getString( "configuring-object.notice", object );
getLogger().debug( message );
// 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( final NoSuchPropertyException nspe )
{
final String message =
REZ.getString( "no-such-attribute.error", elemName, name );
throw new ConfigurationException( message, nspe );
}
catch( final CascadingException ce )
{
final String message =
REZ.getString( "bad-set-attribute.error", elemName, name );
throw new ConfigurationException( message, ce );
}
}

// 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( final NoSuchPropertyException nspe )
{
final String message =
REZ.getString( "no-content.error", elemName );
throw new ConfigurationException( message, nspe );
}
catch( final CascadingException ce )
{
final String message =
REZ.getString( "bad-set-content.error", elemName );
throw new ConfigurationException( message, ce );
}
}

// Create and configure each of the child elements
@@ -122,8 +159,27 @@ public class DefaultConfigurer
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( final NoSuchPropertyException nspe )
{
final String message =
REZ.getString( "no-such-element.error", elemName, name );
throw new ConfigurationException( message, nspe );
}
catch( final CascadingException ce )
{
final String message =
REZ.getString( "bad-set-element.error", name );
throw new ConfigurationException( message, ce );
}
}

// Finish configuring the object
configurer.finishConfiguration( state );
}
}

@@ -147,121 +203,103 @@ public class DefaultConfigurer
// 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( final CascadingException ce )
{
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, ce );
}

// 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( final Exception e )
{
final String message =
REZ.getString( "create-object.error",
childConfigurer.getType().getName() );
throw new ConfigurationException( message, e );
}
}

// 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 )
{
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( ce );
}

// 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 @@ public class DefaultConfigurer
}

// 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,45 @@ public class DefaultConfigurer
{
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 +385,11 @@ public class DefaultConfigurer
* 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 +398,7 @@ public class DefaultConfigurer
objValue = m_converter.convert( clazz, objValue, context );

// Set the value
setter.setValue( object, objValue );
setter.addValue( state, objValue );
}

/**


+ 108
- 31
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultObjectConfigurer.java View File

@@ -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
@@ -42,6 +41,11 @@ public class DefaultObjectConfigurer
*/
private final Map m_props = new HashMap();

/**
* All property configurers.
*/
private final List m_allProps = new ArrayList();

/**
* Content configurer.
*/
@@ -101,8 +105,8 @@ public class DefaultObjectConfigurer
{
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 @@ public class DefaultObjectConfigurer
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 @@ public class DefaultObjectConfigurer
{
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 @@ public class DefaultObjectConfigurer
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;
}
@@ -235,8 +269,14 @@ public class DefaultObjectConfigurer
throw new ConfigurationException( message );
}

Class type = method.getParameterTypes()[0];
m_contentConfigurer = new DefaultPropertyConfigurer( type, null, method );
final Class type = method.getParameterTypes()[ 0 ];
m_contentConfigurer =
new DefaultPropertyConfigurer( m_allProps.size(),
type,
null,
method,
1 );
m_allProps.add( m_contentConfigurer );
}
}

@@ -252,27 +292,64 @@ public class DefaultObjectConfigurer
}

/**
* 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( final ConfigurationState state )
throws ConfigurationException
{
// Make sure there are no pending created objects
final 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 );
final 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 );
}

/**


+ 65
- 29
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultPropertyConfigurer.java View File

@@ -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 @@ class DefaultPropertyConfigurer
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 @@ class DefaultPropertyConfigurer
}

/**
* Determines if the property value must be created via {@link #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 @@ class DefaultPropertyConfigurer
}

/**
* 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 )


+ 26
- 0
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/NoSuchPropertyException.java View File

@@ -0,0 +1,26 @@
/*
* 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.CascadingException;

/**
* An exception thrown when an unknown property is encountered.
*
* TODO - this should extend ConfigurationException, however
* ConfigurationException is final.
*
* @author Adam Murdoch
*/
public class NoSuchPropertyException extends CascadingException
{
public NoSuchPropertyException( String message )
{
super( message );
}
}

+ 31
- 9
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/ObjectConfigurer.java View File

@@ -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,44 @@ package org.apache.myrmidon.components.configurer;
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;
}

+ 9
- 13
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/PropertyConfigurer.java View File

@@ -19,36 +19,32 @@ import org.apache.avalon.framework.configuration.ConfigurationException;
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 {@link #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 {@link #setValue}. This
* method must be called if {@link #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
* {@link #getType}.
* {@link #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 {@link #getType}.
* @throws ConfigurationException If the property cannot be set.
*/
void setValue( Object object, Object value )
void addValue( ConfigurationState state, Object value )
throws ConfigurationException;
}

+ 19
- 18
proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/Resources.properties View File

@@ -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.
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}>.

Loading…
Cancel
Save