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 }