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    package org.apache.tapestry.enhance;
015    
016    import org.apache.hivemind.service.ClassFabUtils;
017    
018    import java.lang.reflect.Method;
019    
020    
021    /**
022     * JDK 1.4 based version of {@link MethodSignature}. 
023     */
024    public class MethodSignatureImpl implements MethodSignature
025    {
026        protected int _hashCode = -1;
027    
028        protected Class _returnType;
029    
030        protected String _name;
031    
032        protected Class[] _parameterTypes;
033    
034        protected Class[] _exceptionTypes;
035    
036        public MethodSignatureImpl(Class returnType, String name, 
037                Class[] parameterTypes, Class[] exceptionTypes)
038        {
039            _returnType = returnType;
040            _name = name;
041            _parameterTypes = parameterTypes;
042            _exceptionTypes = exceptionTypes;
043        }
044        
045        public MethodSignatureImpl(Method m)
046        {
047            this(m.getReturnType(), m.getName(), m.getParameterTypes(), m.getExceptionTypes());
048        }
049        
050        public Class[] getExceptionTypes()
051        {
052            return _exceptionTypes;
053        }
054    
055        public String getName()
056        {
057            return _name;
058        }
059        
060        public Class[] getParameterTypes()
061        {
062            return _parameterTypes;
063        }
064    
065        public Class getReturnType()
066        {
067            return _returnType;
068        }
069    
070        public int hashCode()
071        {
072            if (_hashCode == -1)
073            {
074                _hashCode = _returnType.hashCode();
075    
076                _hashCode = 31 * _hashCode + _name.hashCode();
077    
078                int count = count(_parameterTypes);
079    
080                for (int i = 0; i < count; i++)
081                    _hashCode = 31 * _hashCode + _parameterTypes[i].hashCode();
082    
083                count = count(_exceptionTypes);
084    
085                for (int i = 0; i < count; i++)
086                    _hashCode = 31 * _hashCode + _exceptionTypes[i].hashCode();
087            }
088    
089            return _hashCode;
090        }
091    
092        protected static int count(Object[] array)
093        {
094            return array == null ? 0 : array.length;
095        }
096    
097        /**
098         * Returns true if the other object is an instance of MethodSignature with identical values for
099         * return type, name, parameter types and exception types.
100         */
101        public boolean equals(Object o)
102        {
103            if (o == null || !(o instanceof MethodSignatureImpl))
104                return false;
105    
106            MethodSignatureImpl ms = (MethodSignatureImpl) o;
107    
108            if (_returnType != ms._returnType)
109                return false;
110            
111            if (!_name.equals(ms._name))
112                return false;
113    
114            if (mismatch(_parameterTypes, ms._parameterTypes))
115                return false;
116    
117            return !mismatch(_exceptionTypes, ms._exceptionTypes);
118        }
119    
120        protected boolean mismatch(Class[] a1, Class[] a2)
121        {
122            int a1Count = count(a1);
123            int a2Count = count(a2);
124    
125            if (a1Count != a2Count)
126                return true;
127    
128            // Hm. What if order is important (for exceptions)? We're really saying here that they
129            // were derived from the name Method.
130    
131            for (int i = 0; i < a1Count; i++)
132            {
133                if (!a1[i].isAssignableFrom(a2[i]))
134                    return true;
135            }
136    
137            return false;
138        }
139    
140        public String toString()
141        {
142            StringBuffer buffer = new StringBuffer();
143    
144            buffer.append(ClassFabUtils.getJavaClassName(_returnType));
145            buffer.append(" ");
146            buffer.append(_name);
147            buffer.append("(");
148    
149            for (int i = 0; i < count(_parameterTypes); i++)
150            {
151                if (i > 0)
152                    buffer.append(", ");
153    
154                buffer.append(ClassFabUtils.getJavaClassName(_parameterTypes[i]));
155            }
156    
157            buffer.append(")");
158    
159            for (int i = 0; i < count(_exceptionTypes); i++)
160            {
161                if (i == 0)
162                    buffer.append(" throws ");
163                else
164                    buffer.append(", ");
165    
166                buffer.append(_exceptionTypes[i].getName());
167            }
168    
169            return buffer.toString();
170        }
171    
172        public String getUniqueId()
173        {
174            StringBuffer buffer = new StringBuffer(_name);
175            buffer.append("(");
176    
177            for (int i = 0; i < count(_parameterTypes); i++)
178            {
179                if (i > 0)
180                    buffer.append(",");
181    
182                buffer.append(ClassFabUtils.getJavaClassName(_parameterTypes[i]));
183            }
184    
185            buffer.append(")");
186    
187            return buffer.toString();
188        }
189        
190        public boolean isGeneric()
191        {
192            return false;
193        }
194        
195        public boolean isOverridingSignatureOf(MethodSignature ms)
196        {
197            if (!(ms instanceof MethodSignatureImpl))
198                return false;
199            
200            MethodSignatureImpl sig = (MethodSignatureImpl)ms;
201    
202            if (!sig._returnType.isAssignableFrom(_returnType))
203                return false;
204    
205            if (!_name.equals(sig._name))
206                return false;
207    
208            if (mismatch(_parameterTypes, sig._parameterTypes))
209                return false;
210    
211            return exceptionsEncompass(sig._exceptionTypes);
212        }
213    
214        /**
215         * The nuts and bolts of checking that another method signature's exceptions are a subset of
216         * this signature's.
217         */
218    
219        protected boolean exceptionsEncompass(Class[] otherExceptions)
220        {
221            int ourCount = count(_exceptionTypes);
222            int otherCount = count(otherExceptions);
223    
224            // If we have no exceptions, then ours encompass theirs only if they
225            // have no exceptions, either.
226    
227            if (ourCount == 0)
228                return otherCount == 0;
229    
230            boolean[] matched = new boolean[otherCount];
231            int unmatched = otherCount;
232    
233            for (int i = 0; i < ourCount && unmatched > 0; i++)
234            {
235                for (int j = 0; j < otherCount; j++)
236                {
237                    // Ignore exceptions that have already been matched
238                    
239                    if (matched[j])
240                        continue;
241    
242                    // When one of our exceptions is a super-class of one of their exceptions,
243                    // then their exceptions is matched.
244                    
245                    if (_exceptionTypes[i].isAssignableFrom(otherExceptions[j]))
246                    {
247                        matched[j] = true;
248                        unmatched--;
249                    }
250                }
251            }
252    
253            return unmatched == 0;
254        }
255    }