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