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 }