blob: c15e4363ab7efff53c942feabf073a63a795a6f2 [file] [log] [blame]
Stuart McCullochf3173222012-06-07 21:57:32 +00001package aQute.lib.osgi;
2
3import java.util.*;
4
5import aQute.libg.generics.*;
6
7public class Descriptors {
Stuart McCulloch4482c702012-06-15 13:27:53 +00008 Map<String,TypeRef> typeRefCache = Create.map();
9 Map<String,Descriptor> descriptorCache = Create.map();
10 Map<String,PackageRef> packageCache = Create.map();
Stuart McCullochf3173222012-06-07 21:57:32 +000011
12 // MUST BE BEFORE PRIMITIVES, THEY USE THE DEFAULT PACKAGE!!
Stuart McCulloch4482c702012-06-15 13:27:53 +000013 final static PackageRef DEFAULT_PACKAGE = new PackageRef();
Stuart McCullochf3173222012-06-07 21:57:32 +000014 final static PackageRef PRIMITIVE_PACKAGE = new PackageRef();
Stuart McCullochf3173222012-06-07 21:57:32 +000015
Stuart McCulloch4482c702012-06-15 13:27:53 +000016 final static TypeRef VOID = new ConcreteRef("V", "void", PRIMITIVE_PACKAGE);
17 final static TypeRef BOOLEAN = new ConcreteRef("Z", "boolean", PRIMITIVE_PACKAGE);
18 final static TypeRef BYTE = new ConcreteRef("B", "byte", PRIMITIVE_PACKAGE);
19 final static TypeRef CHAR = new ConcreteRef("C", "char", PRIMITIVE_PACKAGE);
20 final static TypeRef SHORT = new ConcreteRef("S", "short", PRIMITIVE_PACKAGE);
21 final static TypeRef INTEGER = new ConcreteRef("I", "int", PRIMITIVE_PACKAGE);
22 final static TypeRef LONG = new ConcreteRef("J", "long", PRIMITIVE_PACKAGE);
23 final static TypeRef DOUBLE = new ConcreteRef("D", "double", PRIMITIVE_PACKAGE);
24 final static TypeRef FLOAT = new ConcreteRef("F", "float", PRIMITIVE_PACKAGE);
Stuart McCullochf3173222012-06-07 21:57:32 +000025
26 {
27 packageCache.put("", DEFAULT_PACKAGE);
28 }
29
Stuart McCulloch4482c702012-06-15 13:27:53 +000030 public interface TypeRef extends Comparable<TypeRef> {
Stuart McCullochf3173222012-06-07 21:57:32 +000031 String getBinary();
32
33 String getFQN();
34
35 String getPath();
36
37 boolean isPrimitive();
38
39 TypeRef getComponentTypeRef();
40
41 TypeRef getClassRef();
42
43 PackageRef getPackageRef();
44
45 String getShortName();
46
47 boolean isJava();
48
49 boolean isObject();
50
51 String getSourcePath();
52
53 String getDottedOnly();
54
55 }
56
Stuart McCulloch4482c702012-06-15 13:27:53 +000057 public static class PackageRef implements Comparable<PackageRef> {
Stuart McCullochf3173222012-06-07 21:57:32 +000058 final String binaryName;
59 final String fqn;
60 final boolean java;
61
62 private PackageRef(String binaryName) {
63 this.binaryName = fqnToBinary(binaryName);
64 this.fqn = binaryToFQN(binaryName);
Stuart McCulloch4482c702012-06-15 13:27:53 +000065 this.java = this.fqn.startsWith("java."); // &&
66 // !this.fqn.equals("java.sql)"
67
Stuart McCullochf3173222012-06-07 21:57:32 +000068 // For some reason I excluded java.sql but the classloader will
69 // delegate anyway. So lost the understanding why I did it??
70 }
71
72 private PackageRef() {
73 this.binaryName = "";
Stuart McCulloch4482c702012-06-15 13:27:53 +000074 this.fqn = ".";
Stuart McCullochf3173222012-06-07 21:57:32 +000075 this.java = false;
76 }
77
78 public PackageRef getDuplicate() {
Stuart McCulloch4482c702012-06-15 13:27:53 +000079 return new PackageRef(binaryName + Constants.DUPLICATE_MARKER);
Stuart McCullochf3173222012-06-07 21:57:32 +000080 }
Stuart McCulloch4482c702012-06-15 13:27:53 +000081
Stuart McCullochf3173222012-06-07 21:57:32 +000082 public String getFQN() {
83 return fqn;
84 }
85
86 public String getBinary() {
87 return binaryName;
88 }
89
90 public String getPath() {
91 return binaryName;
92 }
93
94 public boolean isJava() {
95 return java;
96 }
97
98 public String toString() {
99 return fqn;
100 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000101
Stuart McCullochf3173222012-06-07 21:57:32 +0000102 boolean isDefaultPackage() {
103 return this.fqn.equals(".");
104 }
105
106 boolean isPrimitivePackage() {
107 return this == PRIMITIVE_PACKAGE;
108 }
109
110 public int compareTo(PackageRef other) {
111 return fqn.compareTo(other.fqn);
112 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000113
Stuart McCullochf3173222012-06-07 21:57:32 +0000114 public boolean equals(Object o) {
115 assert o instanceof PackageRef;
116 return o == this;
117 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000118
Stuart McCullochf3173222012-06-07 21:57:32 +0000119 public int hashCode() {
120 return super.hashCode();
121 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000122
Stuart McCullochf3173222012-06-07 21:57:32 +0000123 /**
124 * Decide if the package is a metadata package.
125 *
126 * @param pack
127 * @return
128 */
129 public boolean isMetaData() {
130 if (isDefaultPackage())
131 return true;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000132
Stuart McCullochf3173222012-06-07 21:57:32 +0000133 for (int i = 0; i < Constants.METAPACKAGES.length; i++) {
134 if (fqn.startsWith(Constants.METAPACKAGES[i]))
135 return true;
136 }
137 return false;
138 }
139
140 }
141
142 // We "intern" the
143 private static class ConcreteRef implements TypeRef {
144 final String binaryName;
145 final String fqn;
146 final boolean primitive;
147 final PackageRef packageRef;
148
149 ConcreteRef(PackageRef packageRef, String binaryName) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000150 if (packageRef.getFQN().length() < 2)
Stuart McCullochf3173222012-06-07 21:57:32 +0000151 System.err.println("in default pack? " + binaryName);
152 this.binaryName = binaryName;
153 this.fqn = binaryToFQN(binaryName);
154 this.primitive = false;
155 this.packageRef = packageRef;
156 }
157
158 ConcreteRef(String binaryName, String fqn, PackageRef pref) {
159 this.binaryName = binaryName;
160 this.fqn = fqn;
161 this.primitive = true;
162 this.packageRef = pref;
163 }
164
165 public String getBinary() {
166 return binaryName;
167 }
168
169 public String getPath() {
170 return binaryName + ".class";
171 }
172
173 public String getSourcePath() {
174 return binaryName + ".java";
175 }
176
177 public String getFQN() {
178 return fqn;
179 }
180
181 public String getDottedOnly() {
182 return fqn.replace('$', '.');
183 }
184
185 public boolean isPrimitive() {
186 return primitive;
187 }
188
189 public TypeRef getComponentTypeRef() {
190 return null;
191 }
192
193 public TypeRef getClassRef() {
194 return this;
195 }
196
197 public PackageRef getPackageRef() {
198 return packageRef;
199 }
200
201 public String getShortName() {
202 int n = binaryName.lastIndexOf('/');
203 return binaryName.substring(n + 1);
204 }
205
206 public boolean isJava() {
207 return packageRef.isJava();
208 }
209
210 public String toString() {
211 return fqn;
212 }
213
214 public boolean isObject() {
215 return fqn.equals("java.lang.Object");
216 }
217
218 public boolean equals(Object other) {
219 assert other instanceof TypeRef;
220 return this == other;
221 }
222
223 public int compareTo(TypeRef other) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000224 if (this == other)
Stuart McCullochf3173222012-06-07 21:57:32 +0000225 return 0;
226 return fqn.compareTo(other.getFQN());
227 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000228
Stuart McCullochf3173222012-06-07 21:57:32 +0000229 }
230
231 private static class ArrayRef implements TypeRef {
232 final TypeRef component;
233
234 ArrayRef(TypeRef component) {
235 this.component = component;
236 }
237
238 public String getBinary() {
239 return "[" + component.getBinary();
240 }
241
242 public String getFQN() {
243 return component.getFQN() + "[]";
244 }
245
246 public String getPath() {
247 return component.getPath();
248 }
249
250 public String getSourcePath() {
251 return component.getSourcePath();
252 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000253
Stuart McCullochf3173222012-06-07 21:57:32 +0000254 public boolean isPrimitive() {
255 return false;
256 }
257
258 public TypeRef getComponentTypeRef() {
259 return component;
260 }
261
262 public TypeRef getClassRef() {
263 return component.getClassRef();
264 }
265
266 public boolean equals(Object other) {
267 if (other == null || other.getClass() != getClass())
268 return false;
269
270 return component.equals(((ArrayRef) other).component);
271 }
272
273 public PackageRef getPackageRef() {
274 return component.getPackageRef();
275 }
276
277 public String getShortName() {
278 return component.getShortName() + "[]";
279 }
280
281 public boolean isJava() {
282 return component.isJava();
283 }
284
285 public String toString() {
286 return component.toString() + "[]";
287 }
288
289 public boolean isObject() {
290 return false;
291 }
292
293 public String getDottedOnly() {
294 return component.getDottedOnly();
295 }
296
297 public int compareTo(TypeRef other) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000298 if (this == other)
Stuart McCullochf3173222012-06-07 21:57:32 +0000299 return 0;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000300
Stuart McCullochf3173222012-06-07 21:57:32 +0000301 return getFQN().compareTo(other.getFQN());
302 }
303
304 }
305
Stuart McCulloch4482c702012-06-15 13:27:53 +0000306 public TypeRef getTypeRef(String binaryClassName) {
Stuart McCullochf3173222012-06-07 21:57:32 +0000307 assert !binaryClassName.endsWith(".class");
Stuart McCulloch4482c702012-06-15 13:27:53 +0000308
Stuart McCullochf3173222012-06-07 21:57:32 +0000309 TypeRef ref = typeRefCache.get(binaryClassName);
310 if (ref != null)
311 return ref;
312
313 if (binaryClassName.startsWith("[")) {
314 ref = getTypeRef(binaryClassName.substring(1));
315 ref = new ArrayRef(ref);
316 } else {
317 if (binaryClassName.length() >= 1) {
318 switch (binaryClassName.charAt(0)) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000319 case 'V' :
320 return VOID;
321 case 'B' :
322 return BYTE;
323 case 'C' :
324 return CHAR;
325 case 'I' :
326 return INTEGER;
327 case 'S' :
328 return SHORT;
329 case 'D' :
330 return DOUBLE;
331 case 'F' :
332 return FLOAT;
333 case 'J' :
334 return LONG;
335 case 'Z' :
336 return BOOLEAN;
337 case 'L' :
338 binaryClassName = binaryClassName.substring(1, binaryClassName.length() - 1);
339 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000340 }
341 // falls trough for other 1 letter class names
342 }
343 ref = typeRefCache.get(binaryClassName);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000344 if (ref != null)
Stuart McCullochf3173222012-06-07 21:57:32 +0000345 return ref;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000346
Stuart McCullochf3173222012-06-07 21:57:32 +0000347 PackageRef pref;
348 int n = binaryClassName.lastIndexOf('/');
349 if (n < 0)
350 pref = DEFAULT_PACKAGE;
351 else
352 pref = getPackageRef(binaryClassName.substring(0, n));
353
354 ref = new ConcreteRef(pref, binaryClassName);
355 }
Stuart McCulloch4482c702012-06-15 13:27:53 +0000356
Stuart McCullochf3173222012-06-07 21:57:32 +0000357 typeRefCache.put(binaryClassName, ref);
358 return ref;
359 }
360
361 public PackageRef getPackageRef(String binaryPackName) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000362 if (binaryPackName.indexOf('.') >= 0) {
Stuart McCullochf3173222012-06-07 21:57:32 +0000363 binaryPackName = binaryPackName.replace('.', '/');
364 }
365 PackageRef ref = packageCache.get(binaryPackName);
366 if (ref != null)
367 return ref;
368
369 ref = new PackageRef(binaryPackName);
370 packageCache.put(binaryPackName, ref);
371 return ref;
372 }
373
374 public Descriptor getDescriptor(String descriptor) {
375 Descriptor d = descriptorCache.get(descriptor);
376 if (d != null)
377 return d;
378 d = new Descriptor(descriptor);
379 descriptorCache.put(descriptor, d);
380 return d;
381 }
382
383 public class Descriptor {
384 final TypeRef type;
385 final TypeRef[] prototype;
386 final String descriptor;
387
388 private Descriptor(String descriptor) {
389 this.descriptor = descriptor;
390 int index = 0;
391 List<TypeRef> types = Create.list();
392 if (descriptor.charAt(index) == '(') {
393 index++;
394 while (descriptor.charAt(index) != ')') {
395 index = parse(types, descriptor, index);
396 }
397 index++; // skip )
398 prototype = types.toArray(new TypeRef[types.size()]);
399 types.clear();
400 } else
401 prototype = null;
402
403 index = parse(types, descriptor, index);
404 type = types.get(0);
405 }
406
407 int parse(List<TypeRef> types, String descriptor, int index) {
408 char c;
409 StringBuilder sb = new StringBuilder();
410 while ((c = descriptor.charAt(index++)) == '[') {
411 sb.append('[');
412 }
413
414 switch (c) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000415 case 'L' :
416 while ((c = descriptor.charAt(index++)) != ';') {
417 // TODO
418 sb.append(c);
419 }
420 break;
421
422 case 'V' :
423 case 'B' :
424 case 'C' :
425 case 'I' :
426 case 'S' :
427 case 'D' :
428 case 'F' :
429 case 'J' :
430 case 'Z' :
Stuart McCullochf3173222012-06-07 21:57:32 +0000431 sb.append(c);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000432 break;
Stuart McCullochf3173222012-06-07 21:57:32 +0000433
Stuart McCulloch4482c702012-06-15 13:27:53 +0000434 default :
435 throw new IllegalArgumentException("Invalid type in descriptor: " + c + " from " + descriptor + "["
436 + index + "]");
Stuart McCullochf3173222012-06-07 21:57:32 +0000437 }
438 types.add(getTypeRef(sb.toString()));
439 return index;
440 }
441
442 public TypeRef getType() {
443 return type;
444 }
445
446 public TypeRef[] getPrototype() {
447 return prototype;
448 }
449
450 public boolean equals(Object other) {
451 if (other == null || other.getClass() != getClass())
452 return false;
453
Stuart McCulloch4482c702012-06-15 13:27:53 +0000454 return Arrays.equals(prototype, ((Descriptor) other).prototype) && type == ((Descriptor) other).type;
Stuart McCullochf3173222012-06-07 21:57:32 +0000455 }
456
457 public int hashCode() {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000458 return prototype == null ? type.hashCode() : type.hashCode() ^ Arrays.hashCode(prototype);
Stuart McCullochf3173222012-06-07 21:57:32 +0000459 }
460
461 public String toString() {
462 return descriptor;
463 }
464 }
465
466 /**
467 * Return the short name of a FQN
468 */
469
470 public static String getShortName(String fqn) {
471 assert fqn.indexOf('/') < 0;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000472
Stuart McCullochf3173222012-06-07 21:57:32 +0000473 int n = fqn.lastIndexOf('.');
474 if (n >= 0) {
475 return fqn.substring(n + 1);
476 }
477 return fqn;
478 }
479
480 public static String binaryToFQN(String binary) {
481 StringBuilder sb = new StringBuilder();
Stuart McCulloch4482c702012-06-15 13:27:53 +0000482 for (int i = 0, l = binary.length(); i < l; i++) {
Stuart McCullochf3173222012-06-07 21:57:32 +0000483 char c = binary.charAt(i);
Stuart McCulloch4482c702012-06-15 13:27:53 +0000484
485 if (c == '/')
Stuart McCullochf3173222012-06-07 21:57:32 +0000486 sb.append('.');
487 else
488 sb.append(c);
489 }
490 String result = sb.toString();
491 assert result.length() > 0;
492 return result;
493 }
494
495 public static String fqnToBinary(String binary) {
496 return binary.replace('.', '/');
497 }
498
499 public static String getPackage(String binaryNameOrFqn) {
500 int n = binaryNameOrFqn.lastIndexOf('/');
501 if (n >= 0)
502 return binaryNameOrFqn.substring(0, n).replace('/', '.');
503
504 n = binaryNameOrFqn.lastIndexOf(".");
505 if (n >= 0)
506 return binaryNameOrFqn.substring(0, n);
507
508 return ".";
509 }
510
511 public static String fqnToPath(String s) {
512 return fqnToBinary(s) + ".class";
513 }
514
515 public TypeRef getTypeRefFromFQN(String fqn) {
Stuart McCulloch4482c702012-06-15 13:27:53 +0000516 if (fqn.equals("boolean"))
Stuart McCullochf3173222012-06-07 21:57:32 +0000517 return BOOLEAN;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000518
519 if (fqn.equals("byte"))
Stuart McCullochf3173222012-06-07 21:57:32 +0000520 return BOOLEAN;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000521
522 if (fqn.equals("char"))
Stuart McCullochf3173222012-06-07 21:57:32 +0000523 return CHAR;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000524
525 if (fqn.equals("short"))
Stuart McCullochf3173222012-06-07 21:57:32 +0000526 return SHORT;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000527
528 if (fqn.equals("int"))
Stuart McCullochf3173222012-06-07 21:57:32 +0000529 return INTEGER;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000530
531 if (fqn.equals("long"))
Stuart McCullochf3173222012-06-07 21:57:32 +0000532 return LONG;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000533
534 if (fqn.equals("float"))
Stuart McCullochf3173222012-06-07 21:57:32 +0000535 return FLOAT;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000536
537 if (fqn.equals("double"))
Stuart McCullochf3173222012-06-07 21:57:32 +0000538 return DOUBLE;
Stuart McCulloch4482c702012-06-15 13:27:53 +0000539
Stuart McCullochf3173222012-06-07 21:57:32 +0000540 return getTypeRef(fqnToBinary(fqn));
541 }
542
543 public TypeRef getTypeRefFromPath(String path) {
544 assert path.endsWith(".class");
Stuart McCulloch4482c702012-06-15 13:27:53 +0000545 return getTypeRef(path.substring(0, path.length() - 6));
Stuart McCullochf3173222012-06-07 21:57:32 +0000546 }
547}