1 /* 2 * Copyright (C) 2017 The Android Open Source Project 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 17 package com.android.tools.metalava.stub 18 19 import com.android.tools.metalava.ARG_EXCLUDE_ALL_ANNOTATIONS 20 import com.android.tools.metalava.ARG_EXCLUDE_ANNOTATION 21 import com.android.tools.metalava.ARG_PASS_THROUGH_ANNOTATION 22 import com.android.tools.metalava.androidxNullableSource 23 import com.android.tools.metalava.libcoreNonNullSource 24 import com.android.tools.metalava.model.SUPPORT_TYPE_USE_ANNOTATIONS 25 import com.android.tools.metalava.model.text.FileFormat 26 import com.android.tools.metalava.requiresApiSource 27 import com.android.tools.metalava.supportParameterName 28 import com.android.tools.metalava.testing.KnownSourceFiles 29 import com.android.tools.metalava.testing.java 30 import org.junit.Test 31 32 @SuppressWarnings("ALL") 33 class StubsAnnotationTest : AbstractStubsTest() { 34 @Test Generate stubs for annotation typenull35 fun `Generate stubs for annotation type`() { 36 // Interface: makes sure the right modifiers etc are shown (and that "package private" 37 // methods 38 // in the interface are taken to be public etc) 39 checkStubs( 40 sourceFiles = 41 arrayOf( 42 java( 43 """ 44 package test.pkg; 45 import static java.lang.annotation.ElementType.*; 46 import java.lang.annotation.*; 47 @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) 48 @Retention(RetentionPolicy.CLASS) 49 public @interface Foo { 50 String value(); 51 } 52 """ 53 ) 54 ), 55 source = 56 """ 57 package test.pkg; 58 @SuppressWarnings({"unchecked", "deprecation", "all"}) 59 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) 60 @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE}) 61 public @interface Foo { 62 public java.lang.String value(); 63 } 64 """ 65 ) 66 } 67 68 @Test Remove Hidden Annotationsnull69 fun `Remove Hidden Annotations`() { 70 // When APIs reference annotations that are hidden, make sure the're excluded from the stubs 71 // and 72 // signature files 73 checkStubs( 74 format = FileFormat.V2, 75 sourceFiles = 76 arrayOf( 77 java( 78 """ 79 package test.pkg; 80 81 public class Foo { 82 public void foo(int p1, @MyAnnotation("publicParameterName") java.util.Map<java.lang.String, @MyAnnotation("Something") String> p2) { 83 } 84 } 85 """ 86 ), 87 java( 88 """ 89 package test.pkg; 90 import java.lang.annotation.*; 91 import static java.lang.annotation.ElementType.*; 92 import static java.lang.annotation.RetentionPolicy.SOURCE; 93 /** @hide */ 94 @SuppressWarnings("WeakerAccess") 95 @Retention(SOURCE) 96 @Target({METHOD, PARAMETER, FIELD, TYPE_USE}) 97 public @interface MyAnnotation { 98 String value(); 99 } 100 """ 101 ) 102 ), 103 api = 104 if (SUPPORT_TYPE_USE_ANNOTATIONS) { 105 """ 106 package test.pkg { 107 public class Foo { 108 ctor public Foo(); 109 method public void foo(int, java.util.Map<java.lang.String!,java.lang.String!>!); 110 } 111 } 112 """ 113 } else { 114 """ 115 package test.pkg { 116 public class Foo { 117 ctor public Foo(); 118 method public void foo(int, java.util.Map<java.lang.String,java.lang.String>); 119 } 120 } 121 """ 122 }, 123 source = 124 if (SUPPORT_TYPE_USE_ANNOTATIONS) { 125 """ 126 package test.pkg; 127 @SuppressWarnings({"unchecked", "deprecation", "all"}) 128 public class Foo { 129 public Foo() { throw new RuntimeException("Stub!"); } 130 public void foo(int p1, java.util.Map<java.lang.String, java.lang.String> p2) { throw new RuntimeException("Stub!"); } 131 } 132 """ 133 } else { 134 """ 135 package test.pkg; 136 @SuppressWarnings({"unchecked", "deprecation", "all"}) 137 public class Foo { 138 public Foo() { throw new RuntimeException("Stub!"); } 139 public void foo(int p1, java.util.Map<java.lang.String,java.lang.String> p2) { throw new RuntimeException("Stub!"); } 140 } 141 """ 142 } 143 ) 144 } 145 146 @Test Rewrite unknown nullability annotations as sdk stubsnull147 fun `Rewrite unknown nullability annotations as sdk stubs`() { 148 check( 149 format = FileFormat.V2, 150 checkCompilation = true, 151 sourceFiles = 152 arrayOf( 153 java( 154 "package my.pkg;\n" + 155 "public class String {\n" + 156 "public String(@other.NonNull char[] value) { throw new RuntimeException(\"Stub!\"); }\n" + 157 "}\n" 158 ) 159 ), 160 expectedIssues = "", 161 api = 162 """ 163 package my.pkg { 164 public class String { 165 ctor public String(@NonNull char[]); 166 } 167 } 168 """, 169 stubFiles = 170 arrayOf( 171 java( 172 """ 173 package my.pkg; 174 @SuppressWarnings({"unchecked", "deprecation", "all"}) 175 public class String { 176 public String(@android.annotation.NonNull char[] value) { throw new RuntimeException("Stub!"); } 177 } 178 """ 179 ) 180 ) 181 ) 182 } 183 184 @Test Rewrite unknown nullability annotations as doc stubsnull185 fun `Rewrite unknown nullability annotations as doc stubs`() { 186 check( 187 format = FileFormat.V2, 188 checkCompilation = true, 189 sourceFiles = 190 arrayOf( 191 java( 192 "package my.pkg;\n" + 193 "public class String {\n" + 194 "public String(@other.NonNull char[] value) { throw new RuntimeException(\"Stub!\"); }\n" + 195 "}\n" 196 ) 197 ), 198 expectedIssues = "", 199 api = 200 """ 201 package my.pkg { 202 public class String { 203 ctor public String(@NonNull char[]); 204 } 205 } 206 """, 207 docStubs = true, 208 stubFiles = 209 arrayOf( 210 java( 211 """ 212 package my.pkg; 213 @SuppressWarnings({"unchecked", "deprecation", "all"}) 214 public class String { 215 public String(@androidx.annotation.NonNull char[] value) { throw new RuntimeException("Stub!"); } 216 } 217 """ 218 ) 219 ) 220 ) 221 } 222 223 @Test Rewrite libcore annotationsnull224 fun `Rewrite libcore annotations`() { 225 check( 226 checkCompilation = true, 227 sourceFiles = 228 arrayOf( 229 java( 230 "package my.pkg;\n" + 231 "public class String {\n" + 232 "public String(char @libcore.util.NonNull [] value) { throw new RuntimeException(\"Stub!\"); }\n" + 233 "}\n" 234 ) 235 ), 236 expectedIssues = "", 237 api = 238 """ 239 package my.pkg { 240 public class String { 241 ctor public String(char[]); 242 } 243 } 244 """, 245 stubFiles = 246 if (SUPPORT_TYPE_USE_ANNOTATIONS) { 247 arrayOf( 248 java( 249 """ 250 package my.pkg; 251 @SuppressWarnings({"unchecked", "deprecation", "all"}) 252 public class String { 253 public String(char @androidx.annotation.NonNull [] value) { throw new RuntimeException("Stub!"); } 254 } 255 """ 256 ) 257 ) 258 } else { 259 arrayOf( 260 java( 261 """ 262 package my.pkg; 263 @SuppressWarnings({"unchecked", "deprecation", "all"}) 264 public class String { 265 public String(char[] value) { throw new RuntimeException("Stub!"); } 266 } 267 """ 268 ) 269 ) 270 } 271 ) 272 } 273 274 @Test Pass through libcore annotationsnull275 fun `Pass through libcore annotations`() { 276 check( 277 format = FileFormat.V2, 278 checkCompilation = true, 279 extraArguments = arrayOf(ARG_PASS_THROUGH_ANNOTATION, "libcore.util.NonNull"), 280 sourceFiles = 281 arrayOf( 282 java( 283 """ 284 package my.pkg; 285 public class String { 286 public String(@libcore.util.NonNull char[] value) { throw new RuntimeException("Stub!"); } 287 } 288 """ 289 ), 290 libcoreNonNullSource 291 ), 292 expectedIssues = "", 293 api = 294 """ 295 package libcore.util { 296 @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE}) public @interface NonNull { 297 } 298 } 299 package my.pkg { 300 public class String { 301 ctor public String(@libcore.util.NonNull char[]); 302 } 303 } 304 """, 305 stubFiles = 306 arrayOf( 307 java( 308 """ 309 package my.pkg; 310 @SuppressWarnings({"unchecked", "deprecation", "all"}) 311 public class String { 312 public String(@libcore.util.NonNull char[] value) { throw new RuntimeException("Stub!"); } 313 } 314 """ 315 ) 316 ) 317 ) 318 } 319 320 @Test Pass through multiple annotationsnull321 fun `Pass through multiple annotations`() { 322 checkStubs( 323 extraArguments = 324 arrayOf( 325 ARG_PASS_THROUGH_ANNOTATION, 326 "androidx.annotation.RequiresApi,androidx.annotation.Nullable", 327 ), 328 sourceFiles = 329 arrayOf( 330 java( 331 """ 332 package my.pkg; 333 public class MyClass { 334 @androidx.annotation.RequiresApi(21) 335 public void testMethod() {} 336 @androidx.annotation.Nullable 337 public String anotherTestMethod() { return null; } 338 } 339 """ 340 ), 341 supportParameterName, 342 requiresApiSource, 343 androidxNullableSource, 344 // Hide androidx.annotation classes. 345 KnownSourceFiles.androidxAnnotationHide, 346 ), 347 source = 348 """ 349 package my.pkg; 350 @SuppressWarnings({"unchecked", "deprecation", "all"}) 351 public class MyClass { 352 public MyClass() { throw new RuntimeException("Stub!"); } 353 @androidx.annotation.Nullable 354 public java.lang.String anotherTestMethod() { throw new RuntimeException("Stub!"); } 355 @androidx.annotation.RequiresApi(21) 356 public void testMethod() { throw new RuntimeException("Stub!"); } 357 } 358 """ 359 ) 360 } 361 362 @Test Skip RequiresApi annotationnull363 fun `Skip RequiresApi annotation`() { 364 check( 365 extraArguments = arrayOf(ARG_EXCLUDE_ANNOTATION, "androidx.annotation.RequiresApi"), 366 sourceFiles = 367 arrayOf( 368 java( 369 """ 370 package my.pkg; 371 public class MyClass { 372 @androidx.annotation.RequiresApi(21) 373 public void testMethod() {} 374 } 375 """ 376 ), 377 requiresApiSource 378 ), 379 expectedIssues = "", 380 api = 381 """ 382 package androidx.annotation { 383 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.CONSTRUCTOR}) public @interface RequiresApi { 384 method public abstract int api() default 1; 385 method public abstract int value() default 1; 386 } 387 } 388 package my.pkg { 389 public class MyClass { 390 ctor public MyClass(); 391 method public void testMethod(); 392 } 393 } 394 """, 395 stubFiles = 396 arrayOf( 397 java( 398 """ 399 package my.pkg; 400 @SuppressWarnings({"unchecked", "deprecation", "all"}) 401 public class MyClass { 402 public MyClass() { throw new RuntimeException("Stub!"); } 403 public void testMethod() { throw new RuntimeException("Stub!"); } 404 } 405 """ 406 ) 407 ) 408 ) 409 } 410 411 @Test Annotation default valuesnull412 fun `Annotation default values`() { 413 checkStubs( 414 sourceFiles = 415 arrayOf( 416 java( 417 """ 418 package test.pkg; 419 420 import java.lang.annotation.ElementType; 421 import java.lang.annotation.Retention; 422 import java.lang.annotation.RetentionPolicy; 423 import java.lang.annotation.Target; 424 425 import static java.lang.annotation.RetentionPolicy.SOURCE; 426 427 /** 428 * This annotation can be used to mark fields and methods to be dumped by 429 * the view server. Only non-void methods with no arguments can be annotated 430 * by this annotation. 431 */ 432 @Target({ElementType.FIELD, ElementType.METHOD}) 433 @Retention(RetentionPolicy.RUNTIME) 434 public @interface ExportedProperty { 435 /** 436 * When resolveId is true, and if the annotated field/method return value 437 * is an int, the value is converted to an Android's resource name. 438 * 439 * @return true if the property's value must be transformed into an Android 440 * resource name, false otherwise 441 */ 442 boolean resolveId() default false; 443 String prefix() default ""; 444 String category() default ""; 445 boolean formatToHexString() default false; 446 boolean hasAdjacentMapping() default false; 447 Class<? extends Number> myCls() default Integer.class; 448 char[] letters1() default {}; 449 char[] letters2() default {'a', 'b', 'c'}; 450 double from() default Double.NEGATIVE_INFINITY; 451 double fromWithCast() default (double)Float.NEGATIVE_INFINITY; 452 InnerAnnotation value() default @InnerAnnotation; 453 char letter() default 'a'; 454 int integer() default 1; 455 long large_integer() default 1L; 456 float floating() default 1.0f; 457 double large_floating() default 1.0; 458 byte small() default 1; 459 short medium() default 1; 460 int math() default 1+2*3; 461 @InnerAnnotation 462 int unit() default PX; 463 int DP = 0; 464 int PX = 1; 465 int SP = 2; 466 @Retention(SOURCE) 467 @interface InnerAnnotation { 468 } 469 } 470 """ 471 ) 472 ), 473 warnings = "", 474 api = 475 """ 476 package test.pkg { 477 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) public @interface ExportedProperty { 478 method public abstract String category() default ""; 479 method public abstract float floating() default 1.0f; 480 method public abstract boolean formatToHexString() default false; 481 method public abstract double from() default java.lang.Double.NEGATIVE_INFINITY; 482 method public abstract double fromWithCast() default (double)java.lang.Float.NEGATIVE_INFINITY; 483 method public abstract boolean hasAdjacentMapping() default false; 484 method public abstract int integer() default 1; 485 method public abstract double large_floating() default 1.0; 486 method public abstract long large_integer() default 1L; 487 method public abstract char letter() default 'a'; 488 method public abstract char[] letters1() default {}; 489 method public abstract char[] letters2() default {'a', 'b', 'c'}; 490 method public abstract int math() default 7; 491 method public abstract short medium() default 1; 492 method public abstract Class<? extends java.lang.Number!> myCls() default java.lang.Integer.class; 493 method public abstract String prefix() default ""; 494 method public abstract boolean resolveId() default false; 495 method public abstract byte small() default 1; 496 method @test.pkg.ExportedProperty.InnerAnnotation public abstract int unit() default test.pkg.ExportedProperty.PX; 497 method public abstract test.pkg.ExportedProperty.InnerAnnotation value() default @test.pkg.ExportedProperty.InnerAnnotation; 498 field public static final int DP = 0; // 0x0 499 field public static final int PX = 1; // 0x1 500 field public static final int SP = 2; // 0x2 501 } 502 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface ExportedProperty.InnerAnnotation { 503 } 504 } 505 """, 506 source = 507 """ 508 package test.pkg; 509 /** 510 * This annotation can be used to mark fields and methods to be dumped by 511 * the view server. Only non-void methods with no arguments can be annotated 512 * by this annotation. 513 */ 514 @SuppressWarnings({"unchecked", "deprecation", "all"}) 515 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) 516 @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD}) 517 public @interface ExportedProperty { 518 public java.lang.String category() default ""; 519 public float floating() default 1.0f; 520 public boolean formatToHexString() default false; 521 public double from() default java.lang.Double.NEGATIVE_INFINITY; 522 public double fromWithCast() default (double)java.lang.Float.NEGATIVE_INFINITY; 523 public boolean hasAdjacentMapping() default false; 524 public int integer() default 1; 525 public double large_floating() default 1.0; 526 public long large_integer() default 1L; 527 public char letter() default 'a'; 528 public char[] letters1() default {}; 529 public char[] letters2() default {'a', 'b', 'c'}; 530 public int math() default 7; 531 public short medium() default 1; 532 public java.lang.Class<? extends java.lang.Number> myCls() default java.lang.Integer.class; 533 public java.lang.String prefix() default ""; 534 /** 535 * When resolveId is true, and if the annotated field/method return value 536 * is an int, the value is converted to an Android's resource name. 537 * 538 * @return true if the property's value must be transformed into an Android 539 * resource name, false otherwise 540 */ 541 public boolean resolveId() default false; 542 public byte small() default 1; 543 public int unit() default test.pkg.ExportedProperty.PX; 544 public test.pkg.ExportedProperty.InnerAnnotation value() default @test.pkg.ExportedProperty.InnerAnnotation; 545 public static final int DP = 0; // 0x0 546 public static final int PX = 1; // 0x1 547 public static final int SP = 2; // 0x2 548 @SuppressWarnings({"unchecked", "deprecation", "all"}) 549 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) 550 public static @interface InnerAnnotation { 551 } 552 } 553 """ 554 ) 555 } 556 557 @Test Annotation metadata in stubsnull558 fun `Annotation metadata in stubs`() { 559 checkStubs( 560 skipEmitPackages = emptyList(), 561 sourceFiles = 562 arrayOf( 563 java( 564 """ 565 package com.android.metalava.test; 566 567 import java.lang.annotation.*; 568 569 @Target(ElementType.METHOD) 570 @Retention(RetentionPolicy.SOURCE) 571 public @interface MyAnnotation { 572 } 573 """ 574 ) 575 ), 576 warnings = "", 577 source = 578 """ 579 package com.android.metalava.test; 580 @SuppressWarnings({"unchecked", "deprecation", "all"}) 581 @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) 582 @java.lang.annotation.Target(java.lang.annotation.ElementType.METHOD) 583 public @interface MyAnnotation { 584 } 585 """ 586 ) 587 } 588 589 @Test Ensure we emit both deprecated javadoc and annotation with exclude-all-annotationsnull590 fun `Ensure we emit both deprecated javadoc and annotation with exclude-all-annotations`() { 591 check( 592 extraArguments = arrayOf(ARG_EXCLUDE_ALL_ANNOTATIONS), 593 sourceFiles = 594 arrayOf( 595 java( 596 """ 597 package test.pkg; 598 public class Foo { 599 /** 600 * @deprecated Use checkPermission instead. 601 */ 602 @Deprecated 603 protected boolean inClass(String name) { 604 return false; 605 } 606 } 607 """ 608 ) 609 ), 610 stubFiles = 611 arrayOf( 612 java( 613 """ 614 package test.pkg; 615 @SuppressWarnings({"unchecked", "deprecation", "all"}) 616 public class Foo { 617 public Foo() { throw new RuntimeException("Stub!"); } 618 /** 619 * @deprecated Use checkPermission instead. 620 */ 621 @Deprecated 622 protected boolean inClass(java.lang.String name) { throw new RuntimeException("Stub!"); } 623 } 624 """ 625 ) 626 ) 627 ) 628 } 629 630 @Test Ensure we emit runtime and deprecated annotations in stubs with exclude-annotationsnull631 fun `Ensure we emit runtime and deprecated annotations in stubs with exclude-annotations`() { 632 check( 633 extraArguments = arrayOf(ARG_EXCLUDE_ALL_ANNOTATIONS), 634 sourceFiles = 635 arrayOf( 636 java( 637 """ 638 package test.pkg; 639 /** @deprecated */ 640 @MySourceRetentionAnnotation 641 @MyClassRetentionAnnotation 642 @MyRuntimeRetentionAnnotation 643 @Deprecated 644 public class Foo { 645 private Foo() {} 646 } 647 """ 648 ), 649 java( 650 """ 651 package test.pkg; 652 import java.lang.annotation.Retention; 653 import static java.lang.annotation.RetentionPolicy.SOURCE; 654 @Retention(SOURCE) 655 public @interface MySourceRetentionAnnotation { 656 } 657 """ 658 ), 659 java( 660 """ 661 package test.pkg; 662 import java.lang.annotation.Retention; 663 import static java.lang.annotation.RetentionPolicy.CLASS; 664 @Retention(CLASS) 665 public @interface MyClassRetentionAnnotation { 666 } 667 """ 668 ), 669 java( 670 """ 671 package test.pkg; 672 import java.lang.annotation.Retention; 673 import static java.lang.annotation.RetentionPolicy.RUNTIME; 674 @Retention(RUNTIME) 675 public @interface MyRuntimeRetentionAnnotation { 676 } 677 """ 678 ) 679 ), 680 stubFiles = 681 arrayOf( 682 java( 683 """ 684 package test.pkg; 685 /** @deprecated */ 686 @SuppressWarnings({"unchecked", "deprecation", "all"}) 687 @Deprecated 688 @test.pkg.MyRuntimeRetentionAnnotation 689 public class Foo { 690 @Deprecated 691 Foo() { throw new RuntimeException("Stub!"); } 692 } 693 """ 694 ) 695 ) 696 ) 697 } 698 699 @Test Ensure we include class and runtime and not source annotations in stubs with include-annotationsnull700 fun `Ensure we include class and runtime and not source annotations in stubs with include-annotations`() { 701 check( 702 extraArguments = arrayOf("--include-annotations"), 703 sourceFiles = 704 arrayOf( 705 java( 706 """ 707 package test.pkg; 708 /** @deprecated */ 709 @MySourceRetentionAnnotation 710 @MyClassRetentionAnnotation 711 @MyRuntimeRetentionAnnotation 712 @Deprecated 713 public class Foo { 714 private Foo() {} 715 protected int foo; 716 public void bar(); 717 } 718 """ 719 ), 720 java( 721 """ 722 package test.pkg; 723 import java.lang.annotation.Retention; 724 import static java.lang.annotation.RetentionPolicy.SOURCE; 725 @Retention(SOURCE) 726 public @interface MySourceRetentionAnnotation { 727 } 728 """ 729 ), 730 java( 731 """ 732 package test.pkg; 733 import java.lang.annotation.Retention; 734 import static java.lang.annotation.RetentionPolicy.CLASS; 735 @Retention(CLASS) 736 public @interface MyClassRetentionAnnotation { 737 } 738 """ 739 ), 740 java( 741 """ 742 package test.pkg; 743 import java.lang.annotation.Retention; 744 import static java.lang.annotation.RetentionPolicy.RUNTIME; 745 @Retention(RUNTIME) 746 public @interface MyRuntimeRetentionAnnotation { 747 } 748 """ 749 ) 750 ), 751 stubFiles = 752 arrayOf( 753 java( 754 """ 755 package test.pkg; 756 /** @deprecated */ 757 @SuppressWarnings({"unchecked", "deprecation", "all"}) 758 @Deprecated 759 @test.pkg.MyClassRetentionAnnotation 760 @test.pkg.MyRuntimeRetentionAnnotation 761 public class Foo { 762 @Deprecated 763 Foo() { throw new RuntimeException("Stub!"); } 764 @Deprecated 765 public void bar() { throw new RuntimeException("Stub!"); } 766 @Deprecated protected int foo; 767 } 768 """ 769 ) 770 ) 771 ) 772 } 773 774 @Test Annotation nested rewritingnull775 fun `Annotation nested rewriting`() { 776 checkStubs( 777 sourceFiles = 778 arrayOf( 779 java( 780 """ 781 package test.pkg; 782 783 import android.view.Gravity; 784 785 public class ActionBar { 786 @ViewDebug.ExportedProperty(category = "layout", mapping = { 787 @ViewDebug.IntToString(from = -1, to = "NONE"), 788 @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), 789 @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), 790 @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"), 791 }) 792 public int gravity = Gravity.NO_GRAVITY; 793 } 794 """ 795 ), 796 java( 797 """ 798 package android.view; 799 800 public class Gravity { 801 public static final int NO_GRAVITY = 0; 802 public static final int TOP = 1; 803 public static final int BOTTOM = 2; 804 } 805 """ 806 ), 807 java( 808 """ 809 package test.pkg; 810 811 import java.lang.annotation.ElementType; 812 import java.lang.annotation.Retention; 813 import java.lang.annotation.RetentionPolicy; 814 import java.lang.annotation.Target; 815 816 public class ViewDebug { 817 @Target({ElementType.FIELD, ElementType.METHOD}) 818 @Retention(RetentionPolicy.RUNTIME) 819 public @interface ExportedProperty { 820 boolean resolveId() default false; 821 IntToString[] mapping() default {}; 822 IntToString[] indexMapping() default {}; 823 boolean deepExport() default false; 824 String prefix() default ""; 825 String category() default ""; 826 boolean formatToHexString() default false; 827 boolean hasAdjacentMapping() default false; 828 } 829 @Target({ElementType.TYPE}) 830 @Retention(RetentionPolicy.RUNTIME) 831 public @interface IntToString { 832 int from(); 833 String to(); 834 } 835 } 836 """ 837 ) 838 ), 839 source = 840 """ 841 package test.pkg; 842 @SuppressWarnings({"unchecked", "deprecation", "all"}) 843 public class ActionBar { 844 public ActionBar() { throw new RuntimeException("Stub!"); } 845 @test.pkg.ViewDebug.ExportedProperty(category="layout", mapping={@test.pkg.ViewDebug.IntToString(from=0xffffffff, to="NONE"), @test.pkg.ViewDebug.IntToString(from=android.view.Gravity.NO_GRAVITY, to="NONE"), @test.pkg.ViewDebug.IntToString(from=android.view.Gravity.TOP, to="TOP"), @test.pkg.ViewDebug.IntToString(from=android.view.Gravity.BOTTOM, to="BOTTOM")}) public int gravity = 0; // 0x0 846 } 847 """ 848 ) 849 } 850 } 851