1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.proxy2.impl;
19
20 import java.io.Serializable;
21 import java.lang.reflect.Array;
22 import java.lang.reflect.Method;
23 import java.text.ParsePosition;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29
30 import org.apache.commons.lang3.ArrayUtils;
31 import org.apache.commons.lang3.StringUtils;
32 import org.apache.commons.lang3.Validate;
33 import org.apache.commons.lang3.builder.HashCodeBuilder;
34 import org.apache.commons.lang3.reflect.MethodUtils;
35 import org.apache.commons.lang3.tuple.Pair;
36
37
38
39
40
41
42 public class MethodSignature implements Serializable
43 {
44 private static final long serialVersionUID = 1L;
45
46 private static final Map<Class<?>, Character> PRIMITIVE_ABBREVIATIONS;
47 private static final Map<Character, Class<?>> REVERSE_ABBREVIATIONS;
48 static
49 {
50 final Map<Class<?>, Character> primitiveAbbreviations = new HashMap<Class<?>, Character>();
51 primitiveAbbreviations.put(Boolean.TYPE, Character.valueOf('Z'));
52 primitiveAbbreviations.put(Byte.TYPE, Character.valueOf('B'));
53 primitiveAbbreviations.put(Short.TYPE, Character.valueOf('S'));
54 primitiveAbbreviations.put(Integer.TYPE, Character.valueOf('I'));
55 primitiveAbbreviations.put(Character.TYPE, Character.valueOf('C'));
56 primitiveAbbreviations.put(Long.TYPE, Character.valueOf('J'));
57 primitiveAbbreviations.put(Float.TYPE, Character.valueOf('F'));
58 primitiveAbbreviations.put(Double.TYPE, Character.valueOf('D'));
59 primitiveAbbreviations.put(Void.TYPE, Character.valueOf('V'));
60 final Map<Character, Class<?>> reverseAbbreviations = new HashMap<Character, Class<?>>();
61 for (Map.Entry<Class<?>, Character> e : primitiveAbbreviations.entrySet())
62 {
63 reverseAbbreviations.put(e.getValue(), e.getKey());
64 }
65 PRIMITIVE_ABBREVIATIONS = Collections.unmodifiableMap(primitiveAbbreviations);
66 REVERSE_ABBREVIATIONS = Collections.unmodifiableMap(reverseAbbreviations);
67 }
68
69 private static void appendTo(StringBuilder buf, Class<?> type)
70 {
71 if (type.isPrimitive())
72 {
73 buf.append(PRIMITIVE_ABBREVIATIONS.get(type));
74 }
75 else if (type.isArray())
76 {
77 buf.append('[');
78 appendTo(buf, type.getComponentType());
79 }
80 else
81 {
82 buf.append('L').append(type.getName().replace('.', '/')).append(';');
83 }
84 }
85
86 private static class SignaturePosition extends ParsePosition
87 {
88 SignaturePosition()
89 {
90 super(0);
91 }
92
93 SignaturePosition next()
94 {
95 return plus(1);
96 }
97
98 SignaturePosition plus(int addend)
99 {
100 setIndex(getIndex() + addend);
101 return this;
102 }
103 }
104
105 private static Pair<String, Class<?>[]> parse(String internal)
106 {
107 Validate.notBlank(internal, "Cannot parse blank method signature");
108 final SignaturePosition pos = new SignaturePosition();
109 int lparen = internal.indexOf('(', pos.getIndex());
110 Validate.isTrue(lparen > 0, "Method signature \"%s\" requires parentheses", internal);
111 final String name = internal.substring(0, lparen).trim();
112 Validate.notBlank(name, "Method signature \"%s\" has blank name", internal);
113
114 pos.setIndex(lparen + 1);
115
116 boolean complete = false;
117 final List<Class<?>> params = new ArrayList<Class<?>>();
118 while (pos.getIndex() < internal.length())
119 {
120 final char c = internal.charAt(pos.getIndex());
121 if (Character.isWhitespace(c))
122 {
123 pos.next();
124 continue;
125 }
126 final Character k = Character.valueOf(c);
127 if (REVERSE_ABBREVIATIONS.containsKey(k))
128 {
129 params.add(REVERSE_ABBREVIATIONS.get(k));
130 pos.next();
131 continue;
132 }
133 if (')' == c)
134 {
135 complete = true;
136 pos.next();
137 break;
138 }
139 try
140 {
141 params.add(parseType(internal, pos));
142 }
143 catch (ClassNotFoundException e)
144 {
145 throw new IllegalArgumentException(String.format("Method signature \"%s\" references unknown type",
146 internal), e);
147 }
148 }
149 Validate.isTrue(complete, "Method signature \"%s\" is incomplete", internal);
150 Validate.isTrue(StringUtils.isBlank(internal.substring(pos.getIndex())),
151 "Method signature \"%s\" includes unrecognized content beyond end", internal);
152
153 return Pair.of(name, params.toArray(ArrayUtils.EMPTY_CLASS_ARRAY));
154 }
155
156 private static Class<?> parseType(String internal, SignaturePosition pos) throws ClassNotFoundException
157 {
158 final int here = pos.getIndex();
159 final char c = internal.charAt(here);
160
161 switch (c)
162 {
163 case '[':
164 pos.next();
165 final Class<?> componentType = parseType(internal, pos);
166 return Array.newInstance(componentType, 0).getClass();
167 case 'L':
168 pos.next();
169 final int type = pos.getIndex();
170 final int semi = internal.indexOf(';', type);
171 Validate.isTrue(semi > 0, "Type at index %d of method signature \"%s\" not terminated by semicolon",
172 Integer.valueOf(here), internal);
173 final String className = internal.substring(type, semi).replace('/', '.');
174 Validate.notBlank(className, "Invalid classname at position %d of method signature \"%s\"",
175 Integer.valueOf(type), internal);
176 pos.setIndex(semi + 1);
177 return Class.forName(className);
178 default:
179 throw new IllegalArgumentException(String.format(
180 "Unexpected character at index %d of method signature \"%s\"",
181 Integer.valueOf(here), internal));
182 }
183 }
184
185
186
187
188
189
190
191
192 private final String internal;
193
194
195
196
197
198
199
200
201
202
203 public MethodSignature(Method method)
204 {
205 final StringBuilder buf = new StringBuilder(method.getName()).append('(');
206 for (Class<?> p : method.getParameterTypes())
207 {
208 appendTo(buf, p);
209 }
210 buf.append(')');
211 this.internal = buf.toString();
212 }
213
214
215
216
217
218
219
220
221
222
223
224 public Method toMethod(Class<?> type)
225 {
226 final Pair<String, Class<?>[]> info = parse(internal);
227 return MethodUtils.getAccessibleMethod(type, info.getLeft(), info.getRight());
228 }
229
230
231
232
233
234
235
236
237 @Override
238 public boolean equals(Object o)
239 {
240 if (o == null)
241 {
242 return false;
243 }
244 if (o == this)
245 {
246 return true;
247 }
248 if (o.getClass() != getClass())
249 {
250 return false;
251 }
252 MethodSignature other = (MethodSignature) o;
253 return other.internal.equals(internal);
254 }
255
256
257
258
259 @Override
260 public int hashCode()
261 {
262 return new HashCodeBuilder().append(internal).build().intValue();
263 }
264
265
266
267
268 @Override
269 public String toString()
270 {
271 return internal;
272 }
273 }