diff --git a/proposal/myrmidon/src/java/org/apache/antlib/file/Delete.java b/proposal/myrmidon/src/java/org/apache/antlib/file/Delete.java index 25187ec10..23baba5b4 100644 --- a/proposal/myrmidon/src/java/org/apache/antlib/file/Delete.java +++ b/proposal/myrmidon/src/java/org/apache/antlib/file/Delete.java @@ -237,4 +237,4 @@ public class Delete } } } -} \ No newline at end of file +} diff --git a/proposal/myrmidon/src/java/org/apache/aut/bzip2/CBZip2InputStream.java b/proposal/myrmidon/src/java/org/apache/aut/bzip2/CBZip2InputStream.java index f9582c492..4836549f7 100644 --- a/proposal/myrmidon/src/java/org/apache/aut/bzip2/CBZip2InputStream.java +++ b/proposal/myrmidon/src/java/org/apache/aut/bzip2/CBZip2InputStream.java @@ -958,4 +958,4 @@ public class CBZip2InputStream } } } -} \ No newline at end of file +} diff --git a/proposal/myrmidon/src/java/org/apache/aut/tar/TarOutputStream.java b/proposal/myrmidon/src/java/org/apache/aut/tar/TarOutputStream.java index 2b78ae49d..a338919bc 100644 --- a/proposal/myrmidon/src/java/org/apache/aut/tar/TarOutputStream.java +++ b/proposal/myrmidon/src/java/org/apache/aut/tar/TarOutputStream.java @@ -338,4 +338,4 @@ public class TarOutputStream m_buffer.writeRecord( m_recordBuf ); } -} \ No newline at end of file +} diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/embeddor/DefaultEmbeddor.java b/proposal/myrmidon/src/java/org/apache/myrmidon/components/embeddor/DefaultEmbeddor.java index 1882401c2..476969f8b 100644 --- a/proposal/myrmidon/src/java/org/apache/myrmidon/components/embeddor/DefaultEmbeddor.java +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/embeddor/DefaultEmbeddor.java @@ -41,6 +41,7 @@ import org.apache.myrmidon.interfaces.role.RoleManager; import org.apache.myrmidon.interfaces.service.MultiSourceServiceManager; import org.apache.myrmidon.interfaces.type.TypeFactory; import org.apache.myrmidon.interfaces.type.TypeManager; +import org.apache.myrmidon.interfaces.workspace.PropertyResolver; import org.apache.myrmidon.interfaces.workspace.Workspace; import org.apache.myrmidon.listeners.ProjectListener; @@ -254,6 +255,7 @@ public class DefaultEmbeddor createComponent( Deployer.class, PREFIX + "deployer.DefaultDeployer" ); createComponent( ClassLoaderManager.class, PREFIX + "deployer.DefaultClassLoaderManager" ); createComponent( Executor.class, PREFIX + "executor.AspectAwareExecutor" ); + createComponent( PropertyResolver.class, PREFIX + "workspace.DefaultPropertyResolver" ); // Setup the components for( Iterator iterator = m_components.iterator(); iterator.hasNext(); ) diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/ClassicPropertyResolver.java b/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/ClassicPropertyResolver.java new file mode 100644 index 000000000..2b9b384c3 --- /dev/null +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/ClassicPropertyResolver.java @@ -0,0 +1,44 @@ +/* + * 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.workspace; + +import org.apache.myrmidon.interfaces.workspace.PropertyResolver; +import org.apache.avalon.framework.context.Context; +import org.apache.avalon.framework.context.ContextException; + +/** + * A {@link PropertyResolver} implementation which resolves properties + * as per Ant1, ignoring undefined properties. + * + * @author Darrell DeBoer + * @version $Revision$ $Date$ + */ +public class ClassicPropertyResolver + extends DefaultPropertyResolver + implements PropertyResolver +{ + /** + * Retrieve a value from the specified context using the specified key. + * If there is no such value, returns the original property reference. + * + * @param propertyName the name of the property to retrieve + * @param context the set of known properties + */ + protected Object getPropertyValue( final String propertyName, + final Context context ) + { + try + { + return context.get( propertyName ); + } + catch( ContextException e ) + { + return "${" + propertyName + "}"; + } + } +} diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultPropertyResolver.java b/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultPropertyResolver.java new file mode 100644 index 000000000..d75e3e805 --- /dev/null +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultPropertyResolver.java @@ -0,0 +1,255 @@ +/* + * 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.workspace; + +import org.apache.avalon.excalibur.i18n.ResourceManager; +import org.apache.avalon.excalibur.i18n.Resources; +import org.apache.avalon.framework.context.Context; +import org.apache.avalon.framework.context.ContextException; +import org.apache.myrmidon.api.TaskException; +import org.apache.myrmidon.interfaces.workspace.PropertyResolver; + +/** + * Base class for PropertyResolver implementations. + * + * @author Peter Donald + * @author Darrell DeBoer + * @version $Revision$ $Date$ + */ +public class DefaultPropertyResolver + implements PropertyResolver +{ + private final static Resources REZ = + ResourceManager.getPackageResources( DefaultPropertyResolver.class ); + + /** + * Resolve a string property. This evaluates all property + * substitutions based on specified context. + * + * If the content contains a single property reference, then the property value + * Object itself is returned. + * Otherwise, a String is returned, comprising the supplied + * content, with all property references replaced with the result of + * toString() called on the property value. + * + * @param content the property to resolve + * @param context the context in which to resolve property + * @return the reolved property + * @exception TaskException if an error occurs + */ + public Object resolveProperties( final String content, + final Context context ) + throws TaskException + { + int start = findNextProperty( content, 0 ); + if( -1 == start ) + { + return content; + } + + int end = findEnding( content, start ); + + final int length = content.length(); + + if( 0 == start && end == ( length - 1 ) ) + { + return getPropertyValue( content.substring( start + 2, end ), + context ); + } + + final StringBuffer sb = new StringBuffer( length * 2 ); + int lastPlace = 0; + + while( true ) + { + final Object propertyValue = + getPropertyValue( content.substring( start + 2, end ), + context ); + + sb.append( content.substring( lastPlace, start ) ); + sb.append( propertyValue ); + + lastPlace = end + 1; + + start = findNextProperty( content, lastPlace ); + if( -1 == start ) + { + break; + } + + end = findEnding( content, start ); + } + + sb.append( content.substring( lastPlace, length ) ); + + return sb.toString(); + } + + /** + * Resolve a string property. This recursively evaluates all property + * substitutions based on specified context. + * + * @param content the property to resolve + * @param context the context in which to resolve property + * @return the reolved property + * @exception TaskException if an error occurs + */ + protected Object recursiveResolveProperty( final String content, + final Context context ) + throws TaskException + { + int start = findNextProperty( content, 0 ); + if( -1 == start ) + { + return content; + } + + int end = findNestedEnding( content, start ); + + final int length = content.length(); + + if( 0 == start && end == ( length - 1 ) ) + { + final String propertyName = content.substring( start + 2, end ); + final Object key = recursiveResolveProperty( propertyName, context ); + return getPropertyValue( key.toString(), context ); + } + + final StringBuffer sb = new StringBuffer( length * 2 ); + + int lastPlace = 0; + + while( true ) + { + final String propertyName = content.substring( start + 2, end ); + final Object key = recursiveResolveProperty( propertyName, context ); + final Object value = getPropertyValue( key.toString(), context ); + + sb.append( content.substring( lastPlace, start ) ); + sb.append( value ); + + lastPlace = end + 1; + + start = findNextProperty( content, lastPlace ); + if( -1 == start ) + { + break; + } + + end = findNestedEnding( content, start ); + } + + sb.append( content.substring( lastPlace, length ) ); + + return sb.toString(); + } + + /** + * Finds the next occurrance of the start of a Property identifier. + * @param content the String to search + * @param currentPosition start location of the search + * @return the position of the next occurrence, or -1 if none + * was found. + */ + private int findNextProperty( final String content, final int currentPosition ) + { + //TODO: Check if it is commented out + return content.indexOf( "${", currentPosition ); + } + + /** + * Finds the next occurrence of the end of a Property identifier. + * @param property the String to search + * @param currentPosition start location of the search + * @return the position of the next occurrence + * @throws TaskException if no end was found + */ + private int findEnding( final String property, final int currentPosition ) + throws TaskException + { + //TODO: Check if it is commented out + final int index = property.indexOf( '}', currentPosition ); + if( -1 == index ) + { + final String message = REZ.getString( "prop.mismatched-braces.error" ); + throw new TaskException( message ); + } + + return index; + } + + /** + * Finds the end of the property identifier at the currentPosition, + * taking into account nested property identifiers. + * @param property the String to search + * @param currentPosition location of the property + * @return the position of the propery ending. + * @throws TaskException if the property is not properly ended. + */ + private int findNestedEnding( final String property, final int currentPosition ) + throws TaskException + { + final int length = property.length(); + final int start = currentPosition + 2; + + int weight = 1; + for( int i = start; ( weight > 0 ) && ( i < length ); i++ ) + { + final char ch = property.charAt( i ); + switch( ch ) + { + case '}': + //TODO: Check if it is commented out + weight--; + if( weight == 0 ) + { + return i; + } + break; + + case '$': + { + //TODO: Check if it is commented out + final int next = i + 1; + if( next < length && '{' == property.charAt( next ) ) + { + weight++; + } + } + break; + } + } + + final String message = REZ.getString( "prop.mismatched-braces.error" ); + throw new TaskException( message ); + } + + /** + * Retrieve a value from the specified context using the specified key. + * + * @param propertyName the key of value in context + * @param context the set of known properties + * @return the object retrieved from context + * @exception TaskException if the property is undefined + */ + protected Object getPropertyValue( final String propertyName, + final Context context ) + throws TaskException + { + try + { + return context.get( propertyName ); + } + catch( ContextException e ) + { + final String message = REZ.getString( "prop.missing-value.error", propertyName ); + throw new TaskException( message ); + } + } +} + diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultTaskContext.java b/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultTaskContext.java index d1d033755..5224a34aa 100644 --- a/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultTaskContext.java +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/DefaultTaskContext.java @@ -13,11 +13,14 @@ import java.util.Map; import org.apache.avalon.excalibur.i18n.ResourceManager; import org.apache.avalon.excalibur.i18n.Resources; import org.apache.avalon.excalibur.io.FileUtil; +import org.apache.avalon.framework.context.Context; +import org.apache.avalon.framework.context.ContextException; import org.apache.avalon.framework.logger.Logger; import org.apache.avalon.framework.service.ServiceException; import org.apache.avalon.framework.service.ServiceManager; import org.apache.myrmidon.api.TaskContext; import org.apache.myrmidon.api.TaskException; +import org.apache.myrmidon.interfaces.workspace.PropertyResolver; /** * Default implementation of TaskContext. @@ -26,7 +29,7 @@ import org.apache.myrmidon.api.TaskException; * @version $Revision$ $Date$ */ public class DefaultTaskContext - implements TaskContext + implements TaskContext, Context { private final static Resources REZ = ResourceManager.getPackageResources( DefaultTaskContext.class ); @@ -35,6 +38,7 @@ public class DefaultTaskContext private final TaskContext m_parent; private ServiceManager m_serviceManager; private Logger m_logger; + private PropertyResolver m_propertyResolver; /** * Constructor that takes both parent context and a service directory. @@ -55,7 +59,7 @@ public class DefaultTaskContext */ public String getName() { - return (String)m_contextData.get( NAME ); + return (String)getProperty( NAME ); } /** @@ -65,7 +69,7 @@ public class DefaultTaskContext */ public File getBaseDirectory() { - return (File)m_contextData.get( BASE_DIRECTORY ); + return (File)getProperty( BASE_DIRECTORY ); } /** @@ -133,10 +137,17 @@ public class DefaultTaskContext public Object resolveValue( final String value ) throws TaskException { + try { + // Lazy lookup of the PropertyResolver + if( m_propertyResolver == null ) + { + m_propertyResolver = (PropertyResolver)getService( PropertyResolver.class ); + } + final Object object = - PropertyUtil.resolveProperty( value, this, false ); + m_propertyResolver.resolveProperties( value, this ); if( null == object ) { @@ -161,7 +172,12 @@ public class DefaultTaskContext */ public Object getProperty( final String name ) { - return m_contextData.get( name ); + Object value = m_contextData.get( name ); + if( value == null && m_parent != null ) + { + value = m_parent.getProperty( name ); + } + return value; } /** @@ -332,6 +348,20 @@ public class DefaultTaskContext return context; } + /** + * Returns a property. + */ + public Object get( final Object key ) throws ContextException + { + final Object value = getProperty( (String)key ); + if( value == null ) + { + final String message = REZ.getString( "unknown-property.error", key ); + throw new ContextException( message ); + } + return value; + } + /** * Make sure property is valid if it is one of the "magic" properties. * diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/PropertyUtil.java b/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/PropertyUtil.java deleted file mode 100644 index 6b6b25322..000000000 --- a/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/PropertyUtil.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) The Apache Software Foundation. All rights reserved. - * - * This software is published under the terms of the Apache Software License - * version 1.1, a copy of which has been included with this distribution in - * the LICENSE.txt file. - */ -package org.apache.myrmidon.components.workspace; - -import org.apache.avalon.excalibur.i18n.ResourceManager; -import org.apache.avalon.excalibur.i18n.Resources; -import org.apache.myrmidon.api.TaskContext; -import org.apache.myrmidon.api.TaskException; - -/** - * Utility class to evaluate properties. - * - * @author Peter Donald - * @version $Revision$ $Date$ - */ -public final class PropertyUtil -{ - private final static Resources REZ = - ResourceManager.getPackageResources( PropertyUtil.class ); - - private PropertyUtil() - { - } - - /** - * Resolve a string property. This evaluates all property - * substitutions based on specified context. - * - * @param property the property to resolve - * @param context the context in which to resolve property - * @param ignoreUndefined if false will throw an TaskException if property is not found - * @return the reolved property - * @exception TaskException if an error occurs - */ - public static Object resolveProperty( final String property, - final TaskContext context, - final boolean ignoreUndefined ) - throws TaskException - { - int start = findBeginning( property, 0 ); - if( -1 == start ) - { - return property; - } - - int end = findEnding( property, start ); - - final int length = property.length(); - - if( 0 == start && end == ( length - 1 ) ) - { - return resolveValue( property.substring( start + 2, end ), - context, - ignoreUndefined ); - } - - final StringBuffer sb = new StringBuffer( length * 2 ); - int lastPlace = 0; - - while( true ) - { - final Object value = - resolveValue( property.substring( start + 2, end ), - context, - ignoreUndefined ); - - sb.append( property.substring( lastPlace, start ) ); - sb.append( value ); - - lastPlace = end + 1; - - start = findBeginning( property, lastPlace ); - if( -1 == start ) - { - break; - } - - end = findEnding( property, start ); - } - - sb.append( property.substring( lastPlace, length ) ); - - return sb.toString(); - } - - /** - * Resolve a string property. This recursively evaluates all property - * substitutions based on specified context. - * - * @param property the property to resolve - * @param context the context in which to resolve property - * @param ignoreUndefined if false will throw an TaskException if property is not found - * @return the reolved property - * @exception TaskException if an error occurs - */ - public static Object recursiveResolveProperty( final String property, - final TaskContext context, - final boolean ignoreUndefined ) - throws TaskException - { - int start = findBeginning( property, 0 ); - if( -1 == start ) - { - return property; - } - - int end = findNestedEnding( property, start ); - - final int length = property.length(); - - if( 0 == start && end == ( length - 1 ) ) - { - final String propertyName = property.substring( start + 2, end ); - final Object key = recursiveResolveProperty( propertyName, context, ignoreUndefined ); - return resolveValue( key.toString(), context, ignoreUndefined ); - } - - final StringBuffer sb = new StringBuffer( length * 2 ); - - int lastPlace = 0; - - while( true ) - { - final String propertyName = property.substring( start + 2, end ); - final Object key = recursiveResolveProperty( propertyName, context, ignoreUndefined ); - final Object value = resolveValue( key.toString(), context, ignoreUndefined ); - - sb.append( property.substring( lastPlace, start ) ); - sb.append( value ); - - lastPlace = end + 1; - - start = findBeginning( property, lastPlace ); - if( -1 == start ) - { - break; - } - - end = findNestedEnding( property, start ); - } - - sb.append( property.substring( lastPlace, length ) ); - - return sb.toString(); - } - - private static int findBeginning( final String property, final int currentPosition ) - { - //TODO: Check if it is commented out - return property.indexOf( "${", currentPosition ); - } - - private static int findEnding( final String property, final int currentPosition ) - throws TaskException - { - //TODO: Check if it is commented out - final int index = property.indexOf( '}', currentPosition ); - if( -1 == index ) - { - final String message = REZ.getString( "prop.mismatched-braces.error" ); - throw new TaskException( message ); - } - - return index; - } - - private static int findNestedEnding( final String property, final int currentPosition ) - throws TaskException - { - final int length = property.length(); - final int start = currentPosition + 2; - - int weight = 1; - for( int i = start; ( weight > 0 ) && ( i < length ); i++ ) - { - final char ch = property.charAt( i ); - switch( ch ) - { - case '}': - //TODO: Check if it is commented out - weight--; - if( weight == 0 ) - { - return i; - } - break; - - case '$': - { - //TODO: Check if it is commented out - final int next = i + 1; - if( next < length && '{' == property.charAt( next ) ) - { - weight++; - } - } - break; - } - } - - final String message = REZ.getString( "prop.mismatched-braces.error" ); - throw new TaskException( message ); - } - - /** - * Retrieve a value from the specified context using the specified key. - * If there is no such value and ignoreUndefined is not false then a - * TaskException is generated. - * - * @param key the key of value in context - * @param context the Context - * @param ignoreUndefined true if undefined variables are ignored - * @return the object retrieved from context - * @exception TaskException if an error occurs - */ - private static Object resolveValue( final String key, - final TaskContext context, - final boolean ignoreUndefined ) - throws TaskException - { - final Object value = context.getProperty( key ); - if( value != null ) - { - return value; - } - if( ignoreUndefined ) - { - return ""; - } - final String message = REZ.getString( "prop.missing-value.error", key ); - throw new TaskException( message ); - } -} - diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/Resources.properties b/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/Resources.properties index 582395c6b..c2db45564 100644 --- a/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/Resources.properties +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/workspace/Resources.properties @@ -11,17 +11,13 @@ exec-target.notice=Executing target {0}. exec-task.notice=Executing task {0}. #DefaultTaskContext -no-version.error=No JavaVersion in Context. -no-name.error=No Name in Context. -no-dir.error=No Base Directory in Context. -no-parent.error=Can't set a property with parent scope when context has no parent. -bad-scope.error=Unknown property scope! ({0}). +unknown-prop.error=Unknown property {0}. bad-property.error=Property {0} must have a value of type {1}. null-resolved-value.error=Value "{0}" resolved to null. bad-resolve.error=Unable to resolve value "{0}". bad-find-service.error=Could not find service "{0}". bad-service-class.error=Find service "{0}" but it was of type {1} where it was expected to be of type {2}. -#PropertyUtil +#AbstractPropertyResolver prop.mismatched-braces.error=Malformed property with mismatched }'s. prop.missing-value.error=Unable to find "{0}" to expand during property resolution. diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/workspace/PropertyResolver.java b/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/workspace/PropertyResolver.java new file mode 100644 index 000000000..4da9ad1fe --- /dev/null +++ b/proposal/myrmidon/src/java/org/apache/myrmidon/interfaces/workspace/PropertyResolver.java @@ -0,0 +1,38 @@ +/* + * 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.interfaces.workspace; + +import org.apache.myrmidon.api.TaskException; +import org.apache.avalon.framework.context.Context; + +/** + * + * Provides a service for the resolution of property identifiers within + * String content. + * + * @author Darrell DeBoer + * @version $Revision$ $Date$ + */ +public interface PropertyResolver +{ + String ROLE = PropertyResolver.class.getName(); + + /** + * Resolve a string property. This evaluates all property + * substitutions based on specified contex. + * Rules used for property resolution are implementation dependent. + * + * @param value the value to resolve, which may contain property identifiers + * @param context the set of properties to resolve against. + * @return the resolved content + * @exception TaskException if an error occurs + */ + Object resolveProperties( final String value, + final Context context ) + throws TaskException; +} 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 c6a3052fb..3a49f0fb6 100644 --- a/proposal/myrmidon/src/test/org/apache/myrmidon/components/AbstractComponentTest.java +++ b/proposal/myrmidon/src/test/org/apache/myrmidon/components/AbstractComponentTest.java @@ -27,6 +27,7 @@ import org.apache.myrmidon.components.deployer.DefaultDeployer; import org.apache.myrmidon.components.extensions.DefaultExtensionManager; import org.apache.myrmidon.components.role.DefaultRoleManager; import org.apache.myrmidon.components.type.DefaultTypeManager; +import org.apache.myrmidon.components.workspace.DefaultPropertyResolver; import org.apache.myrmidon.interfaces.configurer.Configurer; import org.apache.myrmidon.interfaces.converter.ConverterRegistry; import org.apache.myrmidon.interfaces.deployer.Deployer; @@ -36,6 +37,7 @@ import org.apache.myrmidon.interfaces.role.RoleManager; import org.apache.myrmidon.interfaces.type.DefaultTypeFactory; import org.apache.myrmidon.interfaces.type.TypeException; import org.apache.myrmidon.interfaces.type.TypeManager; +import org.apache.myrmidon.interfaces.workspace.PropertyResolver; /** * A base class for tests for the default components. @@ -120,6 +122,10 @@ public abstract class AbstractComponentTest m_serviceManager.put( RoleManager.ROLE, component ); components.add( component ); + component = new DefaultPropertyResolver(); + m_serviceManager.put( PropertyResolver.ROLE, component ); + components.add( component ); + // Log enable the components for( Iterator iterator = components.iterator(); iterator.hasNext(); ) { diff --git a/proposal/myrmidon/src/test/org/apache/myrmidon/components/workspace/ClassicPropertyResolverTest.java b/proposal/myrmidon/src/test/org/apache/myrmidon/components/workspace/ClassicPropertyResolverTest.java new file mode 100644 index 000000000..514b4e11e --- /dev/null +++ b/proposal/myrmidon/src/test/org/apache/myrmidon/components/workspace/ClassicPropertyResolverTest.java @@ -0,0 +1,41 @@ +/* + * 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.workspace; + +import org.apache.myrmidon.interfaces.workspace.PropertyResolver; + +/** + * A test for {@link ClassicPropertyResolver} + * + * @author Darrell DeBoer + * @version $Revision$ $Date$ + */ +public class ClassicPropertyResolverTest + extends DefaultPropertyResolverTest +{ + public ClassicPropertyResolverTest( String name ) + { + super( name ); + } + + protected PropertyResolver createResolver() + { + return new ClassicPropertyResolver(); + } + + /** + * Tests handing undefined property. + */ + public void testUndefinedProp() throws Exception + { + String undefinedProp = "undefinedProperty"; + + final String propRef = "${" + undefinedProp + "}"; + doTestResolution( propRef, propRef, m_context ); + } +} diff --git a/proposal/myrmidon/src/test/org/apache/myrmidon/components/workspace/DefaultPropertyResolverTest.java b/proposal/myrmidon/src/test/org/apache/myrmidon/components/workspace/DefaultPropertyResolverTest.java new file mode 100644 index 000000000..1657b5339 --- /dev/null +++ b/proposal/myrmidon/src/test/org/apache/myrmidon/components/workspace/DefaultPropertyResolverTest.java @@ -0,0 +1,167 @@ +/* + * 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.workspace; + +import java.io.File; +import java.util.Date; +import org.apache.avalon.excalibur.i18n.ResourceManager; +import org.apache.avalon.excalibur.i18n.Resources; +import org.apache.avalon.framework.context.Context; +import org.apache.myrmidon.api.TaskException; +import org.apache.myrmidon.components.AbstractComponentTest; +import org.apache.myrmidon.interfaces.workspace.PropertyResolver; + +/** + * Functional tests for {@link DefaultPropertyResolver}. + * + * @author Darrell DeBoer + * @version $Revision$ $Date$ + */ +public class DefaultPropertyResolverTest + extends AbstractComponentTest +{ + protected final static Resources REZ + = ResourceManager.getPackageResources( DefaultPropertyResolver.class ); + + protected PropertyResolver m_resolver; + protected DefaultTaskContext m_context; + + public DefaultPropertyResolverTest( String name ) + { + super( name ); + } + + protected void setUp() throws Exception + { + super.setUp(); + + m_resolver = createResolver(); + + m_context = new DefaultTaskContext( null, getServiceManager(), getLogger() ); + m_context.setProperty( "intProp", new Integer( 333 ) ); + m_context.setProperty( "stringProp", "String property" ); + } + + protected PropertyResolver createResolver() + { + return new DefaultPropertyResolver(); + } + + /** + * Test property resolution with various different typed properties. + */ + public void testPropertyTypes() throws Exception + { + testPropertyValue( new String( "String value" ) ); + testPropertyValue( new Date() ); + testPropertyValue( new Integer( Integer.MIN_VALUE ) ); + testPropertyValue( new Double( 24234.98453 ) ); + testPropertyValue( this.getClass() ); + testPropertyValue( File.createTempFile( "PropertyResolverTest", null ) ); + } + + /** + * Simple tests with property on it's own, and accompanied by text. + */ + private void testPropertyValue( Object propObject ) + throws Exception + { + m_context.setProperty( "typedProp", propObject ); + String propString = propObject.toString(); + + doTestResolution( "${typedProp}", propObject, m_context ); + doTestResolution( "${typedProp} with following text", + propString + " with following text", m_context ); + doTestResolution( "Preceding text with ${typedProp}", + "Preceding text with " + propString, m_context ); + } + + /** + * Tests multiple property declarations in a single value. + */ + public void testMultipleProperties() throws Exception + { + m_context.setProperty( "prop1", "value1" ); + m_context.setProperty( "prop2", "value2" ); + m_context.setProperty( "int1", new Integer( 123 ) ); + + doTestResolution( "${prop1}${prop2}", "value1value2", m_context ); + doTestResolution( "${prop1}${prop1}${prop1}", "value1value1value1", m_context ); + doTestResolution( "before ${prop2} between ${prop1} after", + "before value2 between value1 after", m_context ); + doTestResolution( "${prop1}-${int1}-${prop2}", "value1-123-value2", m_context ); + + } + + /** + * Tests handing undefined property. + */ + public void testUndefinedProp() throws Exception + { + String undefinedProp = "undefinedProperty"; + doTestFailure( "${" + undefinedProp + "}", + REZ.getString( "prop.missing-value.error", undefinedProp ), + m_context ); + + //TODO - "" should be disallowed as a property name + doTestFailure( "${}", + REZ.getString( "prop.missing-value.error", "" ), + m_context ); + } + + /** + * Tests illegal property syntax. + */ + public void testInvalidTypeDeclarations() throws Exception + { + + doTestFailure( "${unclosed", + REZ.getString( "prop.mismatched-braces.error" ), + m_context ); + doTestFailure( "${", + REZ.getString( "prop.mismatched-braces.error" ), + m_context ); + + /* TODO - need to handle these cases. */ + // testFailure( "${bad${}", "", m_context ); + // testFailure( "${ }", "", m_context ); + + } + + /** + * Resolves the property using the supplied context, and checks the result. + */ + protected void doTestResolution( String value, + Object expected, + Context context ) + throws Exception + { + Object resolved = m_resolver.resolveProperties( value, context ); + + assertEquals( expected, resolved ); + } + + /** + * Attempts to resolve the value using the supplied context, expecting to + * fail with the supplied error message. + */ + protected void doTestFailure( String value, + String expectedErrorMessage, + Context context ) + { + try + { + m_resolver.resolveProperties( value, context ); + fail( "Unexpected sucess - test should have failed." ); + } + catch( TaskException e ) + { + assertSameMessage( expectedErrorMessage, e ); + } + } +} 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 c6a3052fb..3a49f0fb6 100644 --- a/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/AbstractComponentTest.java +++ b/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/AbstractComponentTest.java @@ -27,6 +27,7 @@ import org.apache.myrmidon.components.deployer.DefaultDeployer; import org.apache.myrmidon.components.extensions.DefaultExtensionManager; import org.apache.myrmidon.components.role.DefaultRoleManager; import org.apache.myrmidon.components.type.DefaultTypeManager; +import org.apache.myrmidon.components.workspace.DefaultPropertyResolver; import org.apache.myrmidon.interfaces.configurer.Configurer; import org.apache.myrmidon.interfaces.converter.ConverterRegistry; import org.apache.myrmidon.interfaces.deployer.Deployer; @@ -36,6 +37,7 @@ import org.apache.myrmidon.interfaces.role.RoleManager; import org.apache.myrmidon.interfaces.type.DefaultTypeFactory; import org.apache.myrmidon.interfaces.type.TypeException; import org.apache.myrmidon.interfaces.type.TypeManager; +import org.apache.myrmidon.interfaces.workspace.PropertyResolver; /** * A base class for tests for the default components. @@ -120,6 +122,10 @@ public abstract class AbstractComponentTest m_serviceManager.put( RoleManager.ROLE, component ); components.add( component ); + component = new DefaultPropertyResolver(); + m_serviceManager.put( PropertyResolver.ROLE, component ); + components.add( component ); + // Log enable the components for( Iterator iterator = components.iterator(); iterator.hasNext(); ) { diff --git a/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/workspace/ClassicPropertyResolverTest.java b/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/workspace/ClassicPropertyResolverTest.java new file mode 100644 index 000000000..514b4e11e --- /dev/null +++ b/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/workspace/ClassicPropertyResolverTest.java @@ -0,0 +1,41 @@ +/* + * 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.workspace; + +import org.apache.myrmidon.interfaces.workspace.PropertyResolver; + +/** + * A test for {@link ClassicPropertyResolver} + * + * @author Darrell DeBoer + * @version $Revision$ $Date$ + */ +public class ClassicPropertyResolverTest + extends DefaultPropertyResolverTest +{ + public ClassicPropertyResolverTest( String name ) + { + super( name ); + } + + protected PropertyResolver createResolver() + { + return new ClassicPropertyResolver(); + } + + /** + * Tests handing undefined property. + */ + public void testUndefinedProp() throws Exception + { + String undefinedProp = "undefinedProperty"; + + final String propRef = "${" + undefinedProp + "}"; + doTestResolution( propRef, propRef, m_context ); + } +} diff --git a/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/workspace/DefaultPropertyResolverTest.java b/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/workspace/DefaultPropertyResolverTest.java new file mode 100644 index 000000000..1657b5339 --- /dev/null +++ b/proposal/myrmidon/src/testcases/org/apache/myrmidon/components/workspace/DefaultPropertyResolverTest.java @@ -0,0 +1,167 @@ +/* + * 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.workspace; + +import java.io.File; +import java.util.Date; +import org.apache.avalon.excalibur.i18n.ResourceManager; +import org.apache.avalon.excalibur.i18n.Resources; +import org.apache.avalon.framework.context.Context; +import org.apache.myrmidon.api.TaskException; +import org.apache.myrmidon.components.AbstractComponentTest; +import org.apache.myrmidon.interfaces.workspace.PropertyResolver; + +/** + * Functional tests for {@link DefaultPropertyResolver}. + * + * @author Darrell DeBoer + * @version $Revision$ $Date$ + */ +public class DefaultPropertyResolverTest + extends AbstractComponentTest +{ + protected final static Resources REZ + = ResourceManager.getPackageResources( DefaultPropertyResolver.class ); + + protected PropertyResolver m_resolver; + protected DefaultTaskContext m_context; + + public DefaultPropertyResolverTest( String name ) + { + super( name ); + } + + protected void setUp() throws Exception + { + super.setUp(); + + m_resolver = createResolver(); + + m_context = new DefaultTaskContext( null, getServiceManager(), getLogger() ); + m_context.setProperty( "intProp", new Integer( 333 ) ); + m_context.setProperty( "stringProp", "String property" ); + } + + protected PropertyResolver createResolver() + { + return new DefaultPropertyResolver(); + } + + /** + * Test property resolution with various different typed properties. + */ + public void testPropertyTypes() throws Exception + { + testPropertyValue( new String( "String value" ) ); + testPropertyValue( new Date() ); + testPropertyValue( new Integer( Integer.MIN_VALUE ) ); + testPropertyValue( new Double( 24234.98453 ) ); + testPropertyValue( this.getClass() ); + testPropertyValue( File.createTempFile( "PropertyResolverTest", null ) ); + } + + /** + * Simple tests with property on it's own, and accompanied by text. + */ + private void testPropertyValue( Object propObject ) + throws Exception + { + m_context.setProperty( "typedProp", propObject ); + String propString = propObject.toString(); + + doTestResolution( "${typedProp}", propObject, m_context ); + doTestResolution( "${typedProp} with following text", + propString + " with following text", m_context ); + doTestResolution( "Preceding text with ${typedProp}", + "Preceding text with " + propString, m_context ); + } + + /** + * Tests multiple property declarations in a single value. + */ + public void testMultipleProperties() throws Exception + { + m_context.setProperty( "prop1", "value1" ); + m_context.setProperty( "prop2", "value2" ); + m_context.setProperty( "int1", new Integer( 123 ) ); + + doTestResolution( "${prop1}${prop2}", "value1value2", m_context ); + doTestResolution( "${prop1}${prop1}${prop1}", "value1value1value1", m_context ); + doTestResolution( "before ${prop2} between ${prop1} after", + "before value2 between value1 after", m_context ); + doTestResolution( "${prop1}-${int1}-${prop2}", "value1-123-value2", m_context ); + + } + + /** + * Tests handing undefined property. + */ + public void testUndefinedProp() throws Exception + { + String undefinedProp = "undefinedProperty"; + doTestFailure( "${" + undefinedProp + "}", + REZ.getString( "prop.missing-value.error", undefinedProp ), + m_context ); + + //TODO - "" should be disallowed as a property name + doTestFailure( "${}", + REZ.getString( "prop.missing-value.error", "" ), + m_context ); + } + + /** + * Tests illegal property syntax. + */ + public void testInvalidTypeDeclarations() throws Exception + { + + doTestFailure( "${unclosed", + REZ.getString( "prop.mismatched-braces.error" ), + m_context ); + doTestFailure( "${", + REZ.getString( "prop.mismatched-braces.error" ), + m_context ); + + /* TODO - need to handle these cases. */ + // testFailure( "${bad${}", "", m_context ); + // testFailure( "${ }", "", m_context ); + + } + + /** + * Resolves the property using the supplied context, and checks the result. + */ + protected void doTestResolution( String value, + Object expected, + Context context ) + throws Exception + { + Object resolved = m_resolver.resolveProperties( value, context ); + + assertEquals( expected, resolved ); + } + + /** + * Attempts to resolve the value using the supplied context, expecting to + * fail with the supplied error message. + */ + protected void doTestFailure( String value, + String expectedErrorMessage, + Context context ) + { + try + { + m_resolver.resolveProperties( value, context ); + fail( "Unexpected sucess - test should have failed." ); + } + catch( TaskException e ) + { + assertSameMessage( expectedErrorMessage, e ); + } + } +}