|
37 | 37 | import java.io.IOException; |
38 | 38 | import java.io.InputStream; |
39 | 39 | import java.util.Iterator; |
40 | | -import java.util.LinkedHashMap; |
41 | 40 | import java.util.Map; |
| 41 | +import java.util.TreeMap; |
42 | 42 |
|
43 | 43 | /** |
44 | 44 | * An analyzer to parse {@code @Plugin} annotations inside a {@code .class} |
@@ -440,10 +440,160 @@ public String toString() { |
440 | 440 | } |
441 | 441 | } |
442 | 442 |
|
| 443 | + private Map<String, Map<String, Object>> getAnnotations() { |
| 444 | + final Map<String, Map<String, Object>> annotations = |
| 445 | + new TreeMap<String, Map<String, Object>>(); |
| 446 | + for (final Attribute attr : attributes) { |
| 447 | + if ("RuntimeVisibleAnnotations".equals(attr.getName())) { |
| 448 | + final byte[] buffer = attr.attribute; |
| 449 | + int count = getU2(buffer, 0); |
| 450 | + int offset = 2; |
| 451 | + for (int i = 0; i < count; i++) { |
| 452 | + final String className = |
| 453 | + raw2className(getStringConstant(getU2(buffer, offset))); |
| 454 | + offset += 2; |
| 455 | + final Map<String, Object> values = |
| 456 | + new TreeMap<String, Object>(); |
| 457 | + annotations.put(className, values); |
| 458 | + offset = parseAnnotationValues(buffer, offset, values); |
| 459 | + } |
| 460 | + } |
| 461 | + } |
| 462 | + return annotations; |
| 463 | + } |
| 464 | + |
| 465 | + private int parseAnnotationValues(final byte[] buffer, int offset, |
| 466 | + final Map<String, Object> values) |
| 467 | + { |
| 468 | + int count = getU2(buffer, offset); |
| 469 | + offset += 2; |
| 470 | + for (int i = 0; i < count; i++) { |
| 471 | + final String key = getStringConstant(getU2(buffer, offset)); |
| 472 | + offset += 2; |
| 473 | + offset = parseAnnotationValue(buffer, offset, values, key); |
| 474 | + } |
| 475 | + return offset; |
| 476 | + } |
| 477 | + |
| 478 | + private int parseAnnotationValue(byte[] buffer, int offset, |
| 479 | + Map<String, Object> map, String key) |
| 480 | + { |
| 481 | + Object value; |
| 482 | + switch (getU1(buffer, offset++)) { |
| 483 | + case 'Z': |
| 484 | + value = Boolean.valueOf(getIntegerConstant(getU2(buffer, offset)) != 0); |
| 485 | + offset += 2; |
| 486 | + break; |
| 487 | + case 'B': |
| 488 | + value = Byte.valueOf((byte) getIntegerConstant(getU2(buffer, offset))); |
| 489 | + offset += 2; |
| 490 | + break; |
| 491 | + case 'C': |
| 492 | + value = |
| 493 | + Character.valueOf((char) getIntegerConstant(getU2(buffer, offset))); |
| 494 | + offset += 2; |
| 495 | + break; |
| 496 | + case 'S': |
| 497 | + value = |
| 498 | + Short.valueOf((short) getIntegerConstant(getU2(buffer, offset))); |
| 499 | + offset += 2; |
| 500 | + break; |
| 501 | + case 'I': |
| 502 | + value = |
| 503 | + Integer.valueOf((int) getIntegerConstant(getU2(buffer, offset))); |
| 504 | + offset += 2; |
| 505 | + break; |
| 506 | + case 'J': |
| 507 | + value = Long.valueOf(getLongConstant(getU2(buffer, offset))); |
| 508 | + offset += 2; |
| 509 | + break; |
| 510 | + case 'F': |
| 511 | + value = Float.valueOf(getFloatConstant(getU2(buffer, offset))); |
| 512 | + offset += 2; |
| 513 | + break; |
| 514 | + case 'D': |
| 515 | + value = Double.valueOf(getDoubleConstant(getU2(buffer, offset))); |
| 516 | + offset += 2; |
| 517 | + break; |
| 518 | + case 's': |
| 519 | + value = getStringConstant(getU2(buffer, offset)); |
| 520 | + offset += 2; |
| 521 | + break; |
| 522 | + case 'c': |
| 523 | + value = raw2className(getStringConstant(getU2(buffer, offset))); |
| 524 | + offset += 2; |
| 525 | + break; |
| 526 | + case '[': { |
| 527 | + final Object[] array = new Object[getU2(buffer, offset)]; |
| 528 | + offset += 2; |
| 529 | + for (int i = 0; i < array.length; i++) { |
| 530 | + offset = parseAnnotationValue(buffer, offset, map, key); |
| 531 | + array[i] = map.get(key); |
| 532 | + } |
| 533 | + value = array; |
| 534 | + break; |
| 535 | + } |
| 536 | + case 'e': { |
| 537 | + final Map<String, Object> enumValue = |
| 538 | + new TreeMap<String, Object>(); |
| 539 | + enumValue.put("enum", raw2className(getStringConstant(getU2(buffer, |
| 540 | + offset)))); |
| 541 | + offset += 2; |
| 542 | + enumValue.put("value", getStringConstant(getU2(buffer, offset))); |
| 543 | + offset += 2; |
| 544 | + value = enumValue; |
| 545 | + break; |
| 546 | + } |
| 547 | + case '@': { |
| 548 | + // skipping annotation type |
| 549 | + offset += 2; |
| 550 | + final Map<String, Object> values = new TreeMap<String, Object>(); |
| 551 | + offset = parseAnnotationValues(buffer, offset, values); |
| 552 | + value = values; |
| 553 | + break; |
| 554 | + } |
| 555 | + default: |
| 556 | + throw new RuntimeException("Unhandled annotation value type: " + |
| 557 | + (char) getU1(buffer, offset - 1)); |
| 558 | + } |
| 559 | + if (value == null) { |
| 560 | + throw new NullPointerException(); |
| 561 | + } |
| 562 | + map.put(key, value); |
| 563 | + return offset; |
| 564 | + } |
| 565 | + |
| 566 | + private static String raw2className(final String rawName) { |
| 567 | + if (!rawName.startsWith("L") || !rawName.endsWith(";")) { |
| 568 | + throw new RuntimeException("Invalid raw class name: " + rawName); |
| 569 | + } |
| 570 | + return rawName.substring(1, rawName.length() - 1).replace('/', '.'); |
| 571 | + } |
| 572 | + |
443 | 573 | private String toString(final Attribute[] attributes) { |
444 | 574 | String result = ""; |
445 | 575 | for (final Attribute attribute : attributes) |
446 | 576 | result += (result.equals("") ? "(" : ";") + attribute; |
447 | 577 | return result.equals("") ? "" : result + ")"; |
448 | 578 | } |
| 579 | + |
| 580 | + private static byte[] readFile(final File file) throws IOException { |
| 581 | + final InputStream in = new FileInputStream(file); |
| 582 | + final ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| 583 | + final byte[] buffer = new byte[16384]; |
| 584 | + for (;;) { |
| 585 | + int count = in.read(buffer); |
| 586 | + if (count < 0) break; |
| 587 | + out.write(buffer, 0, count); |
| 588 | + } |
| 589 | + in.close(); |
| 590 | + out.close(); |
| 591 | + return out.toByteArray(); |
| 592 | + } |
| 593 | + |
| 594 | + static Map<String, Map<String, Object>> getAnnotations(File file) |
| 595 | + throws IOException |
| 596 | + { |
| 597 | + return new ByteCodeAnalyzer(readFile(file)).getAnnotations(); |
| 598 | + } |
449 | 599 | } |
0 commit comments