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 }