Clover coverage report - PicoContainer - 1.0-alpha-1
Coverage timestamp: Sun Jun 29 2003 20:23:11 BST
file stats: LOC: 392   Methods: 30
NCLOC: 287   Classes: 4
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
DefaultPicoContainer.java 98.4% 99.3% 100% 99.1%
coverage coverage
 1   
 /*****************************************************************************
 2   
  * Copyright (C) PicoContainer Organization. All rights reserved.            *
 3   
  * ------------------------------------------------------------------------- *
 4   
  * The software in this package is published under the terms of the BSD      *
 5   
  * style license a copy of which has been included with this distribution in *
 6   
  * the LICENSE.txt file.                                                     *
 7   
  *                                                                           *
 8   
  * Idea by Rachel Davies, Original code by Aslak Hellesoy and Paul Hammant   *
 9   
  *****************************************************************************/
 10   
 
 11   
 package picocontainer.defaults;
 12   
 
 13   
 import picocontainer.hierarchical.DuplicateComponentTypeRegistrationException;
 14   
 import picocontainer.hierarchical.AssignabilityRegistrationException;
 15   
 import picocontainer.hierarchical.NotConcreteRegistrationException;
 16   
 import picocontainer.hierarchical.WrongNumberOfConstructorsRegistrationException;
 17   
 import picocontainer.hierarchical.AmbiguousComponentResolutionException;
 18   
 import picocontainer.hierarchical.UnsatisfiedDependencyStartupException;
 19   
 import picocontainer.PicoContainer;
 20   
 import picocontainer.ComponentFactory;
 21   
 import picocontainer.PicoRegistrationException;
 22   
 import picocontainer.PicoInitializationException;
 23   
 import picocontainer.PicoInvocationTargetInitailizationException;
 24   
 
 25   
 import java.util.Set;
 26   
 import java.util.HashSet;
 27   
 import java.util.List;
 28   
 import java.util.Arrays;
 29   
 import java.util.Map;
 30   
 import java.util.HashMap;
 31   
 import java.util.ArrayList;
 32   
 import java.util.Collections;
 33   
 import java.util.Iterator;
 34   
 import java.util.LinkedList;
 35   
 import java.lang.reflect.Method;
 36   
 import java.lang.reflect.Proxy;
 37   
 import java.lang.reflect.InvocationHandler;
 38   
 import java.lang.reflect.InvocationTargetException;
 39   
 import java.lang.reflect.Constructor;
 40   
 import java.lang.reflect.Modifier;
 41   
 
 42   
 /**
 43   
  * Abstract baseclass for various PicoContainer implementations.
 44   
  *
 45   
  * @author Aslak Hellesoy
 46   
  * @version $Revision: 1.8 $
 47   
  */
 48   
 public class DefaultPicoContainer implements PicoContainer {
 49   
 
 50   
     private final ComponentFactory componentFactory;
 51   
     private List registeredComponents = new ArrayList();
 52   
     private Map componentTypeToInstanceMap = new HashMap();
 53   
 
 54   
     // Keeps track of the instantiation order
 55   
     protected List orderedComponents = new ArrayList();
 56   
 
 57   
     // Keeps track of unmanaged components - components instantiated outside this container
 58   
     protected List unmanagedComponents = new ArrayList();
 59   
 
 60   
     private Map parametersForComponent = new HashMap();
 61   
     private boolean initialized;
 62   
 
 63   
     public static class Default extends DefaultPicoContainer {
 64  12
         public Default() {
 65  12
             super(new DefaultComponentFactory());
 66   
         }
 67   
     }
 68   
 
 69  66
     public DefaultPicoContainer(ComponentFactory componentFactory) {
 70  66
         if (componentFactory == null) {
 71  1
             throw new NullPointerException("componentFactory cannot be null");
 72   
         }
 73  65
         this.componentFactory = componentFactory;
 74   
     }
 75   
 
 76  24
     public final Object[] getComponents() {
 77  24
         Class[] componentTypes = getComponentTypes();
 78  24
         Object[] components = new Object[componentTypes.length];
 79  24
         for (int i = 0; i < componentTypes.length; i++) {
 80  60
             Class componentType = componentTypes[i];
 81  60
             components[i] = getComponent(componentType);
 82   
         }
 83  24
         return components;
 84   
     }
 85   
 
 86   
     /**
 87   
      * Shorthand for {@link #getAggregateComponentProxy(boolean, boolean)}(true, true).
 88   
      * @return a proxy.
 89   
      */
 90  4
     public Object getMultipleInheritanceProxy() {
 91  4
         return getAggregateComponentProxy( true, true);
 92   
     }
 93   
 
 94   
     /**
 95   
      * Returns a proxy that implements the union of all the components'
 96   
      * interfaces.
 97   
      * Calling a method on the returned Object will call the
 98   
      * method on all components in the container that implement
 99   
      * that interface.
 100   
      *
 101   
      * @param callInInstantiationOrder whether to call the methods in the order of instantiation (true) or reverse (false)
 102   
      * @param callInInstantiationOrder whether to exclude components registered with {@link #registerComponent(Class, Object)}
 103   
      * or {@link #registerComponent(Object)}
 104   
      */
 105  19
     public Object getAggregateComponentProxy(boolean callInInstantiationOrder, boolean callUnmanagedComponents) {
 106  19
         return Proxy.newProxyInstance(
 107   
                 getClass().getClassLoader(),
 108   
                 getComponentInterfaces(),
 109   
                 new ComponentsInvocationHandler(callInInstantiationOrder, callUnmanagedComponents));
 110   
     }
 111   
 
 112   
     private class ComponentsInvocationHandler implements InvocationHandler {
 113   
         private boolean callInInstantiationOrder;
 114   
         private boolean callUnmanagedComponents;
 115   
 
 116  19
         public ComponentsInvocationHandler(boolean callInInstantiationOrder, boolean callUnmanagedComponents) {
 117  19
             this.callInInstantiationOrder = callInInstantiationOrder;
 118  19
             this.callUnmanagedComponents = callUnmanagedComponents;
 119   
         }
 120   
 
 121  16
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 122   
 
 123  16
             List orderedComponentsCopy = new ArrayList(orderedComponents);
 124   
 
 125  16
             if( !callInInstantiationOrder ) {
 126   
                 // reverse the list
 127  6
                 Collections.reverse(orderedComponentsCopy);
 128   
             }
 129  16
             Object[] components = orderedComponentsCopy.toArray();
 130  16
             return invokeOnComponents(components, method, args);
 131   
         }
 132   
 
 133  16
         private Object invokeOnComponents(Object[] components, Method method, Object[] args) throws IllegalAccessException, InvocationTargetException {
 134  16
             Object result = null;
 135  16
             int invokeCount = 0;
 136  16
             for (int i = 0; i < components.length; i++) {
 137  50
                 Class declarer = method.getDeclaringClass();
 138  50
                 boolean isValidType = declarer.isAssignableFrom(components[i].getClass());
 139  50
                 boolean isUnmanaged = unmanagedComponents.contains(components[i]);
 140  50
                 boolean exclude = !callUnmanagedComponents && isUnmanaged;
 141  50
                 if (isValidType && !exclude) {
 142   
                     // It's ok to call the method on this one
 143  31
                     Object resultCandidate = method.invoke(components[i], args);
 144  31
                     invokeCount++;
 145  31
                     if( invokeCount == 1 ) {
 146  16
                         result = resultCandidate;
 147   
                     } else {
 148  15
                         result = null;
 149   
                     }
 150   
                 }
 151   
             }
 152  16
             return result;
 153   
         }
 154   
     }
 155   
 
 156   
     /**
 157   
      * Get all the interfaces implemented by the registered component instances.
 158   
      * @return an array of interfaces implemented by the concrete component instances.
 159   
      */
 160  19
     private final Class[] getComponentInterfaces() {
 161  19
         Set interfaces = new HashSet();
 162  19
         Object[] components = getComponents();
 163  19
         for (int i = 0; i < components.length; i++) {
 164  52
             Class componentClass = components[i].getClass();
 165   
             // Strangely enough Class.getInterfaces() does not include the interfaces
 166   
             // implemented by superclasses. So we must loop up the hierarchy.
 167  52
             while( componentClass != null ) {
 168  113
                 Class[] implemeted = componentClass.getInterfaces();
 169  113
                 List implementedList = Arrays.asList(implemeted);
 170  113
                 interfaces.addAll(implementedList);
 171  113
                 componentClass = componentClass.getSuperclass();
 172   
             }
 173   
         }
 174   
 
 175  19
         Class[] result = (Class[]) interfaces.toArray(new Class[interfaces.size()]);
 176  19
         return result;
 177   
     }
 178   
 
 179  73
     public void registerComponent(Class componentType, Class componentImplementation) throws DuplicateComponentTypeRegistrationException, AssignabilityRegistrationException, NotConcreteRegistrationException, WrongNumberOfConstructorsRegistrationException {
 180  73
         checkConcrete(componentImplementation);
 181  72
         checkConstructor(componentImplementation);
 182  71
         checkTypeCompatibility(componentType, componentImplementation);
 183  70
         checkTypeDuplication(componentType);
 184  69
         registeredComponents.add(new ComponentSpecification(componentType, componentImplementation));
 185   
     }
 186   
 
 187  72
     private void checkConstructor(Class componentImplementation) throws WrongNumberOfConstructorsRegistrationException {
 188   
         // TODO move this check to checkConstructor and rename the exception to
 189   
         // WrongNumberOfConstructorsRegistrationException extends PicoRegistrationException
 190  72
         Constructor[] constructors = componentImplementation.getConstructors();
 191  72
         if (constructors.length != 1) {
 192  1
             throw new WrongNumberOfConstructorsRegistrationException(constructors.length);
 193   
         }
 194   
     }
 195   
 
 196  77
     private void checkTypeDuplication(Class componentType) throws DuplicateComponentTypeRegistrationException {
 197  77
         for (Iterator iterator = registeredComponents.iterator(); iterator.hasNext();) {
 198  39
             Class aClass = ((ComponentSpecification) iterator.next()).getComponentType();
 199  39
             if (aClass == componentType) {
 200  2
                 throw new DuplicateComponentTypeRegistrationException(aClass);
 201   
             }
 202   
         }
 203   
     }
 204   
 
 205  79
     private void checkTypeCompatibility(Class componentType, Class componentImplementation) throws AssignabilityRegistrationException {
 206  79
         if (!componentType.isAssignableFrom(componentImplementation)) {
 207  2
             throw new AssignabilityRegistrationException(componentType, componentImplementation);
 208   
         }
 209   
     }
 210   
 
 211  73
     private void checkConcrete(Class componentImplementation) throws NotConcreteRegistrationException {
 212   
         // Assert that the component class is concrete.
 213  73
         boolean isAbstract = (componentImplementation.getModifiers() & Modifier.ABSTRACT) == Modifier.ABSTRACT;
 214  73
         if (componentImplementation.isInterface() || isAbstract) {
 215  1
             throw new NotConcreteRegistrationException(componentImplementation);
 216   
         }
 217   
     }
 218   
 
 219  1
     public void registerComponent(Object component) throws PicoRegistrationException {
 220  1
         registerComponent(component.getClass(), component);
 221   
     }
 222   
 
 223  8
     public void registerComponent(Class componentType, Object component) throws PicoRegistrationException {
 224  8
         checkTypeCompatibility(componentType, component.getClass());
 225  7
         checkTypeDuplication(componentType);
 226   
         //checkImplementationDuplication(component.getClass());
 227  6
         componentTypeToInstanceMap.put(componentType, component);
 228  6
         orderedComponents.add(component);
 229  6
         unmanagedComponents.add(component);
 230   
     }
 231   
 
 232  7
     public void addParameterToComponent(Class componentType, Class parameter, Object arg) {
 233  7
         if (!parametersForComponent.containsKey(componentType)) {
 234  4
             parametersForComponent.put(componentType, new ArrayList());
 235   
         }
 236  7
         List args = (List) parametersForComponent.get(componentType);
 237  7
         args.add(new ParameterSpec(arg));
 238   
     }
 239   
 
 240  60
     public void registerComponent(Class componentImplementation) throws DuplicateComponentTypeRegistrationException, AssignabilityRegistrationException, NotConcreteRegistrationException, WrongNumberOfConstructorsRegistrationException {
 241  60
         registerComponent(componentImplementation, componentImplementation);
 242   
     }
 243   
 
 244   
     private class ParameterSpec {
 245   
         private Object arg;
 246   
 
 247  7
         ParameterSpec(Object parameter) {
 248  7
             this.arg = parameter;
 249   
         }
 250   
     }
 251   
 
 252  30
     public void instantiateComponents() throws PicoInitializationException {
 253  30
         if (initialized == false) {
 254  30
             initializeComponents();
 255  28
             checkUnsatisfiedDependencies();
 256  26
             initialized = true;
 257   
         } else {
 258  0
             throw new IllegalStateException("PicoContainer Started Already");
 259   
         }
 260   
     }
 261   
 
 262   
     // This is Lazy and NOT public :-)
 263  30
     private void initializeComponents() throws AmbiguousComponentResolutionException, PicoInvocationTargetInitailizationException {
 264  30
         boolean progress = true;
 265  30
         while (progress == true) {
 266  69
             progress = false;
 267   
 
 268  69
             for (Iterator iterator = registeredComponents.iterator(); iterator.hasNext();) {
 269  141
                 ComponentSpecification componentSpec = (ComponentSpecification) iterator.next();
 270  141
                 Class componentImplementation = componentSpec.getComponentImplementation();
 271  141
                 Class componentType = componentSpec.getComponentType();
 272   
 
 273  141
                 if (componentTypeToInstanceMap.get(componentType) == null) {
 274  74
                     boolean reused = reuseImplementationIfAppropriate(componentType, componentImplementation);
 275  74
                     if (reused) {
 276  1
                         progress = true;
 277   
                     } else {
 278   
                         // hook'em up
 279  73
                         progress = hookEmUp(componentImplementation, componentType, progress);
 280   
                     }
 281   
                 }
 282   
             }
 283   
         }
 284   
     }
 285   
 
 286  73
     protected boolean hookEmUp(Class componentImplementation, Class componentType, boolean progress) throws AmbiguousComponentResolutionException, PicoInvocationTargetInitailizationException {
 287  73
             Constructor[] constructors = componentImplementation.getConstructors();
 288  73
             Constructor constructor = constructors[0];
 289  73
             Class[] parameters = constructor.getParameterTypes();
 290   
 
 291  73
             List paramSpecs = (List) parametersForComponent.get(componentImplementation);
 292  73
             paramSpecs = paramSpecs == null ? Collections.EMPTY_LIST : new LinkedList(paramSpecs); // clone because we are going to modify it
 293   
 
 294   
             // For each param, look up the instantiated componentImplementation.
 295  73
             Object[] args = new Object[parameters.length];
 296  73
             for (int i = 0; i < parameters.length; i++) {
 297  69
                 Class param = parameters[i];
 298  69
                 args[i] = getComponentForParam(param); // lookup a service for this param
 299  68
                 if (args[i] == null && !paramSpecs.isEmpty()) { // failing that, check if any params are available from addParameterToComponent()
 300  10
                     args[i] = ((ParameterSpec) paramSpecs.remove(0)).arg;
 301   
                 }
 302   
             }
 303  72
             if (hasAnyNullArguments(args) == false) {
 304  53
                 Object componentInstance = null;
 305  53
                 componentInstance = makeComponentInstance(componentType, constructor, args);
 306   
                 // Put the instantiated comp back in the map
 307  52
                 componentTypeToInstanceMap.put(componentType, componentInstance);
 308  52
                 orderedComponents.add(componentInstance);
 309  52
                 progress = true;
 310   
             }
 311   
 
 312  71
         return progress;
 313   
     }
 314   
 
 315  74
     protected boolean reuseImplementationIfAppropriate(Class componentType, Class componentImplementation) {
 316  74
         Set compEntries = componentTypeToInstanceMap.entrySet();
 317  74
         for (Iterator iterator = compEntries.iterator();
 318  122
              iterator.hasNext();) {
 319  49
             Map.Entry entry = (Map.Entry) iterator.next();
 320  49
             Object exisitingCompClass = entry.getValue();
 321  49
             if (exisitingCompClass.getClass() == componentImplementation) {
 322  1
                 componentTypeToInstanceMap.put(componentType, exisitingCompClass);
 323  1
                 return true;
 324   
             }
 325   
         }
 326  73
         return false;
 327   
     }
 328   
 
 329  28
     private void checkUnsatisfiedDependencies() throws UnsatisfiedDependencyStartupException {
 330  28
         for (Iterator iterator = registeredComponents.iterator(); iterator.hasNext();) {
 331  53
             ComponentSpecification componentSpecification = (ComponentSpecification) iterator.next();
 332  53
             Class componentType = componentSpecification.getComponentType();
 333  53
             if (componentTypeToInstanceMap.get(componentType) == null) {
 334  2
                 throw new UnsatisfiedDependencyStartupException(componentType);
 335   
             }
 336   
         }
 337   
     }
 338   
 
 339  52
     protected Object makeComponentInstance(Class type, Constructor constructor, Object[] args) throws PicoInvocationTargetInitailizationException {
 340  52
         return componentFactory.createComponent(type, constructor, args);
 341   
     }
 342   
 
 343  68
     protected Object getComponentForParam(Class parameter) throws AmbiguousComponentResolutionException {
 344  68
         Object result = null;
 345   
 
 346   
         // We're keeping track of all candidate parameters, so we can bomb with a detailed error message
 347   
         // if there is ambiguity
 348  68
         List candidateClasses = new ArrayList();
 349   
 
 350  68
         for (Iterator iterator = componentTypeToInstanceMap.entrySet().iterator(); iterator.hasNext();) {
 351  71
             Map.Entry entry = (Map.Entry) iterator.next();
 352  71
             Class clazz = (Class) entry.getKey();
 353  71
             if (parameter.isAssignableFrom(clazz)) {
 354  37
                 candidateClasses.add(clazz);
 355  37
                 result = entry.getValue();
 356   
             }
 357   
         }
 358   
 
 359   
         // We should only have one here.
 360  68
         if (candidateClasses.size() > 1) {
 361  1
             Class[] ambiguities = (Class[]) candidateClasses.toArray(new Class[candidateClasses.size()]);
 362  1
             throw new AmbiguousComponentResolutionException(ambiguities);
 363   
         }
 364   
 
 365  67
         return result;
 366   
     }
 367   
 
 368  72
     private boolean hasAnyNullArguments(Object[] args) {
 369  72
         for (int i = 0; i < args.length; i++) {
 370  64
             Object arg = args[i];
 371  64
             if (arg == null) {
 372  19
                 return true;
 373   
             }
 374   
         }
 375  53
         return false;
 376   
     }
 377   
 
 378  91
     public Object getComponent(Class componentType) {
 379  91
         return componentTypeToInstanceMap.get(componentType);
 380   
     }
 381   
 
 382  23
     public Class[] getComponentTypes() {
 383   
         // Get my own
 384  23
         Set types = componentTypeToInstanceMap.keySet();
 385  23
         return (Class[]) types.toArray(new Class[types.size()]);
 386   
     }
 387   
 
 388  13
     public boolean hasComponent(Class componentType) {
 389  13
         return getComponent(componentType) != null;
 390   
     }
 391   
 }
 392