|
|||||||||||||||||||
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 | |||||||||||||||
DefaultCompositeProxyFactory.java | 100% | 100% | 100% | 100% |
|
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 | 14 |
try {
|
40 | 14 |
equals = Object.class.getMethod("equals", new Class[]{Object.class}); |
41 | 14 |
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 | 57 |
public Object createCompositeProxy(
|
54 |
ClassLoader classLoader, |
|
55 |
List objectsToAggregateCallFor, |
|
56 |
boolean callInReverseOrder
|
|
57 |
) { |
|
58 | 57 |
Class[] interfaces = getInterfaces(objectsToAggregateCallFor); |
59 | 57 |
List copy = new ArrayList(objectsToAggregateCallFor);
|
60 |
|
|
61 | 57 |
if (!callInReverseOrder) {
|
62 |
// reverse the list
|
|
63 | 35 |
Collections.reverse(copy); |
64 |
} |
|
65 | 57 |
Object[] objects = copy.toArray(); |
66 |
|
|
67 | 57 |
Object result = Proxy.newProxyInstance( |
68 |
classLoader, |
|
69 |
interfaces, |
|
70 |
new AggregatingInvocationHandler(classLoader, objects)
|
|
71 |
); |
|
72 |
|
|
73 | 57 |
return result;
|
74 |
} |
|
75 |
|
|
76 |
private class AggregatingInvocationHandler implements InvocationHandler { |
|
77 |
private Object[] children;
|
|
78 |
private ClassLoader classLoader;
|
|
79 |
|
|
80 | 57 |
public AggregatingInvocationHandler(ClassLoader classLoader, Object[] children) {
|
81 | 57 |
this.classLoader = classLoader;
|
82 | 57 |
this.children = children;
|
83 |
} |
|
84 |
|
|
85 | 28 |
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
86 | 28 |
Class declaringClass = method.getDeclaringClass(); |
87 | 28 |
if (declaringClass.equals(Object.class)) { |
88 | 5 |
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 | 2 |
return new Integer(System.identityHashCode(AggregatingInvocationHandler.this)); |
92 |
} |
|
93 | 3 |
if (method.equals(equals)) {
|
94 | 2 |
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 | 1 |
return method.invoke(AggregatingInvocationHandler.this, args); |
99 |
} else {
|
|
100 | 23 |
return invokeOnTargetsOfSameTypeAsDeclaringClass(declaringClass, children, method, args);
|
101 |
} |
|
102 |
} |
|
103 |
|
|
104 | 23 |
private Object invokeOnTargetsOfSameTypeAsDeclaringClass(Class declaringClass, Object[] targets, Method method, Object[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException { |
105 | 23 |
Class returnType = method.getReturnType(); |
106 |
|
|
107 |
// Lazily created list holding all results.
|
|
108 | 23 |
List results = null;
|
109 | 23 |
for (int i = 0; i < targets.length; i++) { |
110 | 69 |
boolean isValidType = declaringClass.isAssignableFrom(targets[i].getClass());
|
111 | 69 |
if (isValidType) {
|
112 |
// It's ok to call the method on this one
|
|
113 | 50 |
Object componentResult = method.invoke(targets[i], args); |
114 | 50 |
if (results == null) { |
115 | 23 |
results = new ArrayList();
|
116 |
} |
|
117 | 50 |
results.add(componentResult); |
118 |
} |
|
119 |
} |
|
120 |
|
|
121 | 23 |
Object result; |
122 |
|
|
123 | 23 |
if (results.size() == 1) {
|
124 |
// Got exactly one result. Just return that.
|
|
125 | 9 |
result = results.get(0); |
126 | 14 |
} 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 | 1 |
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 | 13 |
result = returnType.equals(Void.TYPE) ? null : returnType.newInstance();
|
138 |
} |
|
139 |
|
|
140 | 22 |
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 | 57 |
private final Class[] getInterfaces(List objects) {
|
149 | 57 |
Set interfaces = new HashSet();
|
150 | 57 |
for (Iterator iterator = objects.iterator(); iterator.hasNext();) {
|
151 | 132 |
Object o = iterator.next(); |
152 | 132 |
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 | 132 |
while (componentClass != null) { |
156 | 280 |
Class[] implemeted = componentClass.getInterfaces(); |
157 | 280 |
List implementedList = Arrays.asList(implemeted); |
158 | 280 |
interfaces.addAll(implementedList); |
159 | 280 |
componentClass = componentClass.getSuperclass(); |
160 |
} |
|
161 |
} |
|
162 |
|
|
163 | 57 |
Class[] result = (Class[]) interfaces.toArray(new Class[interfaces.size()]);
|
164 | 57 |
return result;
|
165 |
} |
|
166 |
|
|
167 |
} |
|
168 |
|
|