Clover coverage report - PicoContainer - 1.1
Coverage timestamp: Thu Nov 4 2004 11:55:45 CST
file stats: LOC: 221   Methods: 10
NCLOC: 150   Classes: 2
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
ConstructorInjectionComponentAdapter.java 100% 100% 100% 100%
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 org.picocontainer.defaults;
 12   
 
 13   
 import org.picocontainer.Parameter;
 14   
 import org.picocontainer.PicoContainer;
 15   
 import org.picocontainer.PicoInitializationException;
 16   
 import org.picocontainer.PicoIntrospectionException;
 17   
 
 18   
 import java.lang.reflect.Constructor;
 19   
 import java.lang.reflect.InvocationTargetException;
 20   
 import java.lang.reflect.Modifier;
 21   
 import java.util.ArrayList;
 22   
 import java.util.Arrays;
 23   
 import java.util.Collections;
 24   
 import java.util.Comparator;
 25   
 import java.util.HashSet;
 26   
 import java.util.List;
 27   
 import java.util.Set;
 28   
 
 29   
 /**
 30   
  * Instantiates components using Constructor Injection.
 31   
  * <em>
 32   
  * Note that this class doesn't cache instances. If you want caching,
 33   
  * use a {@link CachingComponentAdapter} around this one.
 34   
  * </em>
 35   
  *
 36   
  * @author Paul Hammant
 37   
  * @author Aslak Helles&oslash;y
 38   
  * @author Jon Tirs&eacute;n
 39   
  * @author Zohar Melamed
 40   
  * @author J&ouml;rg Schaible
 41   
  * @version $Revision: 1.29 $
 42   
  */
 43   
 public class ConstructorInjectionComponentAdapter extends InstantiatingComponentAdapter {
 44   
     private transient List sortedMatchingConstructors;
 45   
     private transient Guard instantiationGuard;
 46   
     
 47   
     private static abstract class Guard extends ThreadLocalCyclicDependencyGuard {
 48   
         protected PicoContainer guardedContainer;
 49  934
         private void setArguments(PicoContainer container) {
 50  934
             this.guardedContainer = container;
 51   
         }
 52   
     }
 53   
 
 54   
     /**
 55   
      * Explicitly specifies parameters. If parameters are null, default parameters
 56   
      * will be used.
 57   
      */
 58  1112
     public ConstructorInjectionComponentAdapter(final Object componentKey,
 59   
                                                 final Class componentImplementation,
 60   
                                                 Parameter[] parameters,
 61   
                                                 boolean allowNonPublicClasses) throws AssignabilityRegistrationException, NotConcreteRegistrationException {
 62  1112
         super(componentKey, componentImplementation, parameters, allowNonPublicClasses);
 63   
     }
 64   
 
 65  976
     public ConstructorInjectionComponentAdapter(Object componentKey, Class componentImplementation, Parameter[] parameters) {
 66  976
         this(componentKey, componentImplementation, parameters, false);
 67   
     }
 68   
 
 69   
     /**
 70   
      * Use default parameters.
 71   
      */
 72  114
     public ConstructorInjectionComponentAdapter(Object componentKey,
 73   
                                                 Class componentImplementation) throws AssignabilityRegistrationException, NotConcreteRegistrationException {
 74  114
         this(componentKey, componentImplementation, null);
 75   
     }
 76   
 
 77  972
     protected Constructor getGreediestSatisfiableConstructor(PicoContainer container) throws PicoIntrospectionException, UnsatisfiableDependenciesException, AmbiguousComponentResolutionException, AssignabilityRegistrationException, NotConcreteRegistrationException {
 78  972
         Constructor greediestConstructor = null;
 79  972
         final Set conflicts = new HashSet();
 80  972
         final Set unsatisfiableDependencyTypes = new HashSet();
 81  972
         if (sortedMatchingConstructors == null) {
 82  812
             sortedMatchingConstructors = getSortedMatchingConstructors();
 83   
         }
 84  972
         int lastSatisfiableConstructorSize = -1;
 85  972
         for (int i = 0; i < sortedMatchingConstructors.size(); i++) {
 86  1990
             boolean failedDependency = false;
 87  1990
             Constructor constructor = (Constructor) sortedMatchingConstructors.get(i);
 88  1990
             Class[] parameterTypes = constructor.getParameterTypes();
 89  1990
             Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes);
 90   
 
 91   
             // remember: all constructors with less arguments than the given parameters are filtered out already
 92  1990
             for (int j = 0; j < currentParameters.length; j++) {
 93   
                 // check wether this constructor is statisfiable
 94  1754
                 if (currentParameters[j].isResolvable(container, this, parameterTypes[j])) {
 95  664
                     continue;
 96   
                 }
 97  1074
                 unsatisfiableDependencyTypes.add(Arrays.asList(parameterTypes));
 98  1074
                 failedDependency = true;
 99  1074
                 break;
 100   
             }
 101   
             
 102  1974
             if (greediestConstructor != null && parameterTypes.length != lastSatisfiableConstructorSize) {
 103  16
                 if (conflicts.isEmpty()) {
 104   
                     // we found our match [aka. greedy and satisfied]
 105  14
                     return greediestConstructor;
 106   
                 } else {
 107   
                     // fits although not greedy
 108  2
                     conflicts.add(constructor);
 109   
                 }
 110  1958
             } else  if (!failedDependency && lastSatisfiableConstructorSize == parameterTypes.length) {
 111   
                 // satisfied and same size as previous one?
 112  2
                 conflicts.add(constructor);
 113  2
                 conflicts.add(greediestConstructor);
 114  1956
             } else if (!failedDependency) {
 115  882
                 greediestConstructor = constructor;
 116  882
                 lastSatisfiableConstructorSize = parameterTypes.length;
 117   
             }
 118   
         }
 119  942
         if (!conflicts.isEmpty()) {
 120  2
             throw new TooManySatisfiableConstructorsException(getComponentImplementation(), conflicts);
 121  940
         } else if (greediestConstructor == null && !unsatisfiableDependencyTypes.isEmpty()) {
 122  72
             throw new UnsatisfiableDependenciesException(this, unsatisfiableDependencyTypes);
 123  868
         } else if (greediestConstructor == null) {
 124   
             // be nice to the user, show all constructors that were filtered out 
 125  2
             final Set nonMatching = new HashSet();
 126  2
             final Constructor[] constructors = getComponentImplementation().getDeclaredConstructors();
 127  2
             for (int i = 0; i < constructors.length; i++) {
 128  2
                 nonMatching.add(constructors[i]);
 129   
             }
 130  2
             throw new PicoInitializationException("Either do the specified parameters not match any of the following constructors: " + nonMatching.toString() + " or the constructors were not accessible for '" + getComponentImplementation() + "'");
 131   
         }
 132  866
         return greediestConstructor;
 133   
     }
 134   
 
 135  934
     public Object getComponentInstance(PicoContainer container) throws PicoInitializationException, PicoIntrospectionException, AssignabilityRegistrationException, NotConcreteRegistrationException {
 136  934
         if (instantiationGuard == null) {
 137  766
             instantiationGuard = new Guard() {
 138  920
                 public Object run() {
 139  920
                     final Constructor constructor;
 140  920
                     try {
 141  920
                         constructor = getGreediestSatisfiableConstructor(guardedContainer);
 142   
                     } catch (AmbiguousComponentResolutionException e) {
 143  16
                         e.setComponent(getComponentImplementation());
 144  16
                         throw e;
 145   
                     }
 146  856
                     long startTime = -1;
 147  856
                     try {
 148  856
                         Object[] parameters = getConstructorArguments(guardedContainer, constructor);
 149   
 //                        if (PicoContainer.SHOULD_LOG) {
 150   
 //                            startTime = System.currentTimeMillis();
 151   
 //                            System.out.print("PICO: Invoking " + constructor.toString() + "... ");
 152   
 //                            System.out.flush();
 153   
 //                        }
 154  826
                         return newInstance(constructor, parameters);
 155   
                     } catch (InvocationTargetException e) {
 156  8
                         if (e.getTargetException() instanceof RuntimeException) {
 157  2
                             throw (RuntimeException) e.getTargetException();
 158  6
                         } else if (e.getTargetException() instanceof Error) {
 159  2
                             throw (Error) e.getTargetException();
 160   
                         }
 161  4
                         throw new PicoInvocationTargetInitializationException(e.getTargetException());
 162   
                     } catch (InstantiationException e) {
 163   
                         // can't get here because checkConcrete() will catch it earlier, but see PICO-191
 164   
                         ///CLOVER:OFF
 165   
                         throw new PicoInitializationException("Should never get here");
 166   
                         ///CLOVER:ON
 167   
                     } catch (IllegalAccessException e) {
 168   
                         // can't get here because either filtered or access mode set
 169   
                         ///CLOVER:OFF
 170   
                         throw new PicoInitializationException(e);
 171   
                         ///CLOVER:ON
 172   
                     } finally {
 173   
 //                        if (PicoContainer.SHOULD_LOG) {
 174   
 //                            if(startTime != -1) {
 175   
 //                                long endTime = System.currentTimeMillis();
 176   
 //                                System.out.println("[" + (endTime - startTime) + "ms]");
 177   
 //                            }
 178   
 //                        }
 179   
                     }
 180   
                 }
 181   
             };
 182   
         }
 183  934
         instantiationGuard.setArguments(container);
 184  934
         return instantiationGuard.observe(getComponentImplementation());
 185   
     }
 186   
 
 187  856
     protected Object[] getConstructorArguments(PicoContainer container, Constructor ctor) {
 188  856
         Class[] parameterTypes = ctor.getParameterTypes();
 189  856
         Object[] result = new Object[parameterTypes.length];
 190  856
         Parameter[] currentParameters = parameters != null ? parameters : createDefaultParameters(parameterTypes);
 191   
 
 192  856
         for (int i = 0; i < currentParameters.length; i++) {
 193  566
             result[i] = currentParameters[i].resolveInstance(container, this, parameterTypes[i]);
 194   
         }
 195  826
         return result;
 196   
     }
 197   
 
 198  812
     private List getSortedMatchingConstructors() {
 199  812
         List matchingConstructors = new ArrayList();
 200  812
         Constructor[] allConstructors = getComponentImplementation().getDeclaredConstructors();
 201   
         // filter out all constructors that will definately not match 
 202  812
         Constructor constructor;
 203  812
         for (int i = 0; i < allConstructors.length; i++) {
 204  1302
             constructor = allConstructors[i];
 205  1302
             if ((parameters == null || constructor.getParameterTypes().length == parameters.length)
 206   
                     && (allowNonPublicClasses || (constructor.getModifiers() & Modifier.PUBLIC) != 0)) {
 207  1202
                 matchingConstructors.add(constructor);
 208   
             }
 209   
         }
 210   
         // optimize list of constructors moving the longest at the beginning
 211  812
         if (parameters == null) {
 212  690
             Collections.sort(matchingConstructors, new Comparator() {
 213  706
                 public int compare(Object arg0, Object arg1) {
 214  706
                     return ((Constructor) arg1).getParameterTypes().length - ((Constructor) arg0).getParameterTypes().length;
 215   
                 }
 216   
             });
 217   
         }
 218  812
         return matchingConstructors;
 219   
     }
 220   
 }
 221