001 // Copyright 2004, 2005 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.hivemind.service; 016 017 import java.lang.reflect.Method; 018 019 /** 020 * A representation of a {@link java.lang.reflect.Method}, identifying the name, return type, 021 * parameter types and exception types. Actual Method objects are tied to a particular class, and 022 * don't compare well with other otherwise identical Methods from other classes or interface; 023 * MethodSignatures are distinct from classes and compare well. 024 * <p> 025 * Because the intended purpose is to compare methods from interfaces (which are always public and 026 * abstract) we don't bother to actually track the modifiers. In addition, at this time, 027 * MethodSignature <em>does not distinguish between instance and static 028 * methods</em>. 029 * 030 * @author Howard Lewis Ship 031 */ 032 public class MethodSignature 033 { 034 private int _hashCode = -1; 035 036 private Class _returnType; 037 038 private String _name; 039 040 private Class[] _parameterTypes; 041 042 private Class[] _exceptionTypes; 043 044 public MethodSignature(Class returnType, String name, Class[] parameterTypes, 045 Class[] exceptionTypes) 046 { 047 _returnType = returnType; 048 _name = name; 049 _parameterTypes = parameterTypes; 050 _exceptionTypes = exceptionTypes; 051 } 052 053 public MethodSignature(Method m) 054 { 055 this(m.getReturnType(), m.getName(), m.getParameterTypes(), m.getExceptionTypes()); 056 } 057 058 /** 059 * Returns the exceptions for this method. Caution: do not modify the returned array. May return 060 * null. 061 */ 062 public Class[] getExceptionTypes() 063 { 064 return _exceptionTypes; 065 } 066 067 public String getName() 068 { 069 return _name; 070 } 071 072 /** 073 * Returns the parameter types for this method. May return null. Caution: do not modify the 074 * returned array. 075 */ 076 public Class[] getParameterTypes() 077 { 078 return _parameterTypes; 079 } 080 081 public Class getReturnType() 082 { 083 return _returnType; 084 } 085 086 public int hashCode() 087 { 088 if (_hashCode == -1) 089 { 090 091 _hashCode = _returnType.hashCode(); 092 093 _hashCode = 31 * _hashCode + _name.hashCode(); 094 095 int count = count(_parameterTypes); 096 097 for (int i = 0; i < count; i++) 098 _hashCode = 31 * _hashCode + _parameterTypes[i].hashCode(); 099 100 count = count(_exceptionTypes); 101 102 for (int i = 0; i < count; i++) 103 _hashCode = 31 * _hashCode + _exceptionTypes[i].hashCode(); 104 } 105 106 return _hashCode; 107 } 108 109 private static int count(Object[] array) 110 { 111 return array == null ? 0 : array.length; 112 } 113 114 /** 115 * Returns true if the other object is an instance of MethodSignature with identical values for 116 * return type, name, parameter types and exception types. 117 */ 118 public boolean equals(Object o) 119 { 120 if (o == null || !(o instanceof MethodSignature)) 121 return false; 122 123 MethodSignature ms = (MethodSignature) o; 124 125 if (_returnType != ms._returnType) 126 return false; 127 128 if (!_name.equals(ms._name)) 129 return false; 130 131 if (mismatch(_parameterTypes, ms._parameterTypes)) 132 return false; 133 134 return !mismatch(_exceptionTypes, ms._exceptionTypes); 135 } 136 137 private boolean mismatch(Class[] a1, Class[] a2) 138 { 139 int a1Count = count(a1); 140 int a2Count = count(a2); 141 142 if (a1Count != a2Count) 143 return true; 144 145 // Hm. What if order is important (for exceptions)? We're really saying here that they 146 // were derived from the name Method. 147 148 for (int i = 0; i < a1Count; i++) 149 { 150 if (a1[i] != a2[i]) 151 return true; 152 } 153 154 return false; 155 } 156 157 public String toString() 158 { 159 StringBuffer buffer = new StringBuffer(); 160 161 buffer.append(ClassFabUtils.getJavaClassName(_returnType)); 162 buffer.append(" "); 163 buffer.append(_name); 164 buffer.append("("); 165 166 for (int i = 0; i < count(_parameterTypes); i++) 167 { 168 if (i > 0) 169 buffer.append(", "); 170 171 buffer.append(ClassFabUtils.getJavaClassName(_parameterTypes[i])); 172 } 173 174 buffer.append(")"); 175 176 for (int i = 0; i < count(_exceptionTypes); i++) 177 { 178 if (i == 0) 179 buffer.append(" throws "); 180 else 181 buffer.append(", "); 182 183 buffer.append(_exceptionTypes[i].getName()); 184 } 185 186 return buffer.toString(); 187 } 188 189 /** 190 * Returns a string consisting of the name of the method and its parameter values. This is 191 * similar to {@link #toString()}, but omits the return type and information about thrown 192 * exceptions. A unique id is used by {@link MethodIterator} to identify overlapping methods 193 * (methods with the same name but different thrown exceptions). 194 * 195 * @since 1.1 196 */ 197 public String getUniqueId() 198 { 199 StringBuffer buffer = new StringBuffer(_name); 200 buffer.append("("); 201 202 for (int i = 0; i < count(_parameterTypes); i++) 203 { 204 if (i > 0) 205 buffer.append(","); 206 207 buffer.append(ClassFabUtils.getJavaClassName(_parameterTypes[i])); 208 } 209 210 buffer.append(")"); 211 212 return buffer.toString(); 213 } 214 215 /** 216 * Returns true if this signature has the same return type, name and parameters types as the 217 * method signature passed in, and this signatures exceptions "trump" (are the same as, or 218 * super-implementations of, all exceptions thrown by the other method signature). 219 * 220 * @since 1.1 221 */ 222 223 public boolean isOverridingSignatureOf(MethodSignature ms) 224 { 225 if (_returnType != ms._returnType) 226 return false; 227 228 if (!_name.equals(ms._name)) 229 return false; 230 231 if (mismatch(_parameterTypes, ms._parameterTypes)) 232 return false; 233 234 return exceptionsEncompass(ms._exceptionTypes); 235 } 236 237 /** 238 * The nuts and bolts of checking that another method signature's exceptions are a subset of 239 * this signature's. 240 * 241 * @since 1.1 242 */ 243 244 private boolean exceptionsEncompass(Class[] otherExceptions) 245 { 246 int ourCount = count(_exceptionTypes); 247 int otherCount = count(otherExceptions); 248 249 // If we have no exceptions, then ours encompass theirs only if they 250 // have no exceptions, either. 251 252 if (ourCount == 0) 253 return otherCount == 0; 254 255 boolean[] matched = new boolean[otherCount]; 256 int unmatched = otherCount; 257 258 for (int i = 0; i < ourCount && unmatched > 0; i++) 259 { 260 for (int j = 0; j < otherCount; j++) 261 { 262 // Ignore exceptions that have already been matched 263 264 if (matched[j]) 265 continue; 266 267 // When one of our exceptions is a super-class of one of their exceptions, 268 // then their exceptions is matched. 269 270 if (_exceptionTypes[i].isAssignableFrom(otherExceptions[j])) 271 { 272 matched[j] = true; 273 unmatched--; 274 } 275 } 276 } 277 278 return unmatched == 0; 279 } 280 }