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