001 // Copyright 2006, 2007, 2008, 2010, 2011 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry5.ioc.services;
016
017 import java.lang.reflect.Method;
018 import java.util.Arrays;
019
020 import org.apache.tapestry5.ioc.internal.util.InternalUtils;
021
022 /**
023 * A representation of a {@link java.lang.reflect.Method}, identifying the name, return type, parameter types and
024 * exception types. Actual Method objects are tied to a particular class, and don't compare well with other otherwise
025 * identical Methods from other classes or interface; MethodSignatures are distinct from classes and compare well.
026 * <p/>
027 * Because the intended purpose is to compare methods from interfaces (which are always public and abstract) we don't
028 * bother to actually track the modifiers. In addition, at this time, MethodSignature <em>does not distinguish between
029 * instance and static methods</em>.
030 * <p/>
031 * This version of MethodSignature works with <em>loaded</em> classes, and it usually used in the context of
032 * {@link org.apache.tapestry5.ioc.services.ClassFab} to create new classes and subclasses.
033 *
034 * @deprecated In 5.3, to be removed in a later release
035 */
036 @SuppressWarnings("all")
037 public class MethodSignature
038 {
039 private int hashCode = -1;
040
041 private final Class returnType;
042
043 private final String name;
044
045 private final Class[] parameterTypes;
046
047 private final Class[] exceptionTypes;
048
049 private final Method method;
050
051 public MethodSignature(Class returnType, String name, Class[] parameterTypes, Class[] exceptionTypes)
052 {
053 this(null, returnType, name, parameterTypes, exceptionTypes);
054 }
055
056 private MethodSignature(Method method, Class returnType, String name, Class[] parameterTypes, Class[] exceptionTypes)
057 {
058 this.method = method;
059 assert returnType != null;
060 this.returnType = returnType;
061 assert InternalUtils.isNonBlank(name);
062 this.name = name;
063
064 // Can be null!
065 this.parameterTypes = parameterTypes;
066 this.exceptionTypes = exceptionTypes;
067 }
068
069 public MethodSignature(Method m)
070 {
071 this(m, m.getReturnType(), m.getName(), m.getParameterTypes(), m.getExceptionTypes());
072 }
073
074 /**
075 * Returns the exceptions for this method. Caution: do not modify the returned array. May return null.
076 */
077 public Class[] getExceptionTypes()
078 {
079 return exceptionTypes;
080 }
081
082 public String getName()
083 {
084 return name;
085 }
086
087 /**
088 * If this signature was created from a method, return that method.
089 *
090 * @since 5.3
091 */
092 public Method getMethod()
093 {
094 return method;
095 }
096
097 /**
098 * Returns the parameter types for this method. May return null. Caution: do not modify the returned array.
099 */
100 public Class[] getParameterTypes()
101 {
102 return parameterTypes;
103 }
104
105 public Class getReturnType()
106 {
107 return returnType;
108 }
109
110 @Override
111 public int hashCode()
112 {
113 if (hashCode == -1)
114 {
115
116 hashCode = returnType.hashCode();
117
118 hashCode = 31 * hashCode + name.hashCode();
119
120 int count = InternalUtils.size(parameterTypes);
121
122 for (int i = 0; i < count; i++)
123 hashCode = 31 * hashCode + parameterTypes[i].hashCode();
124
125 count = InternalUtils.size(exceptionTypes);
126
127 for (int i = 0; i < count; i++)
128 hashCode = 31 * hashCode + exceptionTypes[i].hashCode();
129 }
130
131 return hashCode;
132 }
133
134 /**
135 * Returns true if the other object is an instance of MethodSignature with <em>identical</em> values for return
136 * type, name, parameter types and exception types.
137 *
138 * @see #isOverridingSignatureOf(MethodSignature)
139 */
140 @Override
141 public boolean equals(Object o)
142 {
143 if (o == null || !(o instanceof MethodSignature))
144 return false;
145
146 MethodSignature ms = (MethodSignature) o;
147
148 if (returnType != ms.returnType)
149 return false;
150
151 if (!name.equals(ms.name))
152 return false;
153
154 if (mismatch(parameterTypes, ms.parameterTypes))
155 return false;
156
157 return !mismatch(exceptionTypes, ms.exceptionTypes);
158 }
159
160 private boolean mismatch(Class[] a1, Class[] a2)
161 {
162 int a1Count = InternalUtils.size(a1);
163 int a2Count = InternalUtils.size(a2);
164
165 if (a1Count != a2Count)
166 return true;
167
168 // Hm. What if order is important (for exceptions)? We're really saying here that they
169 // were derived from the name Method.
170
171 for (int i = 0; i < a1Count; i++)
172 {
173 if (a1[i] != a2[i])
174 return true;
175 }
176
177 return false;
178 }
179
180 @Override
181 public String toString()
182 {
183 StringBuilder buffer = new StringBuilder();
184
185 buffer.append(ClassFabUtils.toJavaClassName(returnType));
186 buffer.append(" ");
187 buffer.append(name);
188 buffer.append("(");
189
190 for (int i = 0; i < InternalUtils.size(parameterTypes); i++)
191 {
192 if (i > 0)
193 buffer.append(", ");
194
195 buffer.append(ClassFabUtils.toJavaClassName(parameterTypes[i]));
196 }
197
198 buffer.append(")");
199
200 int _exceptionCount = InternalUtils.size(exceptionTypes);
201 String _exceptionNames[] = new String[_exceptionCount];
202 for (int i = 0; i < _exceptionCount; i++)
203 {
204 _exceptionNames[i] = exceptionTypes[i].getName();
205 }
206
207 Arrays.sort(_exceptionNames);
208
209 for (int i = 0; i < _exceptionCount; i++)
210 {
211 if (i == 0)
212 buffer.append(" throws ");
213 else
214 buffer.append(", ");
215
216 buffer.append(_exceptionNames[i]);
217 }
218
219 return buffer.toString();
220 }
221
222 /**
223 * Returns a string consisting of the name of the method and its parameter types. This is similar to
224 * {@link #toString()}, but omits the return type and information about thrown exceptions. A unique id is used by
225 * {@link MethodIterator} to identify overlapping methods (methods with the same name and parameter types but with
226 * different thrown exceptions).
227 *
228 * @see #isOverridingSignatureOf(MethodSignature)
229 */
230 public String getUniqueId()
231 {
232 StringBuilder buffer = new StringBuilder(name);
233 buffer.append("(");
234
235 for (int i = 0; i < InternalUtils.size(parameterTypes); i++)
236 {
237 if (i > 0)
238 buffer.append(",");
239
240 buffer.append(ClassFabUtils.toJavaClassName(parameterTypes[i]));
241 }
242
243 buffer.append(")");
244
245 return buffer.toString();
246 }
247
248 /**
249 * Returns true if this signature has the same return type, name and parameters types as the method signature passed
250 * in, and this signature's exceptions "trump" (are the same as, or super-implementations of, all exceptions thrown
251 * by the other method signature).
252 */
253
254 public boolean isOverridingSignatureOf(MethodSignature ms)
255 {
256 if (returnType != ms.returnType)
257 return false;
258
259 if (!name.equals(ms.name))
260 return false;
261
262 if (mismatch(parameterTypes, ms.parameterTypes))
263 return false;
264
265 return exceptionsEncompass(ms.exceptionTypes);
266 }
267
268 /**
269 * The nuts and bolts of checking that another method signature's exceptions are a subset of this signature's.
270 */
271
272 @SuppressWarnings("unchecked")
273 private boolean exceptionsEncompass(Class[] otherExceptions)
274 {
275 int ourCount = InternalUtils.size(exceptionTypes);
276 int otherCount = InternalUtils.size(otherExceptions);
277
278 // If we have no exceptions, then ours encompass theirs only if they
279 // have no exceptions, either.
280
281 if (ourCount == 0)
282 return otherCount == 0;
283
284 boolean[] matched = new boolean[otherCount];
285 int unmatched = otherCount;
286
287 for (int i = 0; i < ourCount && unmatched > 0; i++)
288 {
289 for (int j = 0; j < otherCount; j++)
290 {
291 // Ignore exceptions that have already been matched
292
293 if (matched[j])
294 continue;
295
296 // When one of our exceptions is a super-class of one of their exceptions,
297 // then their exceptions is matched.
298
299 if (exceptionTypes[i].isAssignableFrom(otherExceptions[j]))
300 {
301 matched[j] = true;
302 unmatched--;
303 }
304 }
305 }
306
307 return unmatched == 0;
308 }
309 }