1 package annotations.tests.executable; 2 3 /*>>> 4 import org.checkerframework.checker.nullness.qual.*; 5 */ 6 7 import java.io.*; 8 import java.util.*; 9 import java.lang.annotation.RetentionPolicy; 10 11 import com.sun.tools.classfile.TypeAnnotation.Position.TypePathEntryKind; 12 import com.sun.tools.javac.code.TypeAnnotationPosition; 13 14 import junit.framework.*; 15 import annotations.*; 16 import annotations.el.*; 17 import annotations.field.*; 18 import annotations.io.*; 19 20 import plume.FileIOException; 21 22 public class TestSceneLib extends TestCase { 23 LineNumberReader openPackagedIndexFile(String name) { 24 return new LineNumberReader(new InputStreamReader( 25 (InputStream) TestSceneLib.class.getResourceAsStream(name))); 26 } 27 28 static final String fooIndexContents = 29 "package:\n" + 30 "annotation @Ready: @Retention(RUNTIME)\n" + 31 "annotation @Author: @Retention(CLASS)\n" + 32 "String value\n" + 33 "class Foo:\n" + 34 "field x: @Ready\n" + 35 "method y()Z:\n" + 36 "parameter #5:\n" + 37 "type:\n" + 38 "inner-type 0, 0, 3, 2:\n" + 39 "@Author(value=\"Matt M.\")\n"; 40 41 public static AnnotationDef adAuthor 42 = Annotations.createValueAnnotationDef("Author", 43 Annotations.asRetentionClass, 44 BasicAFT.forType(String.class)); 45 46 static final AnnotationDef ready = 47 new AnnotationDef( 48 "Ready", 49 Annotations.asRetentionRuntime, 50 Annotations.noFieldTypes); 51 static final AnnotationDef readyClassRetention = 52 new AnnotationDef( 53 "Ready", 54 Annotations.asRetentionClass, 55 Annotations.noFieldTypes); 56 57 58 /** 59 * Parse indexFileContents as an annotation file, merging the results 60 * into s; the final state of s should equal expectScene. 61 */ 62 void doParseTest(String indexFileContents, 63 AScene s, 64 AScene expectScene) { 65 try { 66 IndexFileParser.parseString(indexFileContents, s); 67 } catch (IOException e) { 68 throw new RuntimeException(e); 69 } 70 if (! expectScene.equals(s)) { 71 System.err.println("expectScene does not equal s"); 72 String esu = expectScene.unparse(); 73 String su = s.unparse(); 74 if (esu.equals(su)) { 75 System.err.println("(but their printed representations are the same)"); 76 } 77 System.err.println(esu); 78 System.err.println(su); 79 } 80 assertEquals(expectScene, s); 81 } 82 83 // lazy typist! 84 AScene newScene() { 85 return new AScene(); 86 } 87 88 void doParseTest(String index, 89 /*@NonNull*/ AScene expectScene) { 90 AScene s = newScene(); 91 doParseTest(index, s, expectScene); 92 } 93 94 private Annotation createEmptyAnnotation(AnnotationDef def) { 95 return new Annotation(def, Collections.<String, Object> emptyMap()); 96 } 97 98 public void testEquals() { 99 AScene s1 = newScene(), s2 = newScene(); 100 101 s1.classes.vivify("Foo"); 102 s1.classes.vivify("Foo").fields.vivify("x"); 103 s1.classes.vivify("Foo").fields.vivify("x").tlAnnotationsHere 104 .add(createEmptyAnnotation(ready)); 105 106 s2.classes.vivify("Foo").fields.vivify("x").tlAnnotationsHere 107 .add(Annotations.aNonNull); 108 s2.classes.vivify("Foo").fields.vivify("x").tlAnnotationsHere 109 .add(createEmptyAnnotation(ready)); 110 111 assertEquals(false, s1.equals(s2)); // FIXME: why does assertion fail? 112 113 s1.classes.vivify("Foo").fields.vivify("x").tlAnnotationsHere 114 .add(Annotations.aNonNull); 115 116 assertEquals(true, s1.equals(s2)); 117 } 118 119 public void testStoreParse1() { 120 AScene s1 = newScene(); 121 122 s1.classes.vivify("Foo").fields.vivify("x").tlAnnotationsHere 123 .add(createEmptyAnnotation(ready)); 124 Annotation myAuthor = Annotations.createValueAnnotation(adAuthor, "Matt M."); 125 s1.classes.vivify("Foo"); 126 s1.classes.vivify("Foo").methods.vivify("y()Z"); 127 s1.classes.vivify("Foo").methods.vivify("y()Z").parameters.vivify(5); 128 @SuppressWarnings("unused") 129 Object dummy = 130 s1.classes.vivify("Foo").methods.vivify("y()Z").parameters.vivify(5).type; 131 @SuppressWarnings("unused") 132 Object dummy2 = 133 s1.classes.vivify("Foo").methods.vivify("y()Z").parameters.vivify(5).type.innerTypes; 134 s1.classes.vivify("Foo").methods.vivify("y()Z").parameters.vivify(5).type.innerTypes 135 .vivify(new InnerTypeLocation( 136 TypeAnnotationPosition.getTypePathFromBinary( 137 Arrays.asList(new Integer[] { 0, 0, 3, 2 })))).tlAnnotationsHere 138 .add(myAuthor); 139 140 doParseTest(fooIndexContents, s1); 141 } 142 143 private void checkConstructor(AMethod constructor) { 144 Annotation ann = ((Annotation) constructor.receiver.type.lookup("p2.D")); 145 assertEquals(Collections.singletonMap("value", "spam"), ann.fieldValues); 146 ATypeElement l = (ATypeElement) constructor.body.locals 147 .get(new LocalLocation(1, 3, 5)).type; 148 AElement i = (AElement) l.innerTypes.get(new InnerTypeLocation( 149 TypeAnnotationPosition.getTypePathFromBinary( 150 Arrays.asList(new Integer[] { 0, 0 })))); 151 assertNotNull(i.lookup("p2.C")); 152 AField l2 = 153 constructor.body.locals.get(new LocalLocation(1, 3, 6)); 154 assertNull(l2); 155 } 156 157 public void testParseRetrieve1() throws Exception { 158 LineNumberReader fr = openPackagedIndexFile("test1.jaif"); 159 AScene s1 = newScene(); 160 IndexFileParser.parse(fr, s1); 161 162 AClass foo1 = s1.classes.get("p1.Foo"); 163 assertNotNull("Didn't find foo1", foo1); 164 AClass foo = (AClass) foo1; 165 boolean sawConstructor = false; 166 for (Map.Entry<String, AMethod> me : foo.methods.entrySet()) { 167 if (me.getKey().equals("<init>(Ljava/util/Set;)V")) { 168 assertFalse(sawConstructor); 169 AMethod constructor = me.getValue(); 170 assertNotNull(constructor); 171 checkConstructor((AMethod) constructor); 172 sawConstructor = true; 173 } 174 } 175 assertTrue(sawConstructor); 176 } 177 178 class TestDefCollector extends DefCollector { 179 AnnotationDef a, b, c, d, e; 180 181 AnnotationDef f; 182 183 public TestDefCollector(AScene s) throws DefException { 184 super(s); 185 } 186 187 @Override 188 protected void visitAnnotationDef(AnnotationDef tldef) { 189 if (tldef.name.equals("p2.A")) { 190 assertNull(a); 191 a = tldef; 192 } else if (tldef.name.equals("p2.B")) { 193 assertNull(b); 194 b = tldef; 195 } else if (tldef.name.equals("p2.C")) { 196 assertNull(c); 197 c = tldef; 198 } else if (tldef.name.equals("p2.D")) { 199 assertNull(d); 200 d = tldef; 201 } else if (tldef.name.equals("p2.E")) { 202 assertNotNull(f); // should give us fields first 203 assertNull(e); 204 e = tldef; 205 } else if (tldef.name.equals("p2.F")) { 206 f = tldef; 207 } else { 208 fail(); 209 } 210 } 211 } 212 213 public void testParseRetrieveTypes() throws Exception { 214 LineNumberReader fr = openPackagedIndexFile("test1.jaif"); 215 AScene s1 = newScene(); 216 IndexFileParser.parse(fr, s1); 217 218 TestDefCollector tdc = new TestDefCollector(s1); 219 tdc.visit(); 220 assertNotNull(tdc.a); 221 assertNotNull(tdc.b); 222 assertNotNull(tdc.c); 223 assertNotNull(tdc.d); 224 assertNotNull(tdc.e); 225 assertNotNull(tdc.f); 226 227 // now look at p2.E because it has some rather complex types 228 AnnotationDef tle = (AnnotationDef) tdc.e; 229 assertEquals(RetentionPolicy.CLASS, tle.retention()); 230 AnnotationDef e = tle; 231 assertEquals(new ArrayAFT(new AnnotationAFT(tdc.a)), e.fieldTypes 232 .get("first")); 233 assertEquals(new AnnotationAFT(tdc.f), e.fieldTypes.get("second")); 234 assertEquals(new EnumAFT("Foo"), e.fieldTypes.get("third")); 235 assertEquals( 236 ClassTokenAFT.ctaft, 237 e.fieldTypes.get("fourth")); 238 assertEquals(ClassTokenAFT.ctaft, e.fieldTypes.get("fifth")); 239 240 AnnotationDef tla = (AnnotationDef) tdc.a; 241 assertEquals(RetentionPolicy.RUNTIME, tla.retention()); 242 AnnotationDef a = tla; 243 assertEquals(BasicAFT.forType(int.class), a.fieldTypes.get("value")); 244 AnnotationDef d = tdc.d; 245 assertEquals(BasicAFT.forType(String.class), d.fieldTypes.get("value")); 246 } 247 248 public void testParseRetrieveValues() throws Exception { 249 LineNumberReader fr = openPackagedIndexFile("test1.jaif"); 250 AScene s1 = newScene(); 251 IndexFileParser.parse(fr, s1); 252 253 // now look at Bar because it has some rather complex values 254 Annotation a = s1.classes.get("p1.Bar").lookup("p2.E"); 255 256 assertEquals("fooconstant", a.fieldValues.get("third")); 257 assertEquals("interface java.util.Map", a.fieldValues.get("fourth").toString()); 258 assertEquals("class [[I", a.fieldValues.get("fifth").toString()); 259 260 List<?> first = 261 (List<?>) a.fieldValues.get("first"); 262 assertEquals(2, first.size(), 2); 263 Annotation aa = (Annotation) first.get(0); 264 assertEquals("p2.A", aa.def().name); 265 assertEquals(-1, aa.fieldValues.get("value")); 266 267 Annotation a2 = 268 s1.classes.get("p1.Baz").lookup("p2.E"); 269 assertEquals("FOO_FOO", a2.fieldValues.get("third")); 270 assertEquals("class java.util.LinkedHashMap", a2.fieldValues.get("fourth").toString()); 271 assertEquals("void", a2.fieldValues.get("fifth").toString()); 272 } 273 274 void doRewriteTest(LineNumberReader r) throws Exception { 275 AScene s1 = newScene(), s2 = newScene(); 276 IndexFileParser.parse(r, s1); 277 StringWriter sbw = new StringWriter(); 278 IndexFileWriter.write(s1, sbw); 279 IndexFileParser.parseString(sbw.toString(), s2); 280 assertEquals(s1, s2); 281 } 282 283 public void testRewriteOne() throws Exception { 284 LineNumberReader fr = openPackagedIndexFile("test1.jaif"); 285 doRewriteTest(fr); 286 } 287 288 public void testRewriteTwo() throws Exception { 289 LineNumberReader fr = openPackagedIndexFile("test2.jaif"); 290 doRewriteTest(fr); 291 } 292 293 public void testConflictedDefinition() throws Exception { 294 AScene s1 = newScene(); 295 s1.classes.vivify("Foo").tlAnnotationsHere 296 .add(createEmptyAnnotation(ready)); 297 s1.classes.vivify("Bar").tlAnnotationsHere 298 .add(createEmptyAnnotation(readyClassRetention)); 299 StringWriter sbw = new StringWriter(); 300 try { 301 IndexFileWriter.write(s1, sbw); 302 fail("an exception should have been thrown"); 303 } catch (DefException de) { 304 assertEquals("Ready", de.annotationType); 305 // success 306 } 307 308 } 309 310 public void testParseErrorMissingColon() throws Exception { 311 AScene s1 = newScene(); 312 String fileContents = "package p1:\n" + "annotation @A:\n" 313 + "class Foo @A"; 314 try { 315 IndexFileParser.parseString(fileContents, s1); 316 fail(); // an exception should have been thrown 317 } catch (FileIOException e) { 318 // TODO: check line number 319 // assertEquals(3, e.line); 320 // success 321 } 322 } 323 324 public void testParseErrorMissingDefinition() throws Exception { 325 AScene s1 = newScene(); 326 String fileContents 327 = "package p1:\n" + "annotation @AIsDefined:\n" 328 + "class Foo:\n" + "@AIsDefined\n" + "@BIsNotDefined\n"; 329 try { 330 IndexFileParser.parseString(fileContents, s1); 331 fail(); // an exception should have been thrown 332 } catch (FileIOException e) { 333 // TODO: check line number 334 // assertEquals(5, e.line); 335 // success 336 } 337 } 338 339 private static Annotation getAnnotation(Set<Annotation> annos, String name) { 340 for (Annotation anno : annos) { 341 if (anno.def.name.equals(name)) { 342 return anno; 343 } 344 } 345 return null; 346 } 347 348 public void testEmptyArrayHack() throws Exception { 349 AScene scene = newScene(); 350 AClass clazz = 351 scene.classes.vivify("bar.Test"); 352 353 // One annotation with an empty array of unknown type... 354 AnnotationBuilder ab1 = 355 AnnotationFactory.saf.beginAnnotation("foo.ArrayAnno", Annotations.asRetentionClass); 356 ab1.addEmptyArrayField("array"); 357 Annotation a1 = ab1.finish(); 358 Annotation tla1 = a1; 359 360 // ... and another with an empty array of known type 361 AnnotationBuilder ab2 = 362 AnnotationFactory.saf.beginAnnotation("foo.ArrayAnno", Annotations.asRetentionClass); 363 ArrayBuilder ab2ab = ab2.beginArrayField("array", 364 new ArrayAFT(BasicAFT.forType(int.class))); 365 ab2ab.finish(); 366 Annotation a2 = ab2.finish(); 367 Annotation tla2 = a2; 368 369 // And they're both fields of another annotation to make sure that 370 // unification works recursively. 371 AnnotationBuilder ab3 = 372 AnnotationFactory.saf.beginAnnotation("foo.CombinedAnno", Annotations.asRetentionRuntime); 373 ab3.addScalarField("fieldOne", new AnnotationAFT(a1.def()), a1); 374 ab3.addScalarField("fieldTwo", new AnnotationAFT(a2.def()), a2); 375 Annotation a3 = ab3.finish(); 376 Annotation tla3 = a3; 377 378 clazz.tlAnnotationsHere.add(tla3); 379 380 StringWriter sw = new StringWriter(); 381 IndexFileWriter.write(scene, sw); 382 383 AScene sceneRead = newScene(); 384 IndexFileParser.parseString(sw.toString(), sceneRead); 385 386 // the anomaly: see second "consequence" on IndexFileWriter#write 387 assertFalse(scene.equals(sceneRead)); 388 389 AClass clazz2 = sceneRead.classes.get("bar.Test"); 390 assertNotNull(clazz2); 391 Annotation a3_2 = getAnnotation(clazz2.tlAnnotationsHere, "foo.CombinedAnno"); 392 Annotation a1_2 = (Annotation) a3_2.getFieldValue("fieldOne"); 393 Annotation a2_2 = (Annotation) a3_2.getFieldValue("fieldTwo"); 394 // now that the defs were merged, the annotations should be equal 395 assertEquals(a1_2, a2_2); 396 397 // Yet another annotation with an array of a different known type 398 AnnotationBuilder ab4 = 399 AnnotationFactory.saf.beginAnnotation("foo.ArrayAnno", Annotations.asRetentionClass); 400 ArrayBuilder ab4ab = ab4.beginArrayField("array", 401 new ArrayAFT(BasicAFT.forType(double.class))); 402 ab4ab.appendElement(5.0); 403 ab4ab.finish(); 404 Annotation a4 = ab4.finish(); 405 Annotation tla4 = a4; 406 407 // try combining unifiable _top-level_ annotations 408 AScene secondScene = newScene(); 409 AClass secondSceneClazz = 410 secondScene.classes.vivify("bar.Test"); 411 secondSceneClazz.tlAnnotationsHere.add(tla1); 412 // Oops--the keyed set gives us an exception if we try to put two 413 // different foo.ArrayAnnos on the same class! 414 AClass secondSceneClazz2 = 415 secondScene.classes.vivify("bar.Test2"); 416 secondSceneClazz2.tlAnnotationsHere.add(tla4); 417 418 // it should be legal to write this 419 StringWriter secondSW = new StringWriter(); 420 IndexFileWriter.write(secondScene, secondSW); 421 422 // add an incompatible annotation 423 AClass secondSceneClazz3 = 424 secondScene.classes.vivify("bar.Test3"); 425 secondSceneClazz3.tlAnnotationsHere.add(tla2); 426 427 // now we should get a DefException 428 StringWriter secondSceneSW2 = new StringWriter(); 429 try { 430 IndexFileWriter.write(secondScene, secondSceneSW2); 431 // we should have gotten an exception 432 fail(); 433 } catch (DefException de) { 434 assertEquals("Conflicting definition of annotation type foo.ArrayAnno", 435 de.getMessage()); 436 // success 437 } 438 } 439 440 public void testEmptyArrayIO() throws Exception { 441 // should succeed 442 String index1 = "package: annotation @Foo: @Retention(CLASS)\n unknown[] arr\n" + 443 "class Bar: @Foo(arr={})"; 444 AScene scene1 = newScene(); 445 IndexFileParser.parseString(index1, scene1); 446 447 // should reject nonempty array 448 String index2 = "package: annotation @Foo: @Retention(CLASS)\n unknown[] arr\n" + 449 "class Bar: @Foo(arr={1})"; 450 AScene scene2 = newScene(); 451 try { 452 IndexFileParser.parseString(index2, scene2); 453 // should have gotten an exception 454 fail(); 455 } catch (FileIOException e) { 456 // success 457 } 458 459 // construct a scene programmatically 460 AScene scene3 = newScene(); 461 AClass clazz3 = 462 scene3.classes.vivify("Bar"); 463 AnnotationBuilder ab = 464 AnnotationFactory.saf.beginAnnotation("Foo", Annotations.asRetentionClass); 465 ab.addEmptyArrayField("arr"); 466 Annotation a = ab.finish(); 467 Annotation tla = a; 468 clazz3.tlAnnotationsHere.add(tla); 469 470 assertEquals(scene1, scene3); 471 472 // when we write the scene out, the index file should contain the 473 // special unknown[] field type 474 StringWriter sw3 = new StringWriter(); 475 IndexFileWriter.write(scene3, sw3); 476 String index3 = sw3.toString(); 477 assertTrue(index3.indexOf("unknown[]") >= 0); 478 479 // can we read it back in and get the same thing? 480 AScene scene4 = newScene(); 481 IndexFileParser.parseString(index3, scene4); 482 assertEquals(scene3, scene4); 483 } 484 485 public void testPrune() { 486 AScene s1 = newScene(), s2 = newScene(); 487 assertTrue(s1.equals(s2)); 488 489 s1.classes.vivify("Foo"); 490 assertFalse(s1.equals(s2)); 491 492 assertTrue(s1.prune()); 493 assertTrue(s1.equals(s2)); 494 495 Annotation sa = AnnotationFactory.saf.beginAnnotation("Anno", Annotations.asRetentionClass).finish(); 496 Annotation tla = sa; 497 498 AClass clazz2 = s2.classes.vivify("Bar"); 499 clazz2.tlAnnotationsHere.add(tla); 500 501 assertFalse(s1.equals(s2)); 502 assertFalse(s2.prune()); 503 assertFalse(s1.equals(s2)); 504 } 505 506 static class MyTAST { 507 final int id; 508 MyTAST(int id) { 509 this.id = id; 510 } 511 512 MyTAST element = null; 513 MyTAST[] typeArgs = null; 514 515 static MyTAST arrayOf(int id, MyTAST element) { 516 MyTAST t = new MyTAST(id); 517 t.element = element; 518 return t; 519 } 520 521 static MyTAST parameterization(int id, MyTAST... args) { 522 MyTAST t = new MyTAST(id); 523 t.typeArgs = args; 524 return t; 525 } 526 } 527 528 /* 529 private static final AnnotationDef idAnnoDef = 530 new AnnotationDef("IdAnno", null, Collections.singletonMap( 531 "id", BasicAFT.forType(int.class))); 532 */ 533 private static final AnnotationDef idAnnoTLDef = 534 new AnnotationDef("IdAnno", Annotations.asRetentionClass, 535 Collections.singletonMap( 536 "id", BasicAFT.forType(int.class))); 537 538 static Annotation makeTLIdAnno(int id) { 539 return new Annotation(idAnnoTLDef, 540 Collections.singletonMap("id", Integer.valueOf(id))); 541 } 542 543 static class MyTASTMapper extends TypeASTMapper<MyTAST> { 544 boolean[] saw = new boolean[11]; 545 546 @Override 547 protected MyTAST getElementType(MyTAST n) { 548 return n.element; 549 } 550 551 @Override 552 protected MyTAST getTypeArgument(MyTAST n, int index) { 553 return n.typeArgs[index]; 554 } 555 556 @Override 557 protected int numTypeArguments(MyTAST n) { 558 return n.typeArgs == null ? 0 : n.typeArgs.length; 559 } 560 561 @Override 562 protected void map(MyTAST n, ATypeElement e) { 563 int nodeID = n.id; 564 if (nodeID == 10) { 565 assertTrue(e.lookup("IdAnno") == null); 566 e.tlAnnotationsHere.add(makeTLIdAnno(10)); 567 } else { 568 int annoID = (Integer) e.lookup("IdAnno").getFieldValue("id"); 569 assertEquals(nodeID, annoID); 570 } 571 assertFalse(saw[nodeID]); 572 saw[nodeID] = true; 573 } 574 } 575 576 private void assignId(ATypeElement myField, int id, 577 Integer... ls) { 578 AElement el = myField.innerTypes.vivify( 579 new InnerTypeLocation(TypeAnnotationPosition.getTypePathFromBinary(Arrays.asList(ls)))); 580 el.tlAnnotationsHere.add(makeTLIdAnno(id)); 581 } 582 583 public void testTypeASTMapper() { 584 // Construct a TAST for the type structure: 585 // 0< 3<4>[1][2], 5<6, 8[7], 9, 10> > 586 MyTAST tast = MyTAST.parameterization(0, 587 MyTAST.arrayOf(1, 588 MyTAST.arrayOf(2, 589 MyTAST.parameterization(3, 590 new MyTAST(4) 591 ) 592 ) 593 ), 594 MyTAST.parameterization(5, 595 new MyTAST(6), 596 MyTAST.arrayOf(7, 597 new MyTAST(8)), 598 new MyTAST(9), 599 new MyTAST(10) 600 ) 601 ); 602 603 // Pretend myField represents a field of the type represented by tast. 604 // We have to do this because clients are no longer allowed to create 605 // AElements directly; instead, they must vivify. 606 AElement myAField = 607 new AScene().classes.vivify("someclass").fields.vivify("somefield"); 608 ATypeElement myAFieldType = myAField.type; 609 // load it with annotations we can check against IDs 610 myAFieldType.tlAnnotationsHere.add(makeTLIdAnno(0)); 611 612 final int ARRAY = TypePathEntryKind.ARRAY.tag; 613 final int TYPE_ARGUMENT = TypePathEntryKind.TYPE_ARGUMENT.tag; 614 615 assignId(myAFieldType, 1, TYPE_ARGUMENT, 0); 616 assignId(myAFieldType, 2, TYPE_ARGUMENT, 0, ARRAY, 0); 617 assignId(myAFieldType, 3, TYPE_ARGUMENT, 0, ARRAY, 0, ARRAY, 0); 618 assignId(myAFieldType, 4, TYPE_ARGUMENT, 0, ARRAY, 0, ARRAY, 0, TYPE_ARGUMENT, 0); 619 assignId(myAFieldType, 5, TYPE_ARGUMENT, 1); 620 assignId(myAFieldType, 6, TYPE_ARGUMENT, 1, TYPE_ARGUMENT, 0); 621 assignId(myAFieldType, 7, TYPE_ARGUMENT, 1, TYPE_ARGUMENT, 1); 622 assignId(myAFieldType, 8, TYPE_ARGUMENT, 1, TYPE_ARGUMENT, 1, ARRAY, 0); 623 assignId(myAFieldType, 9, TYPE_ARGUMENT, 1, TYPE_ARGUMENT, 2); 624 // to test vivification, we don't assign 10 625 626 // now visit and make sure the ID numbers match up 627 MyTASTMapper mapper = new MyTASTMapper(); 628 mapper.traverse(tast, myAFieldType); 629 630 for (int i = 0; i < 11; i++) { 631 assertTrue(mapper.saw[i]); 632 } 633 // make sure it vivified #10 and our annotation stuck 634 AElement e10 = myAFieldType.innerTypes.get( 635 new InnerTypeLocation(TypeAnnotationPosition.getTypePathFromBinary(Arrays.asList(TYPE_ARGUMENT, 1, TYPE_ARGUMENT, 3)))); 636 assertNotNull(e10); 637 int e10aid = (Integer) e10.lookup("IdAnno").getFieldValue("id"); 638 assertEquals(e10aid, 10); 639 } 640 } 641