blob: ec4a5450ad339ea13acb8e684c6db9c154a33662 [file] [log] [blame]
Stuart McCulloch26e7a5a2011-10-17 10:31:43 +00001/*
2 * Copyright (c) OSGi Alliance (2009, 2010). All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package aQute.bnd.compatibility;
18
19import java.lang.reflect.*;
20import java.util.*;
21
22/**
23 * This class is compiled against 1.5 or later to provide access to the generic
24 * signatures. It can convert a Class, Field, Method or constructor to a generic
25 * signature and it can normalize a signature. Both are methods. Normalized
26 * signatures can be string compared and match even if the type variable names
27 * differ.
28 *
29 * @version $Id$
30 */
31public class Signatures {
32
33
34 /**
35 * Check if the environment has generics, i.e. later than
36 * Java 5 VM.
37 *
38 * @return true if generics are supported
39 * @throws Exception
40 */
41 public boolean hasGenerics() throws Exception {
42 try {
43 call( Signatures.class, "getGenericSuperClass");
44 return true;
45 } catch( NoSuchMethodException mnfe ) {
46 return false;
47 }
48 }
49
50
51
52 /**
53 * Helper class to track an index in a string.
54 */
55 class Rover {
56 final String s;
57 int i;
58
59 public Rover(String s) {
60 this.s = s;
61 i = 0;
62 }
63
64 char peek() {
65 return s.charAt(i);
66 }
67
68 char take() {
69 return s.charAt(i++);
70 }
71
72 char take(char c) {
73 char x = s.charAt(i++);
74 if (c != x)
75 throw new IllegalStateException("get() expected " + c
76 + " but got + " + x);
77 return x;
78 }
79
80 public String upTo(String except) {
81 int start = i;
82 while (except.indexOf(peek()) < 0)
83 take();
84 return s.substring(start, i);
85 }
86
87 public boolean isEOF() {
88 return i >= s.length();
89 }
90
91 }
92
93 /**
94 * Calculate the generic signature of a Class,Method,Field, or Constructor.
95 * @param f
96 * @return
97 * @throws Exception
98 */
99 public String getSignature(Object c) throws Exception {
100 if( c instanceof Class<?>)
101 return getSignature((Class<?>)c);
102 if( c instanceof Constructor<?>)
103 return getSignature((Constructor<?>)c);
104 if( c instanceof Method)
105 return getSignature((Method)c);
106 if( c instanceof Field)
107 return getSignature((Field)c);
108
109 throw new IllegalArgumentException(c.toString());
110 }
111
112 /**
113 * Calculate the generic signature of a Class. A Class consists of:
114 *
115 * <pre>
116 * class ::= declaration? reference reference*
117 * </pre>
118 *
119 *
120 * @param f
121 * @return
122 * @throws Exception
123 */
124 public String getSignature(Class< ? > c) throws Exception {
125 StringBuffer sb = new StringBuffer();
126 declaration(sb, c);
127 reference(sb, call(c, "getGenericSuperclass"));
128 for (Object type : (Object[]) call(c,"getGenericInterfaces")) {
129 reference(sb, type);
130 }
131 return sb.toString();
132 }
133
134 /**
135 * Calculate the generic signature of a Method. A Method consists of:
136 *
137 * <pre>
138 * method ::= declaration? '(' reference* ')' reference
139 * </pre>
140 *
141 * @param c
142 * @return
143 * @throws Exception
144 */
145 public String getSignature(Method m) throws Exception {
146 StringBuffer sb = new StringBuffer();
147 declaration(sb, m);
148 sb.append('(');
149 for (Object type : (Object[]) call(m,"getGenericParameterTypes")) {
150 reference(sb, type);
151 }
152 sb.append(')');
153 reference(sb, call(m,"getGenericReturnType"));
154 return sb.toString();
155 }
156
157 /**
158 * Calculate the generic signature of a Constructor. A Constructor consists
159 * of:
160 *
161 * <pre>
162 * constructor ::= declaration? '(' reference* ')V'
163 * </pre>
164 *
165 * @param c
166 * @return
167 * @throws Exception
168 */
169 public String getSignature(Constructor< ? > c) throws Exception {
170 StringBuffer sb = new StringBuffer();
171 declaration(sb, c);
172 sb.append('(');
173 for (Object type : (Object[]) call(c,"getGenericParameterTypes")) {
174 reference(sb, type);
175 }
176 sb.append(')');
177 reference(sb, void.class);
178 return sb.toString();
179 }
180
181 /**
182 * Calculate the generic signature of a Field. A Field consists of:
183 *
184 * <pre>
185 * constructor ::= reference
186 * </pre>
187 *
188 * @param c
189 * @return
190 * @throws Exception
191 */
192 public String getSignature(Field f) throws Exception {
193 StringBuffer sb = new StringBuffer();
194 Object t = call(f,"getGenericType");
195 reference(sb, t);
196 return sb.toString();
197 }
198
199/**
200 * Classes, Methods, or Constructors can have a declaration that provides
201 * nested a scope for type variables. A Method/Constructor inherits
202 * the type variables from its class and a class inherits its type variables
203 * from its outer class. The declaration consists of the following
204 * syntax:
205 * <pre>
206 * declarations ::= '<' declaration ( ',' declaration )* '>'
207 * declaration ::= identifier ':' declare
208 * declare ::= types | variable
209 * types ::= ( 'L' class ';' )? ( ':' 'L' interface ';' )*
210 * variable ::= 'T' id ';'
211 * </pre>
212 *
213 * @param sb
214 * @param gd
215 * @throws Exception
216 */
217 private void declaration(StringBuffer sb, Object gd) throws Exception {
218 Object[] typeParameters = (Object[]) call(gd,"getTypeParameters");
219 if (typeParameters.length > 0) {
220 sb.append('<');
221 for (Object tv : typeParameters) {
222 sb.append( call(tv,"getName"));
223
224 Object[] bounds = (Object[]) call(tv,"getBounds");
225 if (bounds.length > 0 && isInterface(bounds[0])) {
226 sb.append(':');
227 }
228 for (int i = 0; i < bounds.length; i++) {
229 sb.append(':');
230 reference(sb, bounds[i]);
231 }
232 }
233 sb.append('>');
234 }
235 }
236
237 /**
238 * Verify that the type is an interface.
239 *
240 * @param type the type to check.
241 * @return true if this is a class that is an interface or a Parameterized
242 * Type that is an interface
243 * @throws Exception
244 */
245 private boolean isInterface(Object type) throws Exception {
246 if (type instanceof Class)
247 return (((Class< ? >) type).isInterface());
248
249 if ( isInstance(type.getClass(), "java.lang.reflect.ParameterizedType"))
250 return isInterface(call(type,"getRawType"));
251
252 return false;
253 }
254
255
256/**
257 * This is the heart of the signature builder. A reference is used
258 * in a lot of places. It referes to another type.
259 * <pre>
260 * reference ::= array | class | primitive | variable
261 * array ::= '[' reference
262 * class ::= 'L' body ( '.' body )* ';'
263 * body ::= id ( '<' ( wildcard | reference )* '>' )?
264 * variable ::= 'T' id ';'
265 * primitive ::= PRIMITIVE
266 * </pre>
267 *
268 * @param sb
269 * @param t
270 * @throws Exception
271 */
272 private void reference(StringBuffer sb, Object t) throws Exception {
273
274 if ( isInstance(t.getClass(),"java.lang.reflect.ParameterizedType")) {
275 sb.append('L');
276 parameterizedType(sb, t);
277 sb.append(';');
278 return;
279 }
280 else
281 if ( isInstance(t.getClass(), "java.lang.reflect.GenericArrayType")) {
282 sb.append('[');
283 reference(sb, call(t,"getGenericComponentType"));
284 }
285 else
286 if ( isInstance(t.getClass(), "java.lang.reflect.WildcardType")) {
287 Object[] lowerBounds = (Object[]) call(t, "getLowerBounds");
288 Object[] upperBounds = (Object[]) call(t, "getUpperBounds");
289
290 if (upperBounds.length == 1
291 && upperBounds[0] == Object.class)
292 upperBounds = new Object[0];
293
294 if (upperBounds.length != 0) {
295 // extend
296 for (Object upper : upperBounds) {
297 sb.append('+');
298 reference(sb, upper);
299 }
300 }
301 else
302 if (lowerBounds.length != 0) {
303 // super, can only be one by the language
304 for (Object lower : lowerBounds) {
305 sb.append('-');
306 reference(sb, lower);
307 }
308 }
309 else
310 sb.append('*');
311 }
312 else
313 if ( isInstance(t.getClass(),"java.lang.reflect.TypeVariable")) {
314 sb.append('T');
315 sb.append( call(t,"getName"));
316 sb.append(';');
317 }
318 else
319 if (t instanceof Class< ? >) {
320 Class< ? > c = (Class< ? >) t;
321 if (c.isPrimitive()) {
322 sb.append(primitive(c));
323 }
324 else {
325 sb.append('L');
326 String name = c.getName().replace('.', '/');
327 sb.append(name);
328 sb.append(';');
329 }
330 }
331 }
332
333 /**
334 * Creates the signature for a Parameterized Type.
335 *
336 * A Parameterized Type has a raw class and a set of type variables.
337 *
338 * @param sb
339 * @param pt
340 * @throws Exception
341 */
342 private void parameterizedType(StringBuffer sb, Object pt) throws Exception {
343 Object owner = call(pt,"getOwnerType");
344 String name = ((Class< ? >) call(pt,"getRawType")).getName()
345 .replace('.', '/');
346 if (owner != null) {
347 if ( isInstance(owner.getClass(), "java.lang.reflect.ParameterizedType"))
348 parameterizedType(sb, owner);
349 else
350 sb.append(((Class< ? >) owner).getName().replace('.', '/'));
351 sb.append('.');
352 int n = name.lastIndexOf('$');
353 name = name.substring(n + 1);
354 }
355 sb.append(name);
356
357 sb.append('<');
358 for (Object parameterType : (Object[]) call(pt,"getActualTypeArguments")) {
359 reference(sb, parameterType);
360 }
361 sb.append('>');
362
363 }
364
365 /**
366 * Handle primitives, these need to be translated to a single char.
367 *
368 * @param type the primitive class
369 * @return the single char associated with the primitive
370 */
371 private char primitive(Class< ? > type) {
372 if (type == byte.class)
373 return 'B';
374 else
375 if (type == char.class)
376 return 'C';
377 else
378 if (type == double.class)
379 return 'D';
380 else
381 if (type == float.class)
382 return 'F';
383 else
384 if (type == int.class)
385 return 'I';
386 else
387 if (type == long.class)
388 return 'J';
389 else
390 if (type == short.class)
391 return 'S';
392 else
393 if (type == boolean.class)
394 return 'Z';
395 else
396 if (type == void.class)
397 return 'V';
398 else
399 throw new IllegalArgumentException(
400 "Unknown primitive type "
401 + type);
402 }
403
404 /**
405 * Normalize a signature to make sure the name of the variables are always
406 * the same. We change the names of the type variables to _n, where n is an
407 * integer. n is incremented for every new name and already used names are
408 * replaced with the _n name.
409 *
410 * @return a normalized signature
411 */
412
413 public String normalize(String signature) {
414 StringBuffer sb = new StringBuffer();
415 Map<String, String> map = new HashMap<String, String>();
416 Rover rover = new Rover(signature);
417 declare(sb, map, rover);
418
419 if (rover.peek() == '(') {
420 // method or constructor
421 sb.append(rover.take('('));
422 while (rover.peek() != ')') {
423 reference(sb, map, rover, true);
424 }
425 sb.append(rover.take(')'));
426 reference(sb, map, rover, true); // return type
427 }
428 else {
429 // field or class
430 reference(sb, map, rover, true); // field type or super class
431 while (!rover.isEOF()) {
432 reference(sb, map, rover, true); // interfaces
433 }
434 }
435 return sb.toString();
436 }
437
438 /**
439 * The heart of the routine. Handle a reference to a type. Can be
440 * an array, a class, a type variable, or a primitive.
441 *
442 * @param sb
443 * @param map
444 * @param rover
445 * @param primitivesAllowed
446 */
447 private void reference(StringBuffer sb, Map<String, String> map,
448 Rover rover, boolean primitivesAllowed) {
449
450 char type = rover.take();
451 sb.append(type);
452
453 if (type == '[') {
454 reference(sb, map, rover, true);
455 }
456 else
457 if (type == 'L') {
458 String fqnb = rover.upTo("<;.");
459 sb.append(fqnb);
460 body(sb, map, rover);
461 while (rover.peek() == '.') {
462 sb.append(rover.take('.'));
463 sb.append(rover.upTo("<;."));
464 body(sb, map, rover);
465 }
466 sb.append(rover.take(';'));
467 }
468 else
469 if (type == 'T') {
470 String name = rover.upTo(";");
471 name = assign(map, name);
472 sb.append(name);
473 sb.append(rover.take(';'));
474 }
475 else {
476 if (!primitivesAllowed)
477 throw new IllegalStateException(
478 "Primitives are not allowed without an array");
479 }
480 }
481
482 /**
483 * Because classes can be nested the body handles the part that can
484 * be nested, the reference handles the enclosing L ... ;
485 *
486 * @param sb
487 * @param map
488 * @param rover
489 */
490 private void body(StringBuffer sb, Map<String, String> map, Rover rover) {
491 if (rover.peek() == '<') {
492 sb.append(rover.take('<'));
493 while (rover.peek() != '>') {
494 switch (rover.peek()) {
495 case 'L' :
496 case '[' :
497 reference(sb, map, rover, false);
498 break;
499
500 case 'T' :
501 String name;
502 sb.append(rover.take('T')); // 'T'
503 name = rover.upTo(";");
504 sb.append(assign(map, name));
505 sb.append(rover.take(';'));
506 break;
507
508 case '+' : // extends
509 case '-' : // super
510 sb.append(rover.take());
511 reference(sb, map, rover, false);
512 break;
513
514 case '*' : // wildcard
515 sb.append(rover.take());
516 break;
517
518 }
519 }
520 sb.append(rover.take('>'));
521 }
522 }
523
524 /**
525 * Handle the declaration part.
526 *
527 * @param sb
528 * @param map
529 * @param rover
530 */
531 private void declare(StringBuffer sb, Map<String, String> map, Rover rover) {
532 char c = rover.peek();
533 if (c == '<') {
534 sb.append(rover.take('<'));
535
536 while (rover.peek() != '>') {
537 String name = rover.upTo(":");
538 name = assign(map, name);
539 sb.append(name);
540 typeVar: while (rover.peek() == ':') {
541 sb.append(rover.take(':'));
542 switch (rover.peek()) {
543 case ':' : // empty class cases
544 continue typeVar;
545
546 default :
547 reference(sb, map, rover, false);
548 break;
549 }
550 }
551 }
552 sb.append(rover.take('>'));
553 }
554 }
555
556 /**
557 * Handles the assignment of type variables to index names so that
558 * we have a normalized name for each type var.
559 *
560 * @param map the map with variables.
561 * @param name The name of the variable
562 * @return the index name, like _1
563 */
564 private String assign(Map<String, String> map, String name) {
565 if (map.containsKey(name))
566 return map.get(name);
567 else {
568 int n = map.size();
569 map.put(name, "_" + n);
570 return "_" + n;
571 }
572 }
573
574 private boolean isInstance(Class<?> type, String string) {
575 if ( type == null)
576 return false;
577
578 if ( type.getName().equals(string))
579 return true;
580
581 if ( isInstance( type.getSuperclass(), string))
582 return true;
583
584 for ( Class<?> intf : type.getInterfaces()) {
585 if ( isInstance(intf,string))
586 return true;
587 }
588 return false;
589 }
590
591 private Object call(Object gd, String string) throws Exception {
592 Method m = gd.getClass().getMethod(string);
593 return m.invoke(gd);
594 }
595
596}