Clover coverage report - PicoContainer - 1.0-alpha-2
Coverage timestamp: Thu Jul 10 2003 10:40:43 BST
file stats: LOC: 154   Methods: 5
NCLOC: 108   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
DefaultAggregateProxyFactory.java 100% 100% 100% 100%
coverage
 1   
 package picocontainer.defaults;
 2   
 
 3   
 import java.util.List;
 4   
 import java.util.ArrayList;
 5   
 import java.util.Collections;
 6   
 import java.util.Set;
 7   
 import java.util.HashSet;
 8   
 import java.util.Arrays;
 9   
 import java.util.Iterator;
 10   
 import java.lang.reflect.Proxy;
 11   
 import java.lang.reflect.InvocationHandler;
 12   
 import java.lang.reflect.Method;
 13   
 import java.lang.reflect.InvocationTargetException;
 14   
 
 15   
 /**
 16   
  * 
 17   
  * @author Aslak Hellesøy
 18   
  * @version $Revision: 1.2 $
 19   
  */
 20   
 public class DefaultAggregateProxyFactory implements AggregateProxyFactory {
 21   
     private static Method equals;
 22   
     private static Method hashCode;
 23   
 
 24   
     static{
 25  8
         try {
 26  8
             equals = Object.class.getMethod("equals", new Class[] {Object.class});
 27  8
             hashCode = Object.class.getMethod("hashCode", null);
 28   
         } catch (NoSuchMethodException e) {
 29   
             ///CLOVER:OFF
 30   
             throw new InternalError();
 31   
             ///CLOVER:ON
 32   
         } catch (SecurityException e) {
 33   
             ///CLOVER:OFF
 34   
             throw new InternalError();
 35   
             ///CLOVER:ON
 36   
         }
 37   
     }
 38   
 
 39  32
     public Object createAggregateProxy(
 40   
             ClassLoader classLoader,
 41   
             List objectsToAggregateCallFor,
 42   
             boolean callInReverseOrder
 43   
             ) {
 44  32
         Class[] interfaces = getInterfaces(objectsToAggregateCallFor);
 45  32
         List copy = new ArrayList(objectsToAggregateCallFor);
 46   
 
 47  32
         if (!callInReverseOrder) {
 48   
             // reverse the list
 49  18
             Collections.reverse(copy);
 50   
         }
 51  32
         Object[] objects = copy.toArray();
 52   
 
 53  32
         Object result = Proxy.newProxyInstance(
 54   
                 classLoader,
 55   
                 interfaces,
 56   
                 new AggregatingInvocationHandler(classLoader, objects)
 57   
         );
 58   
 
 59  32
         return result;
 60   
     }
 61   
 
 62   
     private class AggregatingInvocationHandler implements InvocationHandler {
 63   
         private Object[] children;
 64   
         private ClassLoader classLoader;
 65   
 
 66  32
         public AggregatingInvocationHandler(ClassLoader classLoader, Object[] children) {
 67  32
             this.classLoader = classLoader;
 68  32
             this.children = children;
 69   
         }
 70   
 
 71  28
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 72  28
             Class declaringClass = method.getDeclaringClass();
 73  28
             if( declaringClass.equals(Object.class) ) {
 74  5
                 if(method.equals(hashCode)) {
 75   
                     // Return the hashCode of ourself, as Proxy.newProxyInstance() may
 76   
                     // return cached proxies. We want a unique hashCode for each created proxy!
 77  2
                     return new Integer(System.identityHashCode(AggregatingInvocationHandler.this));
 78   
                 }
 79  3
                 if(method.equals(equals)) {
 80  2
                     return new Boolean(proxy == args[0]);
 81   
                 }
 82   
                 // If the method is defined by Object (like hashCode or equals), call
 83   
                 // on ourself. This is a bit of a hack, but actually ok in most cases.
 84  1
                 return method.invoke(AggregatingInvocationHandler.this, args);
 85   
             } else {
 86  23
                 return invokeOnTargetsOfSameTypeAsDeclaringClass(declaringClass, children, method, args);
 87   
             }
 88   
         }
 89   
 
 90  23
         private Object invokeOnTargetsOfSameTypeAsDeclaringClass(Class declaringClass, Object[] targets, Method method, Object[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException {
 91  23
             Class returnType = method.getReturnType();
 92   
 
 93   
             // Lazily created list holding all results.
 94  23
             List results = null;
 95  23
             for (int i = 0; i < targets.length; i++) {
 96  69
                 boolean isValidType = declaringClass.isAssignableFrom(targets[i].getClass());
 97  69
                 if (isValidType) {
 98   
                     // It's ok to call the method on this one
 99  50
                     Object componentResult = method.invoke(targets[i], args);
 100  50
                     if (results == null) {
 101  23
                         results = new ArrayList();
 102   
                     }
 103  50
                     results.add(componentResult);
 104   
                 }
 105   
             }
 106   
 
 107  23
             Object result;
 108   
 
 109  23
             if (results.size() == 1) {
 110   
                 // Got exactly one result. Just return that.
 111  9
                 result = results.get(0);
 112  14
             } else if (returnType.isInterface()) {
 113   
                 // We have two or more results
 114   
                 // We can make a new proxy that aggregates all the results.
 115   
                 //Class[] resultInterfaces = getInterfaces(results.toArray());
 116  1
                 result = createAggregateProxy(
 117   
                         classLoader,
 118   
                         results,
 119   
                         true
 120   
                 );
 121   
             } else {
 122   
                 // Got multiple results that can't be wrapped in a proxy. Try to instantiate a default object.
 123  13
                 result = returnType.equals(Void.TYPE) ? null : returnType.newInstance();
 124   
             }
 125   
 
 126  22
             return result;
 127   
         }
 128   
     }
 129   
 
 130   
     /**
 131   
      * Get all the interfaces implemented by an array of objects.
 132   
      * @return an array of interfaces.
 133   
      */
 134  32
     private final Class[] getInterfaces(List objects) {
 135  32
         Set interfaces = new HashSet();
 136  32
         for (Iterator iterator = objects.iterator(); iterator.hasNext();) {
 137  81
             Object o = iterator.next();
 138  81
             Class componentClass = o.getClass();
 139   
             // Strangely enough Class.getInterfaces() does not include the interfaces
 140   
             // implemented by superclasses. So we must loop up the hierarchy.
 141  81
             while (componentClass != null) {
 142  178
                 Class[] implemeted = componentClass.getInterfaces();
 143  178
                 List implementedList = Arrays.asList(implemeted);
 144  178
                 interfaces.addAll(implementedList);
 145  178
                 componentClass = componentClass.getSuperclass();
 146   
             }
 147   
         }
 148   
 
 149  32
         Class[] result = (Class[]) interfaces.toArray(new Class[interfaces.size()]);
 150  32
         return result;
 151   
     }
 152   
 
 153   
 }
 154