View Javadoc

1   /*
2    * Copyright 2007 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package net.sf.morph.util;
17  
18  import java.lang.reflect.Method;
19  import java.lang.reflect.Modifier;
20  import java.util.HashMap;
21  import java.util.Map;
22  
23  import net.sf.composite.util.DelegatingInvocationHandler;
24  import net.sf.composite.util.ObjectPair;
25  
26  /**
27   * DelegatingInvocationHandler that uses reflection to invoke non-public methods
28   * that match invocation signatures.
29   */
30  public class AnyAccessDelegatingInvocationHandler extends DelegatingInvocationHandler {
31  	private static class MethodDescription {
32  		private final String name;
33  		private final Class[] parameterTypes;
34  		private int hashCode;
35  		private MethodDescription(Method m) {
36  			this.name = m.getName();
37  			this.parameterTypes = m.getParameterTypes();
38  			hashCode = name.hashCode();
39  			for (int i = 0; i < parameterTypes.length; i++) {
40  				hashCode += parameterTypes[i].hashCode() * i;
41  			}
42  		}
43  
44  		public boolean equals(Object obj) {
45  			if (this == obj) {
46  				return true;
47  			}
48  			if (!(obj instanceof MethodDescription)) {
49  				return false;
50  			}
51  			return ((MethodDescription) obj).hashCode == this.hashCode;
52  		}
53  
54  		public int hashCode() {
55  			return hashCode;
56  		}
57  	}
58  
59  	private static final Map METHOD_MAP = new HashMap();
60  
61  	/**
62  	 * Construct a new AnyAccessDelegatingInvocationHandler.
63  	 * @param delegate
64  	 */
65  	public AnyAccessDelegatingInvocationHandler(Object delegate) {
66  		super(delegate);
67  	}
68  
69  	protected Method getDelegateMethod(Object delegate, Method method, Object[] args) throws Exception {
70  		try {
71  			return super.getDelegateMethod(delegate, method, args);
72  		} catch (Exception e) {
73  			MethodDescription methodDescription = new MethodDescription(method);
74  			ObjectPair key = new ObjectPair(delegate.getClass(), methodDescription);
75  			Method result;
76  			synchronized (METHOD_MAP) {
77  				//deal with possibility of no matching methods:
78  				if (METHOD_MAP.containsKey(key)) {
79  					result = (Method) METHOD_MAP.get(key);
80  				}
81  				result = getNonPublicMethod(delegate, method);
82  				METHOD_MAP.put(key, result);
83  			}
84  			if (result == null) {
85  				throw new NoSuchMethodException(method.toString());
86  			}
87  			if (!result.isAccessible()) {
88  				result.setAccessible(true);
89  			}
90  			return result;
91  		}
92  	}
93  
94  	private Method getNonPublicMethod(Object delegate, Method method) {
95  		for (Class current = delegate.getClass(); current != null; current = current.getSuperclass()) {
96  			Method[] m = current.getDeclaredMethods();
97  			for (int i = 0; i < m.length; i++) {
98  				if (!Modifier.isPublic(m[i].getModifiers())
99  						&& m[i].getName().equals(method.getName())
100 						&& equals(m[i].getParameterTypes(), method.getParameterTypes())) {
101 					return m[i];
102 				}
103 			}
104 		}
105 		return null;
106 	}
107 
108 	private static boolean equals(Class[] c1, Class[] c2) {
109 		if (c1.length != c2.length) {
110 			return false;
111 		}
112 		for (int i = 0; i < c1.length; i++) {
113 			if (c1[i] != c2[i]) {
114 				return false;
115 			}
116 		}
117 		return true;
118 	}
119 }