You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2007/02/11 00:43:28 UTC

svn commit: r505796 [1/2] - in /tapestry/tapestry5/tapestry-ioc/trunk/src: main/java/org/apache/tapestry/ioc/ main/java/org/apache/tapestry/ioc/internal/ main/java/org/apache/tapestry/ioc/internal/services/ main/java/org/apache/tapestry/ioc/internal/ut...

Author: hlship
Date: Sat Feb 10 15:43:27 2007
New Revision: 505796

URL: http://svn.apache.org/viewvc?view=rev&rev=505796
Log:
Add a Stack utility class for efficiently managing a stack.
Add a CaseInsensitiveMap implementation, a case-insensitive (but case-maintaining) implementation of Map (which requires that keys be strings).
Modify a number of key maps throughout the module to be case insensitive (for module ids, service ids, etc.).
Add documentation describing the use of case-insensitivity.

Added:
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/CaseInsensitiveMap.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/Stack.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/site/apt/case.apt
    tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/util/CaseInsensitiveMapTest.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/util/StackTest.java
Modified:
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/RegistryBuilder.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/DefaultModuleDefImpl.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/GlobPatternMatcher.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/ModuleImpl.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/AbstractFab.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/ClassPropertyAdapterImpl.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/MasterObjectProvider.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/util/CollectionFactory.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/util/MessagesImpl.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/util/Orderer.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassPropertyAdapter.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/services/MethodIterator.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/AbstractMessages.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/UtilMessages.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/main/resources/org/apache/tapestry/ioc/util/UtilStrings.properties
    tapestry/tapestry5/tapestry-ioc/trunk/src/site/apt/provider.apt
    tapestry/tapestry5/tapestry-ioc/trunk/src/site/site.xml
    tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/GlobPatternMatcherTest.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/services/MasterObjectProviderTest.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/services/PropertyAccessImplTest.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/util/MessagesImplTest.java
    tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/util/OrdererTest.java

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/RegistryBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/RegistryBuilder.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/RegistryBuilder.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/RegistryBuilder.java Sat Feb 10 15:43:27 2007
@@ -14,6 +14,10 @@
 
 package org.apache.tapestry.ioc;
 
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
+
 import java.lang.reflect.AnnotatedElement;
 import java.util.Arrays;
 import java.util.List;
@@ -29,9 +33,6 @@
 import org.apache.tapestry.ioc.internal.util.OneShotLock;
 import org.apache.tapestry.ioc.services.TapestryIOCModule;
 
-import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
-import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
-
 /**
  * Used to construct the IoC {@link org.apache.tapestry.ioc.Registry}. This class is <em>not</em>
  * threadsafe. The Registry, once created, <em>is</em> threadsafe.
@@ -41,7 +42,7 @@
     private final OneShotLock _lock = new OneShotLock();
 
     /** Module defs, keyed on module id. */
-    final Map<String, ModuleDef> _modules = newMap();
+    final Map<String, ModuleDef> _modules = newCaseInsensitiveMap();
 
     /**
      * Service implementation overrides, keyed on service id. Service implementations are most
@@ -49,7 +50,7 @@
      * have to stop at a certain "bounary" services by provide stub/ mock objects as their
      * implementations.
      */
-    private final Map<String, Object> _serviceOverrides = newMap();
+    private final Map<String, Object> _serviceOverrides = newCaseInsensitiveMap();
 
     private final ClassLoader _classLoader;
 

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/DefaultModuleDefImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/DefaultModuleDefImpl.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/DefaultModuleDefImpl.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/DefaultModuleDefImpl.java Sat Feb 10 15:43:27 2007
@@ -21,6 +21,7 @@
 import static org.apache.tapestry.ioc.internal.IOCMessages.buildMethodConflict;
 import static org.apache.tapestry.ioc.internal.IOCMessages.buildMethodWrongReturnType;
 import static org.apache.tapestry.ioc.internal.IOCMessages.decoratorMethodWrongReturnType;
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newSet;
 
@@ -49,6 +50,7 @@
 import org.apache.tapestry.ioc.def.DecoratorDef;
 import org.apache.tapestry.ioc.def.ModuleDef;
 import org.apache.tapestry.ioc.def.ServiceDef;
+import org.apache.tapestry.ioc.internal.util.CollectionFactory;
 
 /**
  * Starting from the Class for a module builder, identifies all the services (service builder
@@ -73,10 +75,10 @@
     private final Log _log;
 
     /** Keyed on fully qualified service id. */
-    private final Map<String, ServiceDef> _serviceDefs = newMap();
+    private final Map<String, ServiceDef> _serviceDefs = newCaseInsensitiveMap();
 
     /** Keyed on fully qualified decorator id. */
-    private final Map<String, DecoratorDef> _decoratorDefs = newMap();
+    private final Map<String, DecoratorDef> _decoratorDefs = newCaseInsensitiveMap();
 
     private final Set<ContributionDef> _contributionDefs = newSet();
 

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/GlobPatternMatcher.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/GlobPatternMatcher.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/GlobPatternMatcher.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/GlobPatternMatcher.java Sat Feb 10 15:43:27 2007
@@ -75,19 +75,24 @@
 
             case EXACT:
 
-                return input.equals(_substring);
+                return input.equalsIgnoreCase(_substring);
 
             case INFIX:
 
-                return input.contains(_substring);
+                return input.toLowerCase().contains(_substring.toLowerCase());
 
             case PREFIX:
 
-                return input.startsWith(_substring);
+                return input.regionMatches(true, 0, _substring, 0, _substring.length());
 
             default:
 
-                return input.endsWith(_substring);
+                return input.regionMatches(
+                        true,
+                        input.length() - _substring.length(),
+                        _substring,
+                        0,
+                        _substring.length());
         }
     }
 }

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/ModuleImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/ModuleImpl.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/ModuleImpl.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/ModuleImpl.java Sat Feb 10 15:43:27 2007
@@ -12,456 +12,449 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package org.apache.tapestry.ioc.internal;
-
-import static java.lang.String.format;
+package org.apache.tapestry.ioc.internal;
+
+import static java.lang.String.format;
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newSet;
 import static org.apache.tapestry.ioc.internal.util.Defense.notBlank;
 import static org.apache.tapestry.ioc.internal.util.Defense.notNull;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Modifier;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.commons.logging.Log;
-import org.apache.tapestry.ioc.ObjectCreator;
-import org.apache.tapestry.ioc.ServiceBuilderResources;
-import org.apache.tapestry.ioc.ServiceDecorator;
-import org.apache.tapestry.ioc.ServiceLifecycle;
-import org.apache.tapestry.ioc.ServiceLocator;
-import org.apache.tapestry.ioc.ServiceResources;
-import org.apache.tapestry.ioc.def.ContributionDef;
-import org.apache.tapestry.ioc.def.DecoratorDef;
-import org.apache.tapestry.ioc.def.ModuleDef;
-import org.apache.tapestry.ioc.def.ServiceDef;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.tapestry.ioc.ObjectCreator;
+import org.apache.tapestry.ioc.ServiceBuilderResources;
+import org.apache.tapestry.ioc.ServiceDecorator;
+import org.apache.tapestry.ioc.ServiceLifecycle;
+import org.apache.tapestry.ioc.ServiceLocator;
+import org.apache.tapestry.ioc.ServiceResources;
+import org.apache.tapestry.ioc.def.ContributionDef;
+import org.apache.tapestry.ioc.def.DecoratorDef;
+import org.apache.tapestry.ioc.def.ModuleDef;
+import org.apache.tapestry.ioc.def.ServiceDef;
 import org.apache.tapestry.ioc.internal.util.InternalUtils;
-import org.apache.tapestry.ioc.services.ClassFab;
-import org.apache.tapestry.ioc.services.MethodSignature;
-import org.apache.tapestry.ioc.services.RegistryShutdownListener;
+import org.apache.tapestry.ioc.services.ClassFab;
+import org.apache.tapestry.ioc.services.MethodSignature;
+import org.apache.tapestry.ioc.services.RegistryShutdownListener;
 import org.apache.tapestry.ioc.util.BodyBuilder;
-
-/**
- * 
- */
-public class ModuleImpl implements Module
-{
-    private final InternalRegistry _registry;
-
-    private final ModuleDef _moduleDef;
-
-    private final Log _log;
-
-    private Object _moduleBuilder;
-
-    private final static String INTERNAL_MODULE_ID = "tapestry.ioc";
-
-    // Set to true when invoking the module constructor. Used to
-    // detect endless loops caused by irresponsible dependencies into
-    // the constructor.
-
-    private boolean _insideConstructor;
-
-    public ModuleImpl(InternalRegistry registry, ModuleDef moduleDef, Log log)
-    {
-        _registry = registry;
-        _moduleDef = moduleDef;
-        _log = log;
-    }
-
-    /** Keyed on fully qualified service id; values are instantiated services (proxies). */
-    private final Map<String, Object> _services = newMap();
-
-    public <T> T getService(String serviceId, Class<T> serviceInterface, Module module)
-    {
-        notBlank(serviceId, "serviceId");
-        notNull(serviceInterface, "serviceInterface");
-        // module may be null.
-
-        ServiceDef def = _moduleDef.getServiceDef(serviceId);
-
-        if (def == null)
-            throw new IllegalArgumentException(IOCMessages.missingService(serviceId));
-
-        if (notVisible(def, module))
-            throw new RuntimeException(IOCMessages.serviceIsPrivate(serviceId));
-
-        Object service = findOrCreate(def);
-
-        try
-        {
-            return serviceInterface.cast(service);
-        }
-        catch (ClassCastException ex)
-        {
-            // This may be overkill: I don't know how this could happen
-            // given that the return type of the method determines
-            // the service interface.
-
-            throw new RuntimeException(IOCMessages.serviceWrongInterface(serviceId, def
-                    .getServiceInterface(), serviceInterface));
-        }
-    }
-
-    public Set<DecoratorDef> findMatchingDecoratorDefs(ServiceDef serviceDef)
-    {
-        Set<DecoratorDef> result = newSet();
-
-        for (DecoratorDef def : _moduleDef.getDecoratorDefs())
-        {
-            if (def.matches(serviceDef))
-                result.add(def);
-        }
-
-        return result;
-    }
-
-    private boolean notVisible(ServiceDef def, Module module)
-    {
-        return def.isPrivate() && this != module;
-    }
-
-    public List<ServiceDecorator> findDecoratorsForService(String serviceId)
-    {
-        ServiceDef sd = _moduleDef.getServiceDef(serviceId);
-
-        return _registry.findDecoratorsForService(sd);
-    }
-
-    public Collection<String> findServiceIdsForInterface(Class serviceInterface, Module module)
-    {
-        notNull(serviceInterface, "serviceInterface");
-
-        Collection<String> result = newList();
-
-        for (String id : _moduleDef.getServiceIds())
-        {
-            ServiceDef def = _moduleDef.getServiceDef(id);
-
-            if (def.getServiceInterface() != serviceInterface)
-                continue;
-
-            if (notVisible(def, module))
-                continue;
-
-            result.add(id);
-        }
-
-        return result;
-    }
-
-    // Why synchronized here? Two reasons. First, with some lifecycle models (or perhaps in some
-    // scenarios using interceptors), we may try to acquire the write lock a second time and the
-    // @Concurrent.Write annotation doesn't currently support that. Second, I'm concerned about
-    // multiple threads building services simultaneously, and getting into a thread deadlock. Of
-    // course, this isn't a solution for that ... we may need a global mutex to handle that specific
-    // case! Alternately, I've thought about creating a "service creation" thread at startup and
-    // queuing service creation requests to that thread, and blocking the local thread.
-
-    private synchronized Object findOrCreate(ServiceDef def)
-    {
-        String key = def.getServiceId();
-
-        Object result = _services.get(key);
-
-        if (result == null)
-        {
-            result = create(def);
-            _services.put(key, result);
-        }
-
-        return result;
-    }
-
-    public void eagerLoadServices()
-    {
-        for (String id : _moduleDef.getServiceIds())
-        {
-            ServiceDef def = _moduleDef.getServiceDef(id);
-
-            if (!def.isEagerLoad())
-                continue;
-
-            // The proxy implements the service interface, and RegistryShutdownListener, and (for
-            // eager load services), EagerLoadServiceProxy
-
-            EagerLoadServiceProxy proxy = (EagerLoadServiceProxy) findOrCreate(def);
-
-            proxy.eagerLoadService();
-        }
-    }
-
-    /**
-     * Creates the service and updates the cache of created services. Method is called from
-     * synchronized block.
-     */
-    private Object create(ServiceDef def)
-    {
-        String serviceId = def.getServiceId();
-
-        Log log = _registry.getLog(serviceId);
-
-        if (log.isDebugEnabled())
-            log.debug(IOCMessages.creatingService(serviceId));
-
-        try
-        {
-            ServiceLifecycle lifecycle = _registry.getServiceLifecycle(def.getServiceLifeycle());
-
-            ServiceBuilderResources resources = new ServiceResourcesImpl(_registry, this, def, log);
-
-            // Build up a stack of operations that will be needed to instantiate the service
-            // (by the proxy, at a later date).
-
-            ObjectCreator creator = def.createServiceCreator(resources);
-
-            creator = new LifecycleWrappedServiceCreator(lifecycle, resources, creator);
-
-            // Don't allow the tapestry.ioc services to be decorated.
-
-            if (!getModuleId().equals(INTERNAL_MODULE_ID))
-                creator = new InterceptorStackBuilder(this, serviceId, creator);
-
-            // Add a wrapper that makes sure that it only gets created once.
-
-            creator = new OneShotServiceCreator(def, creator);
-
-            Object service = createProxy(resources, creator, def.isEagerLoad());
-
-            _services.put(serviceId, service);
-
-            return service;
-        }
-        catch (Exception ex)
-        {
-            throw new RuntimeException(IOCMessages.errorBuildingService(serviceId, def, ex), ex);
-        }
-    }
-
-    public synchronized Object getModuleBuilder()
-    {
-        if (_moduleBuilder == null)
-            _moduleBuilder = instantiateModuleBuilder();
-
-        return _moduleBuilder;
-    }
-
-    private Object instantiateModuleBuilder()
-    {
-        Class builderClass = _moduleDef.getBuilderClass();
-
-        Constructor[] constructors = builderClass.getConstructors();
-
-        if (constructors.length == 0)
-            throw new RuntimeException(IOCMessages.noPublicConstructors(
-                    _moduleDef.getModuleId(),
-                    builderClass));
-
-        if (constructors.length > 1)
-        {
-            // Sort the constructors ascending by number of parameters (descending); this is really
-            // just to allow the test suite to work properly across different JVMs (which will
-            // often order the constructors differently).
-
-            Comparator<Constructor> comparator = new Comparator<Constructor>()
-            {
-                public int compare(Constructor c1, Constructor c2)
-                {
-                    return c2.getParameterTypes().length - c1.getParameterTypes().length;
-                }
-            };
-
-            Arrays.sort(constructors, comparator);
-
-            _log.warn(IOCMessages.tooManyPublicConstructors(
-                    _moduleDef.getModuleId(),
-                    builderClass,
-                    constructors[0]));
-        }
-
-        Constructor constructor = constructors[0];
-
-        if (_insideConstructor)
-            throw new RuntimeException(IOCMessages.recursiveModuleConstructor(_moduleDef
-                    .getModuleId(), builderClass, constructor));
-
-        ServiceLocator locator = new ServiceLocatorImpl(_registry, this);
-        Map<Class, Object> parameterDefaults = newMap();
-
-        parameterDefaults.put(Log.class, _log);
-        parameterDefaults.put(String.class, _moduleDef.getModuleId());
-        parameterDefaults.put(ServiceLocator.class, locator);
-
-        Throwable fail = null;
-
-        try
-        {
-            _insideConstructor = true;
-
-            Object[] parameterValues = InternalUtils.calculateParameters(
-                    locator,
-                    parameterDefaults,
-                    constructor.getParameterTypes(),
-                    constructor.getParameterAnnotations());
-
-            return constructor.newInstance(parameterValues);
-        }
-        catch (InvocationTargetException ex)
-        {
-            fail = ex.getTargetException();
-        }
-        catch (Exception ex)
-        {
-            fail = ex;
-        }
-        finally
-        {
-            _insideConstructor = false;
-        }
-
-        throw new RuntimeException(IOCMessages.instantiateBuilderError(builderClass, _moduleDef
-                .getModuleId(), fail), fail);
-    }
-
-    private Object createProxy(ServiceResources resources, ObjectCreator creator, boolean eagerLoad)
-    {
-        String serviceId = resources.getServiceId();
-        Class serviceInterface = resources.getServiceInterface();
-
-        String toString = format("<Proxy for %s(%s)>", serviceId, serviceInterface.getName());
-
-        RegistryShutdownListener proxy = createProxyInstance(
-                creator,
-                serviceId,
-                serviceInterface,
-                eagerLoad,
-                toString);
-
-        _registry.addRegistryShutdownListener(proxy);
-
-        return proxy;
-    }
-
-    private RegistryShutdownListener createProxyInstance(ObjectCreator creator, String serviceId,
-            Class serviceInterface, boolean eagerLoad, String description)
-    {
-        Class proxyClass = createProxyClass(serviceId, serviceInterface, eagerLoad, description);
-
-        try
-        {
-            return (RegistryShutdownListener) proxyClass.getConstructors()[0].newInstance(creator);
-        }
-        catch (Exception ex)
-        {
-            // This should never happen, so we won't go to a lot of trouble
-            // reporting it.
-            throw new RuntimeException(ex.getMessage(), ex);
-        }
-    }
-
-    private Class createProxyClass(String serviceId, Class serviceInterface, boolean eagerLoad,
-            String proxyDescription)
-    {
-        ClassFab cf = _registry.newClass(serviceInterface);
-
-        cf.addField("_creator", ObjectCreator.class);
-        cf.addField("_delegate", serviceInterface);
-        cf.addField("_shutdown", boolean.class);
-
-        cf.addConstructor(new Class[]
-        { ObjectCreator.class }, null, "_creator = $1;");
-
-        addDelegateGetter(cf, serviceInterface, serviceId);
-
-        addShutdownListenerMethod(cf);
-
-        cf.proxyMethodsToDelegate(serviceInterface, "_delegate()", proxyDescription);
-
-        // For eager load services, add an eagerLoadService() method that calls _delegate(), to
-        // force the creation of the service.
-
-        if (eagerLoad)
-        {
-            cf.addInterface(EagerLoadServiceProxy.class);
-
-            cf.addMethod(Modifier.PUBLIC, new MethodSignature(void.class, "eagerLoadService", null,
-                    null), "_delegate();");
-        }
-
-        return cf.createClass();
-    }
-
-    private void addDelegateGetter(ClassFab cf, Class serviceInterface, String serviceId)
-    {
-        BodyBuilder builder = new BodyBuilder();
-        builder.begin();
-
-        // Check to see if the registry has shutdown. The registryShutdown() method
-        // throws IllegalStateException.
-
-        builder.addln("if (_shutdown) %s.registryShutdown(\"%s\");", IOCProxyUtilities.class
-                .getName(), serviceId);
-
-        // We can release the creator after invoking it, we only create the service once.
-
-        builder.addln("if (_delegate == null)");
-        builder.begin();
-        builder.addln("_delegate = (%s) _creator.createObject();", serviceInterface.getName());
-        builder.addln("_creator = null;");
-        builder.end();
-
-        builder.addln("return _delegate;");
-        builder.end();
-
-        MethodSignature sig = new MethodSignature(serviceInterface, "_delegate", null, null);
-
-        // Here's the rub, this _delegate() method has to be synchronized. But after the first
-        // time through (when we create the service), the time inside the method is infintesmal.
-        // Let's hope that they aren't lying when they say that synchronized is now super cheap!
-
-        cf.addMethod(Modifier.PRIVATE | Modifier.SYNCHRONIZED, sig, builder.toString());
-    }
-
-    /**
-     * All proxies implement {@link RegistryShutdownListener}. When the registry shuts down, the
-     * proxy sets a flag that ultimately converts method invocations to
-     * {@link IllegalStateException}s, and discards its delegate and creator.
-     */
-    private void addShutdownListenerMethod(ClassFab cf)
-    {
-        cf.addInterface(RegistryShutdownListener.class);
-
-        MethodSignature sig = new MethodSignature(void.class, "registryDidShutdown", null, null);
-
-        cf.addMethod(
-                Modifier.PUBLIC | Modifier.SYNCHRONIZED,
-                sig,
-                "{ _shutdown = true; _delegate = null; _creator = null; }");
-    }
-
-    public String getModuleId()
-    {
-        return _moduleDef.getModuleId();
-    }
-
-    public Set<ContributionDef> getContributorDefsForService(String serviceId)
-    {
-        Set<ContributionDef> result = newSet();
-
-        for (ContributionDef def : _moduleDef.getContributionDefs())
-        {
-            if (def.getServiceId().equals(serviceId))
-                result.add(def);
-        }
-
-        return result;
-    }
-
-}
+
+/**
+ * 
+ */
+public class ModuleImpl implements Module
+{
+    private final InternalRegistry _registry;
+
+    private final ModuleDef _moduleDef;
+
+    private final Log _log;
+
+    private Object _moduleBuilder;
+
+    private final static String INTERNAL_MODULE_ID = "tapestry.ioc";
+
+    // Set to true when invoking the module constructor. Used to
+    // detect endless loops caused by irresponsible dependencies into
+    // the constructor.
+
+    private boolean _insideConstructor;
+
+    public ModuleImpl(InternalRegistry registry, ModuleDef moduleDef, Log log)
+    {
+        _registry = registry;
+        _moduleDef = moduleDef;
+        _log = log;
+    }
+
+    /** Keyed on fully qualified service id; values are instantiated services (proxies). */
+    private final Map<String, Object> _services = newCaseInsensitiveMap();
+
+    public <T> T getService(String serviceId, Class<T> serviceInterface, Module module)
+    {
+        notBlank(serviceId, "serviceId");
+        notNull(serviceInterface, "serviceInterface");
+        // module may be null.
+
+        ServiceDef def = _moduleDef.getServiceDef(serviceId);
+
+        if (def == null) throw new IllegalArgumentException(IOCMessages.missingService(serviceId));
+
+        if (notVisible(def, module))
+            throw new RuntimeException(IOCMessages.serviceIsPrivate(serviceId));
+
+        Object service = findOrCreate(def);
+
+        try
+        {
+            return serviceInterface.cast(service);
+        }
+        catch (ClassCastException ex)
+        {
+            // This may be overkill: I don't know how this could happen
+            // given that the return type of the method determines
+            // the service interface.
+
+            throw new RuntimeException(IOCMessages.serviceWrongInterface(serviceId, def
+                    .getServiceInterface(), serviceInterface));
+        }
+    }
+
+    public Set<DecoratorDef> findMatchingDecoratorDefs(ServiceDef serviceDef)
+    {
+        Set<DecoratorDef> result = newSet();
+
+        for (DecoratorDef def : _moduleDef.getDecoratorDefs())
+        {
+            if (def.matches(serviceDef)) result.add(def);
+        }
+
+        return result;
+    }
+
+    private boolean notVisible(ServiceDef def, Module module)
+    {
+        return def.isPrivate() && this != module;
+    }
+
+    public List<ServiceDecorator> findDecoratorsForService(String serviceId)
+    {
+        ServiceDef sd = _moduleDef.getServiceDef(serviceId);
+
+        return _registry.findDecoratorsForService(sd);
+    }
+
+    public Collection<String> findServiceIdsForInterface(Class serviceInterface, Module module)
+    {
+        notNull(serviceInterface, "serviceInterface");
+
+        Collection<String> result = newList();
+
+        for (String id : _moduleDef.getServiceIds())
+        {
+            ServiceDef def = _moduleDef.getServiceDef(id);
+
+            if (def.getServiceInterface() != serviceInterface) continue;
+
+            if (notVisible(def, module)) continue;
+
+            result.add(id);
+        }
+
+        return result;
+    }
+
+    // Why synchronized here? Two reasons. First, with some lifecycle models (or perhaps in some
+    // scenarios using interceptors), we may try to acquire the write lock a second time and the
+    // @Concurrent.Write annotation doesn't currently support that. Second, I'm concerned about
+    // multiple threads building services simultaneously, and getting into a thread deadlock. Of
+    // course, this isn't a solution for that ... we may need a global mutex to handle that specific
+    // case! Alternately, I've thought about creating a "service creation" thread at startup and
+    // queuing service creation requests to that thread, and blocking the local thread.
+
+    private synchronized Object findOrCreate(ServiceDef def)
+    {
+        String key = def.getServiceId();
+
+        Object result = _services.get(key);
+
+        if (result == null)
+        {
+            result = create(def);
+            _services.put(key, result);
+        }
+
+        return result;
+    }
+
+    public void eagerLoadServices()
+    {
+        for (String id : _moduleDef.getServiceIds())
+        {
+            ServiceDef def = _moduleDef.getServiceDef(id);
+
+            if (!def.isEagerLoad()) continue;
+
+            // The proxy implements the service interface, and RegistryShutdownListener, and (for
+            // eager load services), EagerLoadServiceProxy
+
+            EagerLoadServiceProxy proxy = (EagerLoadServiceProxy) findOrCreate(def);
+
+            proxy.eagerLoadService();
+        }
+    }
+
+    /**
+     * Creates the service and updates the cache of created services. Method is called from
+     * synchronized block.
+     */
+    private Object create(ServiceDef def)
+    {
+        String serviceId = def.getServiceId();
+
+        Log log = _registry.getLog(serviceId);
+
+        if (log.isDebugEnabled()) log.debug(IOCMessages.creatingService(serviceId));
+
+        try
+        {
+            ServiceLifecycle lifecycle = _registry.getServiceLifecycle(def.getServiceLifeycle());
+
+            ServiceBuilderResources resources = new ServiceResourcesImpl(_registry, this, def, log);
+
+            // Build up a stack of operations that will be needed to instantiate the service
+            // (by the proxy, at a later date).
+
+            ObjectCreator creator = def.createServiceCreator(resources);
+
+            creator = new LifecycleWrappedServiceCreator(lifecycle, resources, creator);
+
+            // Don't allow the tapestry.ioc services to be decorated.
+
+            if (!getModuleId().equals(INTERNAL_MODULE_ID))
+                creator = new InterceptorStackBuilder(this, serviceId, creator);
+
+            // Add a wrapper that makes sure that it only gets created once.
+
+            creator = new OneShotServiceCreator(def, creator);
+
+            Object service = createProxy(resources, creator, def.isEagerLoad());
+
+            _services.put(serviceId, service);
+
+            return service;
+        }
+        catch (Exception ex)
+        {
+            throw new RuntimeException(IOCMessages.errorBuildingService(serviceId, def, ex), ex);
+        }
+    }
+
+    public synchronized Object getModuleBuilder()
+    {
+        if (_moduleBuilder == null) _moduleBuilder = instantiateModuleBuilder();
+
+        return _moduleBuilder;
+    }
+
+    private Object instantiateModuleBuilder()
+    {
+        Class builderClass = _moduleDef.getBuilderClass();
+
+        Constructor[] constructors = builderClass.getConstructors();
+
+        if (constructors.length == 0)
+            throw new RuntimeException(IOCMessages.noPublicConstructors(
+                    _moduleDef.getModuleId(),
+                    builderClass));
+
+        if (constructors.length > 1)
+        {
+            // Sort the constructors ascending by number of parameters (descending); this is really
+            // just to allow the test suite to work properly across different JVMs (which will
+            // often order the constructors differently).
+
+            Comparator<Constructor> comparator = new Comparator<Constructor>()
+            {
+                public int compare(Constructor c1, Constructor c2)
+                {
+                    return c2.getParameterTypes().length - c1.getParameterTypes().length;
+                }
+            };
+
+            Arrays.sort(constructors, comparator);
+
+            _log.warn(IOCMessages.tooManyPublicConstructors(
+                    _moduleDef.getModuleId(),
+                    builderClass,
+                    constructors[0]));
+        }
+
+        Constructor constructor = constructors[0];
+
+        if (_insideConstructor)
+            throw new RuntimeException(IOCMessages.recursiveModuleConstructor(_moduleDef
+                    .getModuleId(), builderClass, constructor));
+
+        ServiceLocator locator = new ServiceLocatorImpl(_registry, this);
+        Map<Class, Object> parameterDefaults = newMap();
+
+        parameterDefaults.put(Log.class, _log);
+        parameterDefaults.put(String.class, _moduleDef.getModuleId());
+        parameterDefaults.put(ServiceLocator.class, locator);
+
+        Throwable fail = null;
+
+        try
+        {
+            _insideConstructor = true;
+
+            Object[] parameterValues = InternalUtils.calculateParameters(
+                    locator,
+                    parameterDefaults,
+                    constructor.getParameterTypes(),
+                    constructor.getParameterAnnotations());
+
+            return constructor.newInstance(parameterValues);
+        }
+        catch (InvocationTargetException ex)
+        {
+            fail = ex.getTargetException();
+        }
+        catch (Exception ex)
+        {
+            fail = ex;
+        }
+        finally
+        {
+            _insideConstructor = false;
+        }
+
+        throw new RuntimeException(IOCMessages.instantiateBuilderError(builderClass, _moduleDef
+                .getModuleId(), fail), fail);
+    }
+
+    private Object createProxy(ServiceResources resources, ObjectCreator creator, boolean eagerLoad)
+    {
+        String serviceId = resources.getServiceId();
+        Class serviceInterface = resources.getServiceInterface();
+
+        String toString = format("<Proxy for %s(%s)>", serviceId, serviceInterface.getName());
+
+        RegistryShutdownListener proxy = createProxyInstance(
+                creator,
+                serviceId,
+                serviceInterface,
+                eagerLoad,
+                toString);
+
+        _registry.addRegistryShutdownListener(proxy);
+
+        return proxy;
+    }
+
+    private RegistryShutdownListener createProxyInstance(ObjectCreator creator, String serviceId,
+            Class serviceInterface, boolean eagerLoad, String description)
+    {
+        Class proxyClass = createProxyClass(serviceId, serviceInterface, eagerLoad, description);
+
+        try
+        {
+            return (RegistryShutdownListener) proxyClass.getConstructors()[0].newInstance(creator);
+        }
+        catch (Exception ex)
+        {
+            // This should never happen, so we won't go to a lot of trouble
+            // reporting it.
+            throw new RuntimeException(ex.getMessage(), ex);
+        }
+    }
+
+    private Class createProxyClass(String serviceId, Class serviceInterface, boolean eagerLoad,
+            String proxyDescription)
+    {
+        ClassFab cf = _registry.newClass(serviceInterface);
+
+        cf.addField("_creator", ObjectCreator.class);
+        cf.addField("_delegate", serviceInterface);
+        cf.addField("_shutdown", boolean.class);
+
+        cf.addConstructor(new Class[]
+        { ObjectCreator.class }, null, "_creator = $1;");
+
+        addDelegateGetter(cf, serviceInterface, serviceId);
+
+        addShutdownListenerMethod(cf);
+
+        cf.proxyMethodsToDelegate(serviceInterface, "_delegate()", proxyDescription);
+
+        // For eager load services, add an eagerLoadService() method that calls _delegate(), to
+        // force the creation of the service.
+
+        if (eagerLoad)
+        {
+            cf.addInterface(EagerLoadServiceProxy.class);
+
+            cf.addMethod(Modifier.PUBLIC, new MethodSignature(void.class, "eagerLoadService", null,
+                    null), "_delegate();");
+        }
+
+        return cf.createClass();
+    }
+
+    private void addDelegateGetter(ClassFab cf, Class serviceInterface, String serviceId)
+    {
+        BodyBuilder builder = new BodyBuilder();
+        builder.begin();
+
+        // Check to see if the registry has shutdown. The registryShutdown() method
+        // throws IllegalStateException.
+
+        builder.addln("if (_shutdown) %s.registryShutdown(\"%s\");", IOCProxyUtilities.class
+                .getName(), serviceId);
+
+        // We can release the creator after invoking it, we only create the service once.
+
+        builder.addln("if (_delegate == null)");
+        builder.begin();
+        builder.addln("_delegate = (%s) _creator.createObject();", serviceInterface.getName());
+        builder.addln("_creator = null;");
+        builder.end();
+
+        builder.addln("return _delegate;");
+        builder.end();
+
+        MethodSignature sig = new MethodSignature(serviceInterface, "_delegate", null, null);
+
+        // Here's the rub, this _delegate() method has to be synchronized. But after the first
+        // time through (when we create the service), the time inside the method is infintesmal.
+        // Let's hope that they aren't lying when they say that synchronized is now super cheap!
+
+        cf.addMethod(Modifier.PRIVATE | Modifier.SYNCHRONIZED, sig, builder.toString());
+    }
+
+    /**
+     * All proxies implement {@link RegistryShutdownListener}. When the registry shuts down, the
+     * proxy sets a flag that ultimately converts method invocations to
+     * {@link IllegalStateException}s, and discards its delegate and creator.
+     */
+    private void addShutdownListenerMethod(ClassFab cf)
+    {
+        cf.addInterface(RegistryShutdownListener.class);
+
+        MethodSignature sig = new MethodSignature(void.class, "registryDidShutdown", null, null);
+
+        cf.addMethod(
+                Modifier.PUBLIC | Modifier.SYNCHRONIZED,
+                sig,
+                "{ _shutdown = true; _delegate = null; _creator = null; }");
+    }
+
+    public String getModuleId()
+    {
+        return _moduleDef.getModuleId();
+    }
+
+    public Set<ContributionDef> getContributorDefsForService(String serviceId)
+    {
+        Set<ContributionDef> result = newSet();
+
+        for (ContributionDef def : _moduleDef.getContributionDefs())
+        {
+            if (def.getServiceId().equals(serviceId)) result.add(def);
+        }
+
+        return result;
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/RegistryImpl.java Sat Feb 10 15:43:27 2007
@@ -14,6 +14,7 @@
 
 package org.apache.tapestry.ioc.internal;
 
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
 
@@ -75,18 +76,18 @@
 
     private final OneShotLock _lock = new OneShotLock();
 
-    private final Map<String, Object> _builtinServices = newMap();
+    private final Map<String, Object> _builtinServices = newCaseInsensitiveMap();
 
-    private final Map<String, Class> _builtinTypes = newMap();
+    private final Map<String, Class> _builtinTypes = newCaseInsensitiveMap();
 
     private final RegistryShutdownHubImpl _registryShutdownHub;
 
     private final LogSource _logSource;
 
     /** Keyed on module id. */
-    private final Map<String, Module> _modules = newMap();
+    private final Map<String, Module> _modules = newCaseInsensitiveMap();
 
-    private final Map<String, ServiceLifecycle> _lifecycles = newMap();
+    private final Map<String, ServiceLifecycle> _lifecycles = newCaseInsensitiveMap();
 
     /**
      * Service implementation overrides, keyed on service id. Service implementations are most

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/AbstractFab.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/AbstractFab.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/AbstractFab.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/AbstractFab.java Sat Feb 10 15:43:27 2007
@@ -59,8 +59,7 @@
         try
         {
             for (CtClass existing : _ctClass.getInterfaces())
-                if (existing == ctInterfaceClass)
-                    return;
+                if (existing == ctInterfaceClass) return;
         }
         catch (Exception ex)
         {
@@ -72,8 +71,7 @@
 
     protected CtClass[] convertClasses(Class[] inputClasses)
     {
-        if (inputClasses == null || inputClasses.length == 0)
-            return null;
+        if (inputClasses == null || inputClasses.length == 0) return null;
 
         int count = inputClasses.length;
         CtClass[] result = new CtClass[count];
@@ -105,8 +103,7 @@
     {
         _lock.lock();
 
-        if (_log.isDebugEnabled())
-            _log.debug(String.format("Creating class from %s", this));
+        if (_log.isDebugEnabled()) _log.debug(String.format("Creating class from %s", this));
 
         return _source.createClass(_ctClass);
     }

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/ClassPropertyAdapterImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/ClassPropertyAdapterImpl.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/ClassPropertyAdapterImpl.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/ClassPropertyAdapterImpl.java Sat Feb 10 15:43:27 2007
@@ -14,7 +14,7 @@
 
 package org.apache.tapestry.ioc.internal.services;
 
-import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
 
 import java.beans.PropertyDescriptor;
 import java.util.List;
@@ -26,7 +26,7 @@
 
 public class ClassPropertyAdapterImpl implements ClassPropertyAdapter
 {
-    private final Map<String, PropertyAdapter> _adapters = newMap();
+    private final Map<String, PropertyAdapter> _adapters = newCaseInsensitiveMap();
 
     private final Class _beanType;
 

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/MasterObjectProvider.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/MasterObjectProvider.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/MasterObjectProvider.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/services/MasterObjectProvider.java Sat Feb 10 15:43:27 2007
@@ -18,6 +18,7 @@
 
 import org.apache.tapestry.ioc.ObjectProvider;
 import org.apache.tapestry.ioc.ServiceLocator;
+import org.apache.tapestry.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry.ioc.services.SymbolSource;
 import org.apache.tapestry.ioc.services.TypeCoercer;
 
@@ -37,7 +38,8 @@
     public MasterObjectProvider(Map<String, ObjectProvider> configuration,
             SymbolSource symbolSource, TypeCoercer typeCoercer)
     {
-        _configuration = configuration;
+        _configuration = CollectionFactory.newCaseInsensitiveMap(configuration);
+
         _symbolSource = symbolSource;
         _typeCoercer = typeCoercer;
     }

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/util/CollectionFactory.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/util/CollectionFactory.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/util/CollectionFactory.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/util/CollectionFactory.java Sat Feb 10 15:43:27 2007
@@ -26,6 +26,9 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 
+import org.apache.tapestry.ioc.util.CaseInsensitiveMap;
+import org.apache.tapestry.ioc.util.Stack;
+
 /**
  * Static factory methods to ease the creation of new collection types (when using generics). Most
  * of these method leverage the compiler's ability to match generic types by return value. Typical
@@ -119,5 +122,20 @@
     public static <T> List<T> newThreadSafeList()
     {
         return new CopyOnWriteArrayList<T>();
+    }
+
+    public static <T> Stack<T> newStack()
+    {
+        return new Stack<T>();
+    }
+
+    public static <V> Map<String, V> newCaseInsensitiveMap()
+    {
+        return new CaseInsensitiveMap<V>();
+    }
+    
+    public static <V> Map<String, V> newCaseInsensitiveMap(Map<String, ? extends V> map)
+    {
+        return new CaseInsensitiveMap<V>(map);
     }
 }

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/util/MessagesImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/util/MessagesImpl.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/util/MessagesImpl.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/util/MessagesImpl.java Sat Feb 10 15:43:27 2007
@@ -14,7 +14,7 @@
 
 package org.apache.tapestry.ioc.internal.util;
 
-import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
 
 import java.util.Enumeration;
 import java.util.Locale;
@@ -30,7 +30,7 @@
  */
 public class MessagesImpl extends AbstractMessages
 {
-    private final Map<String, String> _properties = newMap();
+    private final Map<String, String> _properties = newCaseInsensitiveMap();
 
     /**
      * Finds the messages for a given Messages utility class. Strings the trailing "Messages" and

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/util/Orderer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/util/Orderer.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/util/Orderer.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/internal/util/Orderer.java Sat Feb 10 15:43:27 2007
@@ -14,8 +14,8 @@
 
 package org.apache.tapestry.ioc.internal.util;
 
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
 import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
-import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
 
 import java.util.Collection;
 import java.util.List;
@@ -40,9 +40,9 @@
 
     private final List<Orderable> _orderables = newList();
 
-    private final Map<String, Orderable<T>> _orderablesById = newMap();
+    private final Map<String, Orderable<T>> _orderablesById = newCaseInsensitiveMap();
 
-    private final Map<String, DependencyNode<T>> _dependencyNodesById = newMap();
+    private final Map<String, DependencyNode<T>> _dependencyNodesById = newCaseInsensitiveMap();
 
     // Special node that is always dead last: all other nodes are a dependency
     // of the trailer.
@@ -136,8 +136,7 @@
 
             // Nulls are placeholders that are skipped.
 
-            if (target != null)
-                result.add(target);
+            if (target != null) result.add(target);
         }
 
         return result;
@@ -192,8 +191,7 @@
 
         if ("after".equals(type))
             linker = _after;
-        else if ("before".equals(type))
-            linker = _before;
+        else if ("before".equals(type)) linker = _before;
 
         if (linker == null)
         {
@@ -228,11 +226,9 @@
 
         for (String id : _dependencyNodesById.keySet())
         {
-            if (sourceId.equals(id))
-                continue;
+            if (sourceId.equals(id)) continue;
 
-            if (matcher.matches(id))
-                result.add(_dependencyNodesById.get(id));
+            if (matcher.matches(id)) result.add(_dependencyNodesById.get(id));
         }
 
         return result;

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassPropertyAdapter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassPropertyAdapter.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassPropertyAdapter.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassPropertyAdapter.java Sat Feb 10 15:43:27 2007
@@ -17,9 +17,11 @@
 import java.util.List;
 
 /**
- * Organizes all {@link org.apache.tapestry.ioc.services.PropertyAdapter}s for a particular class. *
+ * Organizes all {@link org.apache.tapestry.ioc.services.PropertyAdapter}s for a particular class.
  * <p>
  * Only provides access to <em>simple</em> properties. Indexed properties are ignored.
+ * <p>
+ * When acessing properties by name, the case of the name is ignored.
  */
 public interface ClassPropertyAdapter
 {
@@ -31,12 +33,19 @@
 
     /**
      * Returns the property adapter with the given name, or null if no such adapter exists.
+     * 
+     * @param name
+     *            of the property (case is ignored)
      */
     PropertyAdapter getPropertyAdapter(String name);
 
     /**
      * Reads the value of a property.
      * 
+     * @param instance
+     *            the object to read a value from
+     * @param propertyName
+     *            the name of the property to read (case is ignored)
      * @throws UnsupportedOperationException
      *             if the property is write only
      * @throws IllegalArgumentException
@@ -45,8 +54,12 @@
     Object get(Object instance, String propertyName);
 
     /**
-     * Updates the value of a property. *
+     * Updates the value of a property.
      * 
+     * @param instance
+     *            the object to update
+     * @param propertyName
+     *            the name of the property to update (case is ignored)
      * @throws UnsupportedOperationException
      *             if the property is read only
      * @throws IllegalArgumentException

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/services/MethodIterator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/services/MethodIterator.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/services/MethodIterator.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/services/MethodIterator.java Sat Feb 10 15:43:27 2007
@@ -28,7 +28,6 @@
  * method from multiple interfaces and with slightly different signatures (due to the fact that
  * declared thrown exceptions can vary slightly for the "same" method).
  * 
- * @author Howard Lewis Ship
  * @see org.apache.tapestry.ioc.services.MethodSignature#isOverridingSignatureOf(MethodSignature)
  */
 public class MethodIterator
@@ -54,7 +53,6 @@
         _count = _signatures.size();
     }
 
-    /** @since 1.1 */
     private void processMethod(Method m, Map<String, MethodSignature> map)
     {
         _toString |= ClassFabUtils.isToString(m);
@@ -64,8 +62,7 @@
 
         MethodSignature existing = map.get(uid);
 
-        if (existing == null || sig.isOverridingSignatureOf(existing))
-            map.put(uid, sig);
+        if (existing == null || sig.isOverridingSignatureOf(existing)) map.put(uid, sig);
     }
 
     public boolean hasNext()
@@ -84,8 +81,7 @@
      */
     public MethodSignature next()
     {
-        if (_index >= _count)
-            throw new NoSuchElementException();
+        if (_index >= _count) throw new NoSuchElementException();
 
         return _signatures.get(_index++);
     }

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/AbstractMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/AbstractMessages.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/AbstractMessages.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/AbstractMessages.java Sat Feb 10 15:43:27 2007
@@ -14,12 +14,14 @@
 
 package org.apache.tapestry.ioc.util;
 
-import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newThreadSafeMap;
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
 
 import java.util.Map;
 
 import org.apache.tapestry.ioc.MessageFormatter;
 import org.apache.tapestry.ioc.Messages;
+import org.apache.tapestry.ioc.internal.util.ConcurrentBarrier;
+import org.apache.tapestry.ioc.internal.util.Invokable;
 import org.apache.tapestry.ioc.internal.util.MessageFormatterImpl;
 
 /**
@@ -28,15 +30,17 @@
  */
 public abstract class AbstractMessages implements Messages
 {
+    private final ConcurrentBarrier _barrier = new ConcurrentBarrier();
+
     /** String key to MF instance. */
-    private Map<String, MessageFormatter> _cache = newThreadSafeMap();
+    private final Map<String, MessageFormatter> _cache = newCaseInsensitiveMap();
 
     /**
      * Invoked to provide the value for a particular key. This may be invoked multiple times even
-     * for the same key.
+     * for the same key. The implementation should <em>ignore the case of the key</em>.
      * 
      * @param key
-     *            the key to obtain a value for
+     *            the key to obtain a value for (case insensitive)
      * @return the value for the key, or null if this instance can not provide the value
      */
     protected abstract String valueForKey(String key);
@@ -53,17 +57,29 @@
         return String.format("[[missing key: %s]]", key);
     }
 
-    public MessageFormatter getFormatter(String key)
+    public MessageFormatter getFormatter(final String key)
     {
-        MessageFormatter result = _cache.get(key);
+        MessageFormatter result = _barrier.withRead(new Invokable<MessageFormatter>()
+        {
+            public MessageFormatter invoke()
+            {
+                return _cache.get(key);
+            }
+        });
+
+        if (result != null) return result;
+
+        final MessageFormatter newFormatter = buildMessageFormatter(key);
 
-        if (result == null)
+        _barrier.withWrite(new Runnable()
         {
-            result = buildMessageFormatter(key);
-            _cache.put(key, result);
-        }
+            public void run()
+            {
+                _cache.put(key, newFormatter);
+            };
+        });
 
-        return result;
+        return newFormatter;
     }
 
     private MessageFormatter buildMessageFormatter(String key)

Added: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/CaseInsensitiveMap.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/CaseInsensitiveMap.java?view=auto&rev=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/CaseInsensitiveMap.java (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/CaseInsensitiveMap.java Sat Feb 10 15:43:27 2007
@@ -0,0 +1,479 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.ioc.util;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * An mapped collection where the keys are always strings and access to values is case-insensitive.
+ * The case of keys in the map is <em>maintained</em>, but on any access to a key (directly or
+ * indirectly), all key comparisons are performed in a case-insensitive manner. The map
+ * implementation is intended to support a reasonably finite number (dozens or hundreds, not
+ * thousands or millions of key/value pairs. Unlike HashMap, it is based on a sorted list of entries
+ * rather than hash bucket. It is also geared towards a largely static map, one that is created and
+ * then used without modification.
+ * 
+ * @param <V>
+ *            the type of value stored
+ */
+public class CaseInsensitiveMap<V> extends AbstractMap<String, V> implements Map<String, V>
+{
+    private static final int NULL_HASH = Integer.MIN_VALUE;
+
+    private static final int DEFAULT_SIZE = 20;
+
+    private static class CIMEntry<V> implements Map.Entry<String, V>
+    {
+        private String _key;
+
+        private final int _hashCode;
+
+        V _value;
+
+        public CIMEntry(final String key, final int hashCode, V value)
+        {
+            _key = key;
+            _hashCode = hashCode;
+            _value = value;
+        }
+
+        public String getKey()
+        {
+            return _key;
+        }
+
+        public V getValue()
+        {
+            return _value;
+        }
+
+        public V setValue(V value)
+        {
+            V result = _value;
+
+            _value = value;
+
+            return result;
+        }
+
+        /**
+         * Returns true if both keys are null, or if the provided key is the same as, or
+         * case-insensitively equal to, the entrie's key.
+         * 
+         * @param key
+         *            to compare against
+         * @return true if equal
+         */
+        boolean matches(String key)
+        {
+            return key == _key || (key != null && key.equalsIgnoreCase(_key));
+        }
+
+        boolean valueMatches(Object value)
+        {
+            return value == _value || (value != null && value.equals(_value));
+        }
+    }
+
+    private class EntrySetIterator implements Iterator
+    {
+        int _expectedModCount = _modCount;
+
+        int _index;
+
+        int _current = -1;
+
+        public boolean hasNext()
+        {
+            return _index < _size;
+        }
+
+        public Object next()
+        {
+            check();
+
+            if (_index >= _size) throw new NoSuchElementException();
+
+            _current = _index++;
+
+            return _entries[_current];
+        }
+
+        public void remove()
+        {
+            check();
+
+            if (_current < 0) throw new NoSuchElementException();
+
+            _cursor = _current;
+            removeAtCursor();
+
+            _expectedModCount = _modCount;
+        }
+
+        private void check()
+        {
+            if (_expectedModCount != _modCount) throw new ConcurrentModificationException();
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private class EntrySet extends AbstractSet
+    {
+        @Override
+        public Iterator iterator()
+        {
+            return new EntrySetIterator();
+        }
+
+        @Override
+        public int size()
+        {
+            return _size;
+        }
+
+        @Override
+        public void clear()
+        {
+            CaseInsensitiveMap.this.clear();
+        }
+
+        @Override
+        public boolean contains(Object o)
+        {
+            if (!(o instanceof Map.Entry)) return false;
+
+            Map.Entry e = (Map.Entry) o;
+
+            select(e.getKey());
+
+            if (!_found) return false;
+
+            return _entries[_cursor].valueMatches(e.getValue());
+        }
+
+        @Override
+        public boolean remove(Object o)
+        {
+            if (!(o instanceof Map.Entry)) return false;
+
+            Map.Entry e = (Map.Entry) o;
+
+            select(e.getKey());
+
+            if (!_found) return false;
+
+            if (!_entries[_cursor].valueMatches(e.getValue())) return false;
+
+            removeAtCursor();
+
+            return true;
+        }
+
+    }
+
+    // The list of entries. This is kept sorted by hash code. In some cases, there may be different
+    // keys with the same hash code in adjacent indexes.
+    private CIMEntry<V>[] _entries;
+
+    private int _size = 0;
+
+    // Used by iterators to check for concurrent modifications
+
+    private transient int _modCount = 0;
+
+    private transient Set<Map.Entry<String, V>> _entrySet;
+
+    private transient int _cursor;
+
+    private transient boolean _found;
+
+    public CaseInsensitiveMap()
+    {
+        this(DEFAULT_SIZE);
+    }
+
+    @SuppressWarnings("unchecked")
+    public CaseInsensitiveMap(int size)
+    {
+        _entries = new CIMEntry[Math.max(size, 3)];
+    }
+
+    public CaseInsensitiveMap(Map<String, ? extends V> map)
+    {
+        this(map.size());
+
+        for (Map.Entry<String, ? extends V> entry : map.entrySet())
+        {
+            put(entry.getKey(), entry.getValue());
+        }
+    }
+
+    @Override
+    public void clear()
+    {
+        for (int i = 0; i < _size; i++)
+            _entries[i] = null;
+
+        _size = 0;
+        _modCount++;
+    }
+
+    @Override
+    public boolean isEmpty()
+    {
+        return _size == 0;
+    }
+
+    @Override
+    public int size()
+    {
+        return _size;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public V put(String key, V value)
+    {
+        int hashCode = caseInsenitiveHashCode(key);
+
+        select(key, hashCode);
+
+        if (_found)
+        {
+            CIMEntry<V> e = _entries[_cursor];
+
+            V result = e._value;
+
+            // Not a structural change, so no change to _modCount
+
+            // Update the key (to maintain case). By definition, the hash code
+            // will not change.
+
+            e._key = key;
+            e._value = value;
+
+            return result;
+        }
+
+        // Not found, we're going to add it.
+
+        int newSize = _size + 1;
+
+        if (newSize == _entries.length)
+        {
+            // Time to expand!
+
+            int newCapacity = (_size * 3) / 2 + 1;
+
+            CIMEntry<V>[] newEntries = new CIMEntry[newCapacity];
+
+            System.arraycopy(_entries, 0, newEntries, 0, _cursor);
+
+            System.arraycopy(_entries, _cursor, newEntries, _cursor + 1, _size - _cursor);
+
+            _entries = newEntries;
+        }
+        else
+        {
+            // Open up a space for the new entry
+
+            System.arraycopy(_entries, _cursor, _entries, _cursor + 1, _size - _cursor);
+        }
+
+        CIMEntry<V> newEntry = new CIMEntry<V>(key, hashCode, value);
+        _entries[_cursor] = newEntry;
+
+        _size++;
+
+        // This is definately a structural change
+
+        _modCount++;
+
+        return null;
+    }
+
+    @Override
+    public boolean containsKey(Object key)
+    {
+        select(key);
+
+        return _found;
+    }
+
+    @Override
+    public V get(Object key)
+    {
+        select(key);
+
+        if (_found) return _entries[_cursor]._value;
+
+        return null;
+    }
+
+    @Override
+    public V remove(Object key)
+    {
+        select(key);
+
+        if (!_found) return null;
+
+        V result = _entries[_cursor]._value;
+
+        removeAtCursor();
+
+        return result;
+    }
+
+    private void removeAtCursor()
+    {
+        // Remove the entry by shifting everything else down.
+
+        System.arraycopy(_entries, _cursor + 1, _entries, _cursor, _size - _cursor - 1);
+
+        // We shifted down, leaving one (now duplicate) entry behind.
+
+        _entries[--_size] = null;
+
+        // A structural change for sure
+
+        _modCount++;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public Set<Map.Entry<String, V>> entrySet()
+    {
+        if (_entrySet == null) _entrySet = new EntrySet();
+
+        return _entrySet;
+    }
+
+    private void select(Object key)
+    {
+        if (key == null || key instanceof String)
+        {
+            String keyString = (String) key;
+            select(keyString, caseInsenitiveHashCode(keyString));
+        }
+        else
+        {
+            _found = false;
+        }
+    }
+
+    /**
+     * Searches the elements for the index of the indicated key and (case insensitive) hash code.
+     * Sets the _cursor and _found attributes.
+     */
+    private void select(String key, int hashCode)
+    {
+
+        int low = 0;
+        int high = _size - 1;
+
+        _cursor = 0;
+        _found = false;
+
+        if (_size == 0) return;
+
+        while (low <= high)
+        {
+            _cursor = (low + high) >> 1;
+
+            CIMEntry e = _entries[_cursor];
+
+            if (e._hashCode < hashCode)
+            {
+                low = _cursor + 1;
+                continue;
+            }
+
+            if (e._hashCode > hashCode)
+            {
+                high = _cursor - 1;
+                continue;
+            }
+
+            tuneCursor(key, hashCode);
+            return;
+        }
+
+        _cursor = low;
+    }
+
+    /**
+     * find() has located a matching hashCode, but there's an outlying possibility that multiple
+     * keys share the same hashCode. Backup the cursor until we get to locate the initial hashCode
+     * match, then march forward until the key is located, or the hashCode stops matching.
+     * 
+     * @param key
+     * @param hashCode
+     */
+    private void tuneCursor(String key, int hashCode)
+    {
+        while (_cursor > 0)
+        {
+            if (_entries[_cursor - 1]._hashCode != hashCode) break;
+
+            _cursor--;
+        }
+
+        while (true)
+        {
+            if (_entries[_cursor].matches(key))
+            {
+                _found = true;
+                return;
+            }
+
+            // Advance to the next entry.
+
+            _cursor++;
+
+            // If out of entries,
+            if (_cursor >= _size || _entries[_cursor]._hashCode != hashCode) return;
+        }
+    }
+
+    static int caseInsenitiveHashCode(String input)
+    {
+        if (input == null) return NULL_HASH;
+
+        int length = input.length();
+        int hash = 0;
+
+        // This should end up more or less equal to input.toLowerCase().hashCode(), unless String
+        // changes its implementation. Let's hope this is reasonably fast.
+
+        for (int i = 0; i < length; i++)
+        {
+            int ch = input.charAt(i);
+
+            int caselessCh = Character.toLowerCase(ch);
+
+            hash = 31 * hash + caselessCh;
+        }
+
+        return hash;
+    }
+
+}

Added: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/Stack.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/Stack.java?view=auto&rev=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/Stack.java (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/Stack.java Sat Feb 10 15:43:27 2007
@@ -0,0 +1,125 @@
+// Copyright 2007 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.ioc.util;
+
+import org.apache.tapestry.ioc.internal.util.CollectionFactory;
+
+/**
+ * A simple, streamlined implementation of {@link java.util.Stack}. The implementation is
+ * <em>not</em> threadsafe.
+ * 
+ * @param <E>
+ *            the type of elements stored in the map
+ * @see CollectionFactory#newStack()
+ */
+public class Stack<E>
+{
+    private static final int DEFAULT_ARRAY_SIZE = 20;
+
+    private Object[] _items;
+
+    private int _index = -1;
+
+    public Stack()
+    {
+        _items = new Object[DEFAULT_ARRAY_SIZE];
+    }
+
+    /** Returns true if the stack is empty. */
+    public boolean isEmpty()
+    {
+        return _index < 0;
+    }
+
+    /** Pushes a new item onto the stack. */
+    public void push(E item)
+    {
+        _index++;
+
+        if (_index == _items.length)
+        {
+            int newCapacity = (_items.length * 3) / 2 + 1;
+            Object[] newItems = new Object[newCapacity];
+            System.arraycopy(_items, 0, newItems, 0, _items.length);
+
+            _items = newItems;
+        }
+
+        _items[_index] = item;
+    }
+
+    /**
+     * Pops the top element off the stack and returns it.
+     * 
+     * @return the top element of the stack
+     * @throws IllegalStateException
+     *             if the stack is empty
+     */
+    @SuppressWarnings("unchecked")
+    public E pop()
+    {
+        checkIfEmpty();
+
+        Object result = _items[_index];
+
+        _items[_index] = null;
+
+        _index--;
+
+        return (E) result;
+    }
+
+    private void checkIfEmpty()
+    {
+        if (_index < 0) throw new IllegalStateException(UtilMessages.stackIsEmpty());
+    }
+
+    /**
+     * Returns the top element of the stack without affecting the stack.
+     * 
+     * @return top element on the stack
+     * @throws IllegalStateException
+     *             if the stack is empty
+     */
+    @SuppressWarnings("unchecked")
+    public E peek()
+    {
+        checkIfEmpty();
+
+        return (E) _items[_index];
+    }
+
+    /**
+     * Describes the stack, listing the element in order of depth (top element first).
+     * 
+     * @return string description of the stack
+     */
+    @Override
+    public String toString()
+    {
+        StringBuilder builder = new StringBuilder("Stack[");
+
+        for (int i = _index; i >= 0; i--)
+        {
+            if (i != _index) builder.append(", ");
+
+            builder.append(String.valueOf(_items[i]));
+        }
+
+        builder.append("]");
+
+        return builder.toString();
+    }
+}

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/UtilMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/UtilMessages.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/UtilMessages.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/java/org/apache/tapestry/ioc/util/UtilMessages.java Sat Feb 10 15:43:27 2007
@@ -36,4 +36,9 @@
                 adapterType.getName(),
                 catalog);
     }
+
+    static String stackIsEmpty()
+    {
+        return MESSAGES.get("stack-is-empty");
+    }
 }

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/main/resources/org/apache/tapestry/ioc/util/UtilStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/main/resources/org/apache/tapestry/ioc/util/UtilStrings.properties?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/main/resources/org/apache/tapestry/ioc/util/UtilStrings.properties (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/main/resources/org/apache/tapestry/ioc/util/UtilStrings.properties Sat Feb 10 15:43:27 2007
@@ -13,4 +13,4 @@
 # limitations under the License.
 
 no-strategy-adapter=No adapter from type %s to type %s is available (registered types are %s).
-
+stack-is-empty=Stack is empty.

Added: tapestry/tapestry5/tapestry-ioc/trunk/src/site/apt/case.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/site/apt/case.apt?view=auto&rev=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/site/apt/case.apt (added)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/site/apt/case.apt Sat Feb 10 15:43:27 2007
@@ -0,0 +1,27 @@
+ ----
+ Case Insensitivity
+ ----
+ 
+Case Insensitivity
+
+  Ever get frustrated because you typed the right thing with the wrong case and your system blew up?  We do.
+  
+  Tapestry IOC attempts to be case insensitive for all the main constructs:
+  
+  * Module ids.
+  
+  * Service ids.
+  
+  * Object provider prefixes.
+  
+  * Message keys.
+  
+  []
+  
+  Thus, <<<getService("foo.bar.Baz", Baz.class)>>> is preferred, but <<<getService("FOO.BAR.BAZ", Baz.class)>>> (or any variation thereof) will work just exactly as well.  This also extends to other naming conventions,
+  such as <<<contributeFoo>>> methods. It also applies to values inside annotations.
+  
+  Just case is ignored --
+  other punctuation, as well as whitespace, must exactly match.
+  
+  Under the covers, this is supported by the {{{../apidocs/org/apache/tapestry/ioc/CaseInsensitiveMap.html}CaseInsensitiveMap}} class.
\ No newline at end of file

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/site/apt/provider.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/site/apt/provider.apt?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/site/apt/provider.apt (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/site/apt/provider.apt Sat Feb 10 15:43:27 2007
@@ -28,8 +28,8 @@
   
 * infrastructure provider
 
-  The tapestry module provides the
-  {{{..tapestry-core/guide/infrastructure.html}infrastructure}} object provider, which exists
+  The tapestry module (provided by the {{{http://tapestry.apache.org/tapestry5/tapestry-core/}tapestry-core library}}) provides the
+  {{{http://tapestry.apache.org/tapestry5/tapestry-core/guide/infrastructure.html}infrastructure}} object provider, which exists
   to provide shorter names for injecting services and to support overrides.  
     
 * default provider  

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/site/site.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/site/site.xml?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/site/site.xml (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/site/site.xml Sat Feb 10 15:43:27 2007
@@ -54,6 +54,7 @@
             <item name="Services" href="service.html"/>
             <item name="Decorators" href="decorator.html"/>
             <item name="Configuration" href="configuration.html"/>
+            <item name="Case Insensitivity" href="case.html"/>
             <item name="Symbols" href="symbols.html"/>
             <item name="Starting the Registry" href="run.html"/>
             <item name="Object Providers" href="provider.html"/>

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java Sat Feb 10 15:43:27 2007
@@ -416,4 +416,14 @@
 
         assertEquals(holder.getValue(), 42);
     }
+
+    @Test
+    public void access_to_services_ignores_case()
+    {
+        Registry r = buildRegistry(FredModule.class);
+
+        Runnable fred = r.getService("fred.Fred", Runnable.class);
+
+        assertSame(r.getService("FRED.FRED", Runnable.class), fred);
+    }
 }

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/GlobPatternMatcherTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/GlobPatternMatcherTest.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/GlobPatternMatcherTest.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/GlobPatternMatcherTest.java Sat Feb 10 15:43:27 2007
@@ -33,6 +33,7 @@
     public void glob_match_exact()
     {
         assertTrue(globMatch("fred", "fred"));
+        assertTrue(globMatch("fred", "FRED"));
         assertFalse(globMatch("xfred", "fred"));
         assertFalse(globMatch("fredx", "fred"));
         assertFalse(globMatch("fred", "xfred"));
@@ -50,6 +51,7 @@
     public void glob_match_prefix()
     {
         assertTrue(globMatch("fred.Barney", "*Barney"));
+        assertTrue(globMatch("fred.Barney", "*BARNEY"));
         assertFalse(globMatch("fred.Barneyx", "*Barney"));
         assertFalse(globMatch("fred.Barney", "*Barneyx"));
         assertFalse(globMatch("fred.Barney", "*xBarney"));
@@ -59,6 +61,7 @@
     public void glob_match_suffix()
     {
         assertTrue(globMatch("fred.Barney", "fred*"));
+        assertTrue(globMatch("fred.Barney", "FRED*"));
         assertFalse(globMatch("xfred.Barney", "fred*"));
         assertFalse(globMatch("fred.Barney", "fredx*"));
         assertFalse(globMatch("fred.Barney", "xfred*"));
@@ -68,8 +71,10 @@
     public void glob_match_infix()
     {
         assertTrue(globMatch("fred.Barney", "*d.B*"));
+        assertTrue(globMatch("fred.Barney", "*D.B*"));
         assertTrue(globMatch("fred.Barney", "*Barney*"));
         assertTrue(globMatch("fred.Barney", "*fred*"));
+        assertTrue(globMatch("fred.Barney", "*FRED*"));
         assertFalse(globMatch("fred.Barney", "*flint*"));
     }
 

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/services/MasterObjectProviderTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/services/MasterObjectProviderTest.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/services/MasterObjectProviderTest.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/services/MasterObjectProviderTest.java Sat Feb 10 15:43:27 2007
@@ -55,6 +55,28 @@
     }
 
     @Test
+    public void successful_lookup_case_insensitive()
+    {
+        ObjectProvider provider = newObjectProvider();
+        ServiceLocator locator = newServiceLocator();
+        Runnable r = newRunnable();
+        SymbolSource source = newSymbolSource();
+
+        train_expandSymbols(source, "PREFIX:expression");
+        train_provide(provider, "expression", Runnable.class, locator, r);
+
+        replay();
+
+        ObjectProvider master = new MasterObjectProvider(newMap("prefix", provider), source, null);
+
+        Runnable actual = master.provide("PREFIX:expression", Runnable.class, locator);
+
+        assertSame(actual, r);
+
+        verify();
+    }
+
+    @Test
     public void symbols_are_expanded()
     {
         ObjectProvider provider = newObjectProvider();

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/services/PropertyAccessImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/services/PropertyAccessImplTest.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/services/PropertyAccessImplTest.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/services/PropertyAccessImplTest.java Sat Feb 10 15:43:27 2007
@@ -165,6 +165,18 @@
     }
 
     @Test
+    public void property_name_case_is_ignored_on_read()
+    {
+        Bean b = new Bean();
+
+        int value = _random.nextInt();
+
+        b.setValue(value);
+
+        assertEquals(_access.get(b, "VALUE"), value);     
+    }
+    
+    @Test
     public void simple_write_access()
     {
         Bean b = new Bean();
@@ -174,6 +186,18 @@
         _access.set(b, "value", value);
 
         assertEquals(b.getValue(), value);
+    }
+    
+    @Test
+    public void property_name_case_is_ignored_on_write()
+    {
+        Bean b = new Bean();
+
+        int value = _random.nextInt();
+
+        _access.set(b, "VALUE", value);
+
+        assertEquals(b.getValue(), value); 
     }
 
     @Test

Modified: tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/util/MessagesImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/util/MessagesImplTest.java?view=diff&rev=505796&r1=505795&r2=505796
==============================================================================
--- tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/util/MessagesImplTest.java (original)
+++ tapestry/tapestry5/tapestry-ioc/trunk/src/test/java/org/apache/tapestry/ioc/internal/util/MessagesImplTest.java Sat Feb 10 15:43:27 2007
@@ -31,11 +31,25 @@
     }
 
     @Test
+    public void contains_key_is_case_insensitive()
+    {
+        assertTrue(_messages.contains("No-Args"));
+        assertFalse(_messages.contains("Xyzzyx"));
+    }
+
+    @Test
     public void get_message_from_catalog()
     {
         assertEquals(_messages.get("no-args"), "No arguments.");
         assertEquals(_messages.get("something-failed"), "Something failed: %s");
     }
+    
+    @Test
+    public void get_message_from_catalog_is_case_insensitive()
+    {
+        assertEquals(_messages.get("No-args"), "No arguments.");
+        assertEquals(_messages.get("Something-Failed"), "Something failed: %s");        
+    }
 
     @Test
     public void get_unknown_message_from_catalog()
@@ -50,6 +64,12 @@
     }
 
     @Test
+    public void format_message_is_case_insensitive()
+    {
+        assertEquals(_messages.format("Result", "good"), "The result is 'good'.");
+    }
+    
+    @Test
     public void get_formatter()
     {
         MessageFormatter mf = _messages.getFormatter("result");
@@ -61,7 +81,8 @@
     public void formatters_are_cached()
     {
         MessageFormatter mf1 = _messages.getFormatter("result");
-        MessageFormatter mf2 = _messages.getFormatter("result");
+        // Throw in a case-insensitive check:
+        MessageFormatter mf2 = _messages.getFormatter("Result");
 
         assertSame(mf2, mf1);
     }