    001    // Copyright 2006, 2007, 2010 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.services;
    016
    017    import java.lang.reflect.Modifier;
    018
    019    import org.apache.tapestry5.internal.InternalConstants;
    020    import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    021
    022    /**
    023     * A representation of a method signature, which consists of its name, modifiers (primarily,
    024     * visibility), return type,
    025     * parameter types, and declared exception types.
    026     * <p/>
    027     * Types are stored as class names (or primitive names) because the signature is used with {@link ClassTransformation}
    028     * (which operates on as-yet unloaded classes).
    029     */
    030    public class TransformMethodSignature implements Comparable<TransformMethodSignature>
    031    {
    032        private int hashCode = -1;
    033
    034        private final int modifiers;
    035
    036        private final String returnType, methodName, signature;
    037
    038        private final String[] parameterTypes, exceptionTypes;
    039
    040        /**
    041         * Convenience for adding a public void method with no parameters or exception types.
    042         */
    043
    044        public TransformMethodSignature(String name)
    045        {
    046            this(Modifier.PUBLIC, "void", name, InternalConstants.EMPTY_STRING_ARRAY, InternalConstants.EMPTY_STRING_ARRAY);
    047        }
    048
    049        public TransformMethodSignature(int modifiers, String type, String name, String[] parameterTypes,
    050                    String[] exceptionTypes)
    051        {
    052            this(modifiers, type, null, name, parameterTypes, exceptionTypes);
    053        }
    054
    055        /**
    056         * @since 5.3
    057         */
    058        public TransformMethodSignature(int modifiers, String type, String signature, String name, String[] parameterTypes,
    059                String[] exceptionTypes)
    060        {
    061            assert InternalUtils.isNonBlank(name);
    062            assert InternalUtils.isNonBlank(type);
    063
    064            this.modifiers = modifiers;
    065            this.signature = signature;
    066
    067            returnType = type;
    068            methodName = name;
    069
    070            // TODO: Checks that no element within the two arrays
    071            // is null or blank.
    072
    073            this.parameterTypes = typeNamesOrEmpty(parameterTypes);
    074            this.exceptionTypes = typeNamesOrEmpty(exceptionTypes);
    075        }
    076
    077        private String[] typeNamesOrEmpty(String[] types)
    078        {
    079            return types == null ? InternalConstants.EMPTY_STRING_ARRAY : types;
    080        }
    081
    082        /**
    083         * Returns a non-null array of the names of each declared exception type thrown by the method.
    084         * Calling code should
    085         * not modify the array.
    086         */
    087        public String[] getExceptionTypes()
    088        {
    089            return exceptionTypes;
    090        }
    091
    092        /**
    093         * Returns the name of the method.
    094         */
    095        public String getMethodName()
    096        {
    097            return methodName;
    098        }
    099
    100        /**
    101         * Returns the set of modifier flags for this method.
    102         *
    103         * @see java.lang.reflect.Modifier
    104         */
    105        public int getModifiers()
    106        {
    107            return modifiers;
    108        }
    109
    110        public String getSignature() {
    111            return signature;
    112        }
    113
    114        /**
    115         * Returns an array of the type name for each parameter. Calling code should not modify the
    116         * array.
    117         */
    118        public String[] getParameterTypes()
    119        {
    120            return parameterTypes;
    121        }
    122
    123        /**
    124         * Return the type name of the return type of the method.
    125         */
    126        public String getReturnType()
    127        {
    128            return returnType;
    129        }
    130
    131        @Override
    132        public int hashCode()
    133        {
    134            if (hashCode == -1)
    135            {
    136                hashCode = 17 * modifiers;
    137                hashCode += 31 * returnType.hashCode();
    138                hashCode += 31 * methodName.hashCode();
    139
    140                for (String parameterType : parameterTypes)
    141                {
    142                    hashCode += 31 * parameterType.hashCode();
    143                }
    144
    145                for (String exceptionType : exceptionTypes)
    146                {
    147                    hashCode += 31 * exceptionType.hashCode();
    148                }
    149            }
    150
    151            return hashCode;
    152        }
    153
    154        @Override
    155        public boolean equals(Object other)
    156        {
    157            if (other == null || !(other instanceof TransformMethodSignature))
    158                return false;
    159
    160            TransformMethodSignature ms = (TransformMethodSignature) other;
    161
    162            return modifiers == ms.modifiers && returnType.equals(ms.returnType) && methodName.equals(ms.methodName)
    163                    && matches(parameterTypes, ms.parameterTypes) && matches(exceptionTypes, ms.exceptionTypes);
    164        }
    165
    166        private boolean matches(String[] values, String[] otherValues)
    167        {
    168            if (values.length != otherValues.length)
    169                return false;
    170
    171            for (int i = 0; i < values.length; i++)
    172            {
    173                if (!values[i].equals(otherValues[i]))
    174                    return false;
    175            }
    176
    177            return true;
    178        }
    179
    180        /**
    181         * Returns the long form description of the signature. This includes modifiers, return type,
    182         * method name, parameters
    183         * and thrown exceptions, formatted approximately as it would appear in Java source (except that
    184         * parameter names,
    185         * which are not known, do no appear).
    186         */
    187        @Override
    188        public String toString()
    189        {
    190            StringBuilder builder = new StringBuilder();
    191
    192            // Package private is simply omitted.
    193
    194            if (modifiers != 0)
    195            {
    196                builder.append(Modifier.toString(modifiers));
    197                builder.append(' ');
    198            }
    199
    200            builder.append(returnType);
    201            builder.append(' ');
    202
    203            addMethodNameAndParameters(builder);
    204
    205            for (int i = 0; i < exceptionTypes.length; i++)
    206            {
    207                if (i == 0)
    208                    builder.append(" throws ");
    209                else
    210                    builder.append(", ");
    211
    212                builder.append(exceptionTypes[i]);
    213            }
    214
    215            return builder.toString();
    216        }
    217
    218        private void addMethodNameAndParameters(StringBuilder builder)
    219        {
    220            builder.append(methodName);
    221            builder.append('(');
    222
    223            for (int i = 0; i < parameterTypes.length; i++)
    224            {
    225                if (i > 0)
    226                    builder.append(", ");
    227
    228                builder.append(parameterTypes[i]);
    229            }
    230
    231            builder.append(')');
    232        }
    233
    234        /**
    235         * Sorting is primarily via method name. For methods with the same name, the second level of
    236         * sorting is by parameter
    237         * count (descending).
    238         */
    239        public int compareTo(TransformMethodSignature o)
    240        {
    241            int result = methodName.compareTo(o.methodName);
    242
    243            // Sort descending
    244            if (result == 0)
    245                result = o.parameterTypes.length - parameterTypes.length;
    246
    247            return result;
    248        }
    249
    250        /**
    251         * Returns a shortened form of the string representation of the method. It lists just the name
    252         * of the method and the
    253         * types of any parameters, omitting return type, exceptions and modifiers.
    254         *
    255         */
    256        public String getMediumDescription()
    257        {
    258            StringBuilder builder = new StringBuilder();
    259
    260            addMethodNameAndParameters(builder);
    261
    262            return builder.toString();
    263        }
    264
    265    }