diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultConfigurer.java b/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultConfigurer.java index a3775f8f6..f5f71b9e3 100644 --- a/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultConfigurer.java +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultConfigurer.java @@ -28,6 +28,8 @@ import org.apache.myrmidon.interfaces.converter.MasterConverter; import org.apache.myrmidon.interfaces.type.TypeException; import org.apache.myrmidon.interfaces.type.TypeFactory; import org.apache.myrmidon.interfaces.type.TypeManager; +import org.apache.myrmidon.interfaces.role.RoleManager; +import org.apache.myrmidon.framework.DataType; /** * Class used to configure tasks. @@ -48,6 +50,9 @@ public class DefaultConfigurer //TypeManager to use to create types in typed adders private TypeManager m_typeManager; + //RoleManager to use to map from type names -> role shorthand + private RoleManager m_roleManager; + ///Cached object configurers. This is a map from Class to the ///ObjectConfigurer for that class. private Map m_configurerCache = new HashMap(); @@ -57,6 +62,7 @@ public class DefaultConfigurer { m_converter = (MasterConverter)componentManager.lookup( MasterConverter.ROLE ); m_typeManager = (TypeManager)componentManager.lookup( TypeManager.ROLE ); + m_roleManager = (RoleManager)componentManager.lookup( RoleManager.ROLE ); } /** @@ -122,7 +128,6 @@ public class DefaultConfigurer } catch( final ConfigurationException ce ) { - ce.fillInStackTrace(); throw ce; } catch( final CascadingException ce ) @@ -151,7 +156,6 @@ public class DefaultConfigurer } catch( final ConfigurationException ce ) { - ce.fillInStackTrace(); throw ce; } catch( final CascadingException ce ) @@ -180,13 +184,12 @@ public class DefaultConfigurer } catch( final ConfigurationException ce ) { - ce.fillInStackTrace(); throw ce; } catch( final CascadingException ce ) { final String message = - REZ.getString( "bad-set-element.error", name ); + REZ.getString( "bad-set-element.error", elemName, name ); throw new ConfigurationException( message, ce ); } } @@ -269,8 +272,8 @@ public class DefaultConfigurer final String name = element.getName(); // Locate the configurer for the child element - final PropertyConfigurer childConfigurer = - state.getConfigurer().getProperty( name ); + final PropertyConfigurer childConfigurer + = getConfigurerFromName( state.getConfigurer(), name, true ); // Create & configure the child element final Object child = @@ -288,9 +291,6 @@ public class DefaultConfigurer final Context context ) throws CascadingException { - // Adjust the name - final String elementName = element.getName(); - final String name = elementName.substring( 0, elementName.length() - 4 ); // Extract the id final String id = element.getAttribute( "id" ); @@ -302,6 +302,7 @@ public class DefaultConfigurer } // Set the property + final String name = element.getName(); setReference( state, name, id, context ); } @@ -309,13 +310,17 @@ public class DefaultConfigurer * Sets a property using a reference. */ private void setReference( final ConfigurationState state, - final String name, + final String refName, final String unresolvedId, final Context context ) throws CascadingException { - // Locate the configurer for the child element - final PropertyConfigurer childConfigurer = state.getConfigurer().getProperty( name ); + // Adjust the name + final String name = refName.substring( 0, refName.length() - 4 ); + + // Locate the configurer for the property + final PropertyConfigurer childConfigurer + = getConfigurerFromName( state.getConfigurer(), name, false ); // Resolve any props in the id Object id = PropertyUtil.resolveProperty( unresolvedId, context, false ); @@ -356,14 +361,13 @@ public class DefaultConfigurer if( name.toLowerCase().endsWith( "-ref" ) ) { // A reference - final String refName = name.substring( 0, name.length() - 4 ); - setReference( state, refName, value, context ); + setReference( state, name, value, context ); } else { // Set the value - final PropertyConfigurer propConfigurer = - state.getConfigurer().getProperty( name ); + PropertyConfigurer propConfigurer + = getConfigurerFromName( state.getConfigurer(), name, false ); setValue( propConfigurer, state, value, context ); } } @@ -424,7 +428,7 @@ public class DefaultConfigurer } else if( null == child ) { - // Create an instance using the default constructor + // Create an instance if( type.isInterface() ) { child = createdTypedObject( name, type ); @@ -439,18 +443,62 @@ public class DefaultConfigurer return child; } + /** + * Determines the property configurer to use for a particular element + * or attribute. If the supplied name matches a property of the + * class being configured, that property configurer is returned. If + * the supplied name matches the role shorthand for the class' typed + * property, then the typed property configurer is used. + * + * @param configurer The configurer for the class being configured. + * @param name The attribute/element name. + */ + private PropertyConfigurer getConfigurerFromName( final ObjectConfigurer configurer, + final String name, + boolean ignoreRoleName ) + throws NoSuchPropertyException + { + // Try a named property + final NoSuchPropertyException exc; + try + { + return configurer.getProperty( name ); + } + catch( NoSuchPropertyException e ) + { + // Keep for later + exc = e; + } + + // Try a typed property + final PropertyConfigurer propertyConfigurer = configurer.getTypedProperty(); + if( ! ignoreRoleName ) + { + final String roleShorthand = m_roleManager.getNameForRole( propertyConfigurer.getType().getName() ); + if( ! name.equalsIgnoreCase(roleShorthand) ) + { + // Rethrow the original exception + throw exc; + } + } + + return propertyConfigurer; + } + /** * Utility method to create an instance of the - * specified type that satisfied supplied interface. + * specified type that satisfies supplied interface. */ private Object createdTypedObject( final String name, final Class type ) throws ConfigurationException { - final TypeFactory factory = getTypeFactory( type ); + // Attempt to create the object + final Object obj; try { - return factory.create( name ); + final TypeFactory factory = getTypeFactory( DataType.class ); + obj = factory.create( name ); } catch( final Exception e ) { @@ -460,6 +508,16 @@ public class DefaultConfigurer type.getName() ); throw new ConfigurationException( message, e ); } + + // Check the types + if( ! type.isInstance( obj ) ) + { + final String message = + REZ.getString( "mismatched-typed-object.error", name, type.getName() ); + throw new ConfigurationException( message ); + } + + return obj; } /** diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultObjectConfigurer.java b/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultObjectConfigurer.java index 00d836eb4..fe4ab3ddc 100644 --- a/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultObjectConfigurer.java +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/DefaultObjectConfigurer.java @@ -46,6 +46,11 @@ class DefaultObjectConfigurer */ private final List m_allProps = new ArrayList(); + /** + * The typed property configurer. + */ + private PropertyConfigurer m_typedPropConfigurer; + /** * Content configurer. */ @@ -67,15 +72,15 @@ class DefaultObjectConfigurer public void enableAll() throws ConfigurationException { - // TODO - get rid of creators, and either setter or adders - enableAdders(); + // TODO - get rid of creators + enableProperties(); enableContent(); } /** * Enables all creators + adders. */ - public void enableAdders() + private void enableProperties() throws ConfigurationException { final Map creators = findCreators(); @@ -119,6 +124,16 @@ class DefaultObjectConfigurer type = addMethod.getParameterTypes()[ 0 ]; } + final boolean isTypedProp = (propName.length() == 0); + if( isTypedProp && !type.isInterface() ) + { + final String message = + REZ.getString( "typed-adder-non-interface.error", + m_class.getName(), + type.getName() ); + throw new ConfigurationException( message ); + } + // Determine the max count for the property int maxCount = Integer.MAX_VALUE; if( addMethod != null && addMethod.getName().startsWith( "set" ) ) @@ -132,8 +147,15 @@ class DefaultObjectConfigurer createMethod, addMethod, maxCount ); - m_props.put( propName, configurer ); m_allProps.add( configurer ); + if( isTypedProp ) + { + m_typedPropConfigurer = configurer; + } + else + { + m_props.put( propName, configurer ); + } } } @@ -160,19 +182,7 @@ class DefaultObjectConfigurer continue; } - final boolean isTypedAdder = methodName.equals( "add" ); - - final Class paramType = method.getParameterTypes()[ 0 ]; - if( isTypedAdder && !paramType.isInterface() ) - { - final String message = - REZ.getString( "typed-adder-non-interface.error", - m_class.getName(), - paramType.getName() ); - throw new ConfigurationException( message ); - } - - // TODO - un-hard-code this + // Skip the text content method if( methodName.equals( "addContent" ) ) { continue; @@ -180,8 +190,7 @@ class DefaultObjectConfigurer // Extract property name final String propName = extractName( 3, methodName ); - - final Class type = paramType; + final Class type = method.getParameterTypes()[0]; // Add to the adders map if( adders.containsKey( propName ) ) @@ -190,15 +199,7 @@ class DefaultObjectConfigurer final Class currentType = candidate.getParameterTypes()[ 0 ]; // Ditch the string version, if any - if( isTypedAdder ) - { - // Both are string, or both are not string - final String message = - REZ.getString( "multiple-typed-adder-methods-for-element.error", - m_class.getName() ); - throw new ConfigurationException( message ); - } - else if( currentType != String.class && type == String.class ) + if( currentType != String.class && type == String.class ) { // New type is string, and current type is not. Ignore // the new method @@ -217,6 +218,7 @@ class DefaultObjectConfigurer // Else, current type is string, and new type is not, so // continue below, and overwrite the current method } + adders.put( propName, method ); } return adders; @@ -253,7 +255,7 @@ class DefaultObjectConfigurer final String message = REZ.getString( "multiple-creator-methods-for-element.error", m_class.getName(), - elemName ); + methodName ); throw new ConfigurationException( message ); } creators.put( elemName, method ); @@ -264,9 +266,13 @@ class DefaultObjectConfigurer /** * Enables content. */ - public void enableContent() + private void enableContent() throws ConfigurationException { + // TODO - should be using 'setContent', rather than 'addContent', + // to better match the call-at-most-once semantics of the other + // setter methods + // Locate any 'addContent' methods, which return void, and take // a single parameter. final Method[] methods = m_class.getMethods(); @@ -355,16 +361,25 @@ class DefaultObjectConfigurer return configurer; } - //Maybe there is a typed adder?? - configurer = (PropertyConfigurer)m_props.get( "" ); - if( null != configurer ) + // Unknown property + final String message = REZ.getString( "unknown-property.error", m_class.getName(), name ); + throw new NoSuchPropertyException( message ); + } + + /** + * Returns a configurer for the typed property of this class. + */ + public PropertyConfigurer getTypedProperty() + throws NoSuchPropertyException + { + if( null != m_typedPropConfigurer ) { - return configurer; + return m_typedPropConfigurer; } else { - // Unknown property - final String message = REZ.getString( "unknown-property.error", m_class.getName(), name ); + // No typed property + final String message = REZ.getString( "no-typed-property.error", m_class.getName() ); throw new NoSuchPropertyException( message ); } } diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/ObjectConfigurer.java b/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/ObjectConfigurer.java index de2db8a75..760374e7a 100644 --- a/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/ObjectConfigurer.java +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/ObjectConfigurer.java @@ -58,4 +58,14 @@ interface ObjectConfigurer */ PropertyConfigurer getContentConfigurer() throws NoSuchPropertyException; + + /** + * Returns a configurer for the typed property of this class. + * + * @return A configurer for the typed property. + * @throws NoSuchPropertyException If the class does not have a typed + * property. + */ + PropertyConfigurer getTypedProperty() + throws NoSuchPropertyException; } diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/Resources.properties b/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/Resources.properties index a26083651..e5a78ffd7 100644 --- a/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/Resources.properties +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/configurer/Resources.properties @@ -3,9 +3,8 @@ extra-config-for-ref.error=A reference element can only include an "id" attribut 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-typed-adder-methods-for-element.error=Multiple typed add() methods found in class {0}. -multiple-creator-methods-for-element.error=Multiple creator methods found in class {0} for property "{1}". +multiple-adder-methods-for-element.error=Multiple add{1}() or set{1}() methods found in class {0}. +multiple-creator-methods-for-element.error=Multiple {1}() methods found in class {0}. multiple-content-setter-methods.error=Multiple content setter methods found in class {0}. 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. @@ -22,4 +21,5 @@ no-content.error=Text content is not allowed for element <{0}>. bad-set-content.error=Could not set text content for element <{0}>. typed-adder-non-interface.error=The typed adder for class "{0}" must have a single parameter that is an interface rather than {1} which defines a class. no-factory-for-role.error=Unable to locate type factory for role "{0}" -create-typed-object.error=Could not create an object of type "{0}" of class {1}. \ No newline at end of file +create-typed-object.error=Could not create an object of type "{0}" of class {1}. +typed-property-not-supported.error=Class {0} does not have a typed property. \ No newline at end of file diff --git a/proposal/myrmidon/src/test/org/apache/myrmidon/components/AbstractComponentTest.java b/proposal/myrmidon/src/test/org/apache/myrmidon/components/AbstractComponentTest.java index baf96177d..378c19dde 100644 --- a/proposal/myrmidon/src/test/org/apache/myrmidon/components/AbstractComponentTest.java +++ b/proposal/myrmidon/src/test/org/apache/myrmidon/components/AbstractComponentTest.java @@ -38,6 +38,9 @@ import org.apache.myrmidon.interfaces.deployer.Deployer; import org.apache.myrmidon.interfaces.extensions.ExtensionManager; import org.apache.myrmidon.interfaces.role.RoleManager; import org.apache.myrmidon.interfaces.type.TypeManager; +import org.apache.myrmidon.interfaces.type.TypeException; +import org.apache.myrmidon.interfaces.type.DefaultTypeFactory; +import org.apache.myrmidon.converter.Converter; /** * A base class for tests for the default components. @@ -145,6 +148,21 @@ public abstract class AbstractComponentTest } } + /** + * Utility method to register a Converter. + */ + protected void registerConverter( final Class converterClass, + final Class sourceClass, + final Class destClass ) + throws ComponentException, TypeException + { + ConverterRegistry converterRegistry = (ConverterRegistry)getComponentManager().lookup( ConverterRegistry.ROLE ); + converterRegistry.registerConverter( converterClass.getName(), sourceClass.getName(), destClass.getName() ); + DefaultTypeFactory factory = new DefaultTypeFactory( getClass().getClassLoader() ); + factory.addNameClassMapping( converterClass.getName(), converterClass.getName() ); + getTypeManager().registerType( Converter.class, converterClass.getName(), factory ); + } + /** * Asserts that an exception contains the expected message. * diff --git a/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/ConfigTest10.java b/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/ConfigTest10.java new file mode 100644 index 000000000..e7a67f032 --- /dev/null +++ b/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/ConfigTest10.java @@ -0,0 +1,46 @@ +/* + * 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.myrmidon.components.AbstractComponentTest; + +/** + * A class for testing conversion. + * + * @author Adam Murdoch + */ +public class ConfigTest10 +{ + private int m_intProp; + private Integer m_integerProp; + + public void setIntProp( int intProp ) + { + m_intProp = intProp; + } + + public void setIntegerProp( Integer integerProp ) + { + m_integerProp = integerProp; + } + + public boolean equals( Object obj ) + { + ConfigTest10 test = (ConfigTest10)obj; + if( m_intProp != test.m_intProp ) + { + return false; + } + if ( !AbstractComponentTest.equals( m_integerProp, test.m_integerProp ) ) + { + return false; + } + + return true; + } +} diff --git a/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/DefaultConfigurerTest.java b/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/DefaultConfigurerTest.java index 9eaf6224e..416ada497 100644 --- a/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/DefaultConfigurerTest.java +++ b/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/DefaultConfigurerTest.java @@ -8,7 +8,7 @@ package org.apache.myrmidon.components.configurer; import java.io.File; -import junit.framework.AssertionFailedError; +import org.apache.antlib.core.StringToIntegerConverter; import org.apache.avalon.excalibur.i18n.ResourceManager; import org.apache.avalon.excalibur.i18n.Resources; import org.apache.avalon.framework.configuration.ConfigurationException; @@ -16,7 +16,9 @@ import org.apache.avalon.framework.configuration.DefaultConfiguration; import org.apache.myrmidon.api.TaskContext; import org.apache.myrmidon.components.AbstractComponentTest; import org.apache.myrmidon.components.workspace.DefaultTaskContext; +import org.apache.myrmidon.framework.DataType; import org.apache.myrmidon.interfaces.configurer.Configurer; +import org.apache.myrmidon.interfaces.role.RoleManager; import org.apache.myrmidon.interfaces.type.DefaultTypeFactory; /** @@ -80,6 +82,35 @@ public class DefaultConfigurerTest assertEquals( expected, test ); } + /** + * Tests attribute conversion. + */ + public void testAttributeConvert() + throws Exception + { + // Setup test data + final DefaultConfiguration config = new DefaultConfiguration( "test", "test" ); + config.setAttribute( "int-prop", "90" ); + config.setAttribute( "integer-prop", "-401" ); + + // Register the converter + final Class converterClass = StringToIntegerConverter.class; + final Class sourceClass = String.class; + final Class destClass = Integer.class; + registerConverter( converterClass, sourceClass, destClass ); + + final ConfigTest10 test = new ConfigTest10(); + + // Configure the object + m_configurer.configure( test, config, m_context ); + + // Check result + final ConfigTest10 expected = new ConfigTest10(); + expected.setIntProp( 90 ); + expected.setIntegerProp( new Integer(-401) ); + assertEquals( expected, test ); + } + /** * Tests setting an unknown attribute. */ @@ -96,7 +127,7 @@ public class DefaultConfigurerTest try { m_configurer.configure( test, config, m_context ); - throw new AssertionFailedError(); + fail(); } catch( final ConfigurationException ce ) { @@ -155,7 +186,7 @@ public class DefaultConfigurerTest try { m_configurer.configure( test, config, m_context ); - throw new AssertionFailedError(); + fail(); } catch( final ConfigurationException ce ) { @@ -202,7 +233,7 @@ public class DefaultConfigurerTest try { m_configurer.configure( test, config, m_context ); - throw new AssertionFailedError(); + fail(); } catch( final ConfigurationException ce ) { @@ -280,6 +311,34 @@ public class DefaultConfigurerTest assertEquals( expected, test ); } + /** + * Tests that extra content is not allowed in a reference element. + */ + public void testReferenceElementExtra() + throws Exception + { + // Setup test data + final DefaultConfiguration config = new DefaultConfiguration( "test", "test" ); + final DefaultConfiguration elem = new DefaultConfiguration( "some-prop-ref", "test" ); + elem.setAttribute( "id", "prop-a" ); + elem.setAttribute( "extra-attr", "some value" ); + config.addChild( elem ); + + final ConfigTest1 test = new ConfigTest1(); + + try + { + // Configure the object + m_configurer.configure( test, config, m_context ); + fail(); + } + catch( ConfigurationException e ) + { + final String message = REZ.getString( "extra-config-for-ref.error" ); + assertSameMessage( message, e ); + } + } + /** * Tests whether an object with a non-iterface typed adder causes an * exception. @@ -326,8 +385,9 @@ public class DefaultConfigurerTest } catch( final ConfigurationException ce ) { - final String message = REZ.getString( "multiple-typed-adder-methods-for-element.error", - ConfigTest5.class.getName() ); + final String message = REZ.getString( "multiple-adder-methods-for-element.error", + ConfigTest5.class.getName(), + ""); assertSameMessage( message, ce ); } } @@ -349,8 +409,8 @@ public class DefaultConfigurerTest final DefaultTypeFactory factory = new DefaultTypeFactory( loader ); factory.addNameClassMapping( "my-type1", MyType1.class.getName() ); factory.addNameClassMapping( "my-type2", MyType2.class.getName() ); - getTypeManager().registerType( MyRole1.class, "my-type1", factory ); - getTypeManager().registerType( MyRole1.class, "my-type2", factory ); + getTypeManager().registerType( DataType.class, "my-type1", factory ); + getTypeManager().registerType( DataType.class, "my-type2", factory ); final ConfigTest6 test = new ConfigTest6(); @@ -363,6 +423,32 @@ public class DefaultConfigurerTest assertEquals( expected, test ); } + /** + * Tests to see if typed adder can be used via an attribute. + */ + public void testTypedAdderAttribute() + throws Exception + { + // Setup test data + final DefaultConfiguration config = new DefaultConfiguration( "test", "test" ); + config.setAttribute( "my-role1", "some value" ); + + // Set up the converter and role + RoleManager roleMgr = (RoleManager)getComponentManager().lookup( RoleManager.ROLE ); + roleMgr.addNameRoleMapping( "my-role1", MyRole1.ROLE ); + registerConverter( StringToMyRole1Converter.class, String.class, MyRole1.class ); + + final ConfigTest6 test = new ConfigTest6(); + + // Configure the object + m_configurer.configure( test, config, m_context ); + + // Check result + final ConfigTest6 expected = new ConfigTest6(); + expected.add( new MyType1() ); + assertEquals( expected, test ); + } + /** * Tests to see if typed adder works, with Configuration type. */ @@ -388,7 +474,7 @@ public class DefaultConfigurerTest } /** - * Tests to see if typed adder works, with Configuration objects. + * Tests to see if adder works, with Configuration objects. */ public void testConfigAdder() throws Exception @@ -470,7 +556,7 @@ public class DefaultConfigurerTest try { m_configurer.configure( test, config, m_context ); - throw new AssertionFailedError(); + fail(); } catch( ConfigurationException e ) { @@ -498,7 +584,7 @@ public class DefaultConfigurerTest try { m_configurer.configure( test, config, m_context ); - throw new AssertionFailedError(); + fail(); } catch( ConfigurationException e ) { @@ -510,6 +596,38 @@ public class DefaultConfigurerTest } } + /** + * Tests using a reference with a typed adder. Tests using an attribute + * and a nested element. + */ + public void testTypedAdderReference() + throws Exception + { + // Setup test data + final DefaultConfiguration config = new DefaultConfiguration( "test", "test" ); + config.setAttribute( "my-role1-ref", "id" ); + final DefaultConfiguration child = new DefaultConfiguration( "my-role1-ref", "test" ); + child.setAttribute( "id", "id2" ); + config.addChild( child ); + + // Add role mapping, and add to reference to context + final RoleManager roleMgr = (RoleManager)getComponentManager().lookup( RoleManager.ROLE ); + roleMgr.addNameRoleMapping( "my-role1", MyRole1.class.getName() ); + m_context.setProperty( "id", new MyType1() ); + m_context.setProperty( "id2", new MyType2() ); + + final ConfigTest6 test = new ConfigTest6(); + + // Configure the object + m_configurer.configure( test, config, m_context ); + + // Compare against expected value + final ConfigTest6 expected = new ConfigTest6(); + expected.add( new MyType1() ); + expected.add( new MyType2() ); + assertEquals( expected, test ); + } + /** * Tests reporting of nested errors. */ @@ -527,7 +645,7 @@ public class DefaultConfigurerTest { // Configure the object m_configurer.configure( test, config, m_context ); - throw new AssertionFailedError(); + fail(); } catch( ConfigurationException e ) { diff --git a/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/MyRole1.java b/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/MyRole1.java index 4e524204d..f62943b3c 100644 --- a/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/MyRole1.java +++ b/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/MyRole1.java @@ -7,6 +7,8 @@ */ package org.apache.myrmidon.components.configurer; +import org.apache.myrmidon.framework.DataType; + /** * A basic interface to test configurer. * @@ -14,5 +16,7 @@ package org.apache.myrmidon.components.configurer; * @version $Revision$ $Date$ */ public interface MyRole1 + extends DataType { + String ROLE = MyRole1.class.getName(); } diff --git a/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/StringToMyRole1Converter.java b/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/StringToMyRole1Converter.java new file mode 100644 index 000000000..7d2dfd641 --- /dev/null +++ b/proposal/myrmidon/src/test/org/apache/myrmidon/components/configurer/StringToMyRole1Converter.java @@ -0,0 +1,32 @@ +/* + * 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.myrmidon.converter.AbstractConverter; +import org.apache.myrmidon.converter.ConverterException; +import org.apache.avalon.framework.context.Context; + +/** + * Converts from a string to a {@link MyRole1} implementation. + * + * @author Adam Murdoch + */ +public class StringToMyRole1Converter + extends AbstractConverter +{ + public StringToMyRole1Converter() + { + super( String.class, MyRole1.class ); + } + + protected Object convert( Object original, Context context ) + throws ConverterException + { + return new MyType1(); + } +} diff --git a/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/AbstractComponentTest.java b/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/AbstractComponentTest.java index baf96177d..378c19dde 100644 --- a/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/AbstractComponentTest.java +++ b/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/AbstractComponentTest.java @@ -38,6 +38,9 @@ import org.apache.myrmidon.interfaces.deployer.Deployer; import org.apache.myrmidon.interfaces.extensions.ExtensionManager; import org.apache.myrmidon.interfaces.role.RoleManager; import org.apache.myrmidon.interfaces.type.TypeManager; +import org.apache.myrmidon.interfaces.type.TypeException; +import org.apache.myrmidon.interfaces.type.DefaultTypeFactory; +import org.apache.myrmidon.converter.Converter; /** * A base class for tests for the default components. @@ -145,6 +148,21 @@ public abstract class AbstractComponentTest } } + /** + * Utility method to register a Converter. + */ + protected void registerConverter( final Class converterClass, + final Class sourceClass, + final Class destClass ) + throws ComponentException, TypeException + { + ConverterRegistry converterRegistry = (ConverterRegistry)getComponentManager().lookup( ConverterRegistry.ROLE ); + converterRegistry.registerConverter( converterClass.getName(), sourceClass.getName(), destClass.getName() ); + DefaultTypeFactory factory = new DefaultTypeFactory( getClass().getClassLoader() ); + factory.addNameClassMapping( converterClass.getName(), converterClass.getName() ); + getTypeManager().registerType( Converter.class, converterClass.getName(), factory ); + } + /** * Asserts that an exception contains the expected message. * diff --git a/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/configurer/ConfigTest10.java b/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/configurer/ConfigTest10.java new file mode 100644 index 000000000..e7a67f032 --- /dev/null +++ b/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/configurer/ConfigTest10.java @@ -0,0 +1,46 @@ +/* + * 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.myrmidon.components.AbstractComponentTest; + +/** + * A class for testing conversion. + * + * @author Adam Murdoch + */ +public class ConfigTest10 +{ + private int m_intProp; + private Integer m_integerProp; + + public void setIntProp( int intProp ) + { + m_intProp = intProp; + } + + public void setIntegerProp( Integer integerProp ) + { + m_integerProp = integerProp; + } + + public boolean equals( Object obj ) + { + ConfigTest10 test = (ConfigTest10)obj; + if( m_intProp != test.m_intProp ) + { + return false; + } + if ( !AbstractComponentTest.equals( m_integerProp, test.m_integerProp ) ) + { + return false; + } + + return true; + } +} diff --git a/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/configurer/DefaultConfigurerTest.java b/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/configurer/DefaultConfigurerTest.java index 9eaf6224e..416ada497 100644 --- a/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/configurer/DefaultConfigurerTest.java +++ b/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/configurer/DefaultConfigurerTest.java @@ -8,7 +8,7 @@ package org.apache.myrmidon.components.configurer; import java.io.File; -import junit.framework.AssertionFailedError; +import org.apache.antlib.core.StringToIntegerConverter; import org.apache.avalon.excalibur.i18n.ResourceManager; import org.apache.avalon.excalibur.i18n.Resources; import org.apache.avalon.framework.configuration.ConfigurationException; @@ -16,7 +16,9 @@ import org.apache.avalon.framework.configuration.DefaultConfiguration; import org.apache.myrmidon.api.TaskContext; import org.apache.myrmidon.components.AbstractComponentTest; import org.apache.myrmidon.components.workspace.DefaultTaskContext; +import org.apache.myrmidon.framework.DataType; import org.apache.myrmidon.interfaces.configurer.Configurer; +import org.apache.myrmidon.interfaces.role.RoleManager; import org.apache.myrmidon.interfaces.type.DefaultTypeFactory; /** @@ -80,6 +82,35 @@ public class DefaultConfigurerTest assertEquals( expected, test ); } + /** + * Tests attribute conversion. + */ + public void testAttributeConvert() + throws Exception + { + // Setup test data + final DefaultConfiguration config = new DefaultConfiguration( "test", "test" ); + config.setAttribute( "int-prop", "90" ); + config.setAttribute( "integer-prop", "-401" ); + + // Register the converter + final Class converterClass = StringToIntegerConverter.class; + final Class sourceClass = String.class; + final Class destClass = Integer.class; + registerConverter( converterClass, sourceClass, destClass ); + + final ConfigTest10 test = new ConfigTest10(); + + // Configure the object + m_configurer.configure( test, config, m_context ); + + // Check result + final ConfigTest10 expected = new ConfigTest10(); + expected.setIntProp( 90 ); + expected.setIntegerProp( new Integer(-401) ); + assertEquals( expected, test ); + } + /** * Tests setting an unknown attribute. */ @@ -96,7 +127,7 @@ public class DefaultConfigurerTest try { m_configurer.configure( test, config, m_context ); - throw new AssertionFailedError(); + fail(); } catch( final ConfigurationException ce ) { @@ -155,7 +186,7 @@ public class DefaultConfigurerTest try { m_configurer.configure( test, config, m_context ); - throw new AssertionFailedError(); + fail(); } catch( final ConfigurationException ce ) { @@ -202,7 +233,7 @@ public class DefaultConfigurerTest try { m_configurer.configure( test, config, m_context ); - throw new AssertionFailedError(); + fail(); } catch( final ConfigurationException ce ) { @@ -280,6 +311,34 @@ public class DefaultConfigurerTest assertEquals( expected, test ); } + /** + * Tests that extra content is not allowed in a reference element. + */ + public void testReferenceElementExtra() + throws Exception + { + // Setup test data + final DefaultConfiguration config = new DefaultConfiguration( "test", "test" ); + final DefaultConfiguration elem = new DefaultConfiguration( "some-prop-ref", "test" ); + elem.setAttribute( "id", "prop-a" ); + elem.setAttribute( "extra-attr", "some value" ); + config.addChild( elem ); + + final ConfigTest1 test = new ConfigTest1(); + + try + { + // Configure the object + m_configurer.configure( test, config, m_context ); + fail(); + } + catch( ConfigurationException e ) + { + final String message = REZ.getString( "extra-config-for-ref.error" ); + assertSameMessage( message, e ); + } + } + /** * Tests whether an object with a non-iterface typed adder causes an * exception. @@ -326,8 +385,9 @@ public class DefaultConfigurerTest } catch( final ConfigurationException ce ) { - final String message = REZ.getString( "multiple-typed-adder-methods-for-element.error", - ConfigTest5.class.getName() ); + final String message = REZ.getString( "multiple-adder-methods-for-element.error", + ConfigTest5.class.getName(), + ""); assertSameMessage( message, ce ); } } @@ -349,8 +409,8 @@ public class DefaultConfigurerTest final DefaultTypeFactory factory = new DefaultTypeFactory( loader ); factory.addNameClassMapping( "my-type1", MyType1.class.getName() ); factory.addNameClassMapping( "my-type2", MyType2.class.getName() ); - getTypeManager().registerType( MyRole1.class, "my-type1", factory ); - getTypeManager().registerType( MyRole1.class, "my-type2", factory ); + getTypeManager().registerType( DataType.class, "my-type1", factory ); + getTypeManager().registerType( DataType.class, "my-type2", factory ); final ConfigTest6 test = new ConfigTest6(); @@ -363,6 +423,32 @@ public class DefaultConfigurerTest assertEquals( expected, test ); } + /** + * Tests to see if typed adder can be used via an attribute. + */ + public void testTypedAdderAttribute() + throws Exception + { + // Setup test data + final DefaultConfiguration config = new DefaultConfiguration( "test", "test" ); + config.setAttribute( "my-role1", "some value" ); + + // Set up the converter and role + RoleManager roleMgr = (RoleManager)getComponentManager().lookup( RoleManager.ROLE ); + roleMgr.addNameRoleMapping( "my-role1", MyRole1.ROLE ); + registerConverter( StringToMyRole1Converter.class, String.class, MyRole1.class ); + + final ConfigTest6 test = new ConfigTest6(); + + // Configure the object + m_configurer.configure( test, config, m_context ); + + // Check result + final ConfigTest6 expected = new ConfigTest6(); + expected.add( new MyType1() ); + assertEquals( expected, test ); + } + /** * Tests to see if typed adder works, with Configuration type. */ @@ -388,7 +474,7 @@ public class DefaultConfigurerTest } /** - * Tests to see if typed adder works, with Configuration objects. + * Tests to see if adder works, with Configuration objects. */ public void testConfigAdder() throws Exception @@ -470,7 +556,7 @@ public class DefaultConfigurerTest try { m_configurer.configure( test, config, m_context ); - throw new AssertionFailedError(); + fail(); } catch( ConfigurationException e ) { @@ -498,7 +584,7 @@ public class DefaultConfigurerTest try { m_configurer.configure( test, config, m_context ); - throw new AssertionFailedError(); + fail(); } catch( ConfigurationException e ) { @@ -510,6 +596,38 @@ public class DefaultConfigurerTest } } + /** + * Tests using a reference with a typed adder. Tests using an attribute + * and a nested element. + */ + public void testTypedAdderReference() + throws Exception + { + // Setup test data + final DefaultConfiguration config = new DefaultConfiguration( "test", "test" ); + config.setAttribute( "my-role1-ref", "id" ); + final DefaultConfiguration child = new DefaultConfiguration( "my-role1-ref", "test" ); + child.setAttribute( "id", "id2" ); + config.addChild( child ); + + // Add role mapping, and add to reference to context + final RoleManager roleMgr = (RoleManager)getComponentManager().lookup( RoleManager.ROLE ); + roleMgr.addNameRoleMapping( "my-role1", MyRole1.class.getName() ); + m_context.setProperty( "id", new MyType1() ); + m_context.setProperty( "id2", new MyType2() ); + + final ConfigTest6 test = new ConfigTest6(); + + // Configure the object + m_configurer.configure( test, config, m_context ); + + // Compare against expected value + final ConfigTest6 expected = new ConfigTest6(); + expected.add( new MyType1() ); + expected.add( new MyType2() ); + assertEquals( expected, test ); + } + /** * Tests reporting of nested errors. */ @@ -527,7 +645,7 @@ public class DefaultConfigurerTest { // Configure the object m_configurer.configure( test, config, m_context ); - throw new AssertionFailedError(); + fail(); } catch( ConfigurationException e ) { diff --git a/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/configurer/MyRole1.java b/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/configurer/MyRole1.java index 4e524204d..f62943b3c 100644 --- a/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/configurer/MyRole1.java +++ b/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/configurer/MyRole1.java @@ -7,6 +7,8 @@ */ package org.apache.myrmidon.components.configurer; +import org.apache.myrmidon.framework.DataType; + /** * A basic interface to test configurer. * @@ -14,5 +16,7 @@ package org.apache.myrmidon.components.configurer; * @version $Revision$ $Date$ */ public interface MyRole1 + extends DataType { + String ROLE = MyRole1.class.getName(); } diff --git a/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/configurer/StringToMyRole1Converter.java b/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/configurer/StringToMyRole1Converter.java new file mode 100644 index 000000000..7d2dfd641 --- /dev/null +++ b/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/configurer/StringToMyRole1Converter.java @@ -0,0 +1,32 @@ +/* + * 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.myrmidon.converter.AbstractConverter; +import org.apache.myrmidon.converter.ConverterException; +import org.apache.avalon.framework.context.Context; + +/** + * Converts from a string to a {@link MyRole1} implementation. + * + * @author Adam Murdoch + */ +public class StringToMyRole1Converter + extends AbstractConverter +{ + public StringToMyRole1Converter() + { + super( String.class, MyRole1.class ); + } + + protected Object convert( Object original, Context context ) + throws ConverterException + { + return new MyType1(); + } +}