    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    }