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    }