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
015package org.apache.tapestry5.services;
016
017import java.lang.reflect.Modifier;
018
019import org.apache.tapestry5.internal.InternalConstants;
020import 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 */
030public 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}