1 /*
  2  * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 /*
 25  * @test
 26  * @bug 8266670 8291734 8296743
 27  * @summary Test expected AccessFlag's on classes.
 28  */
 29 
 30 import java.lang.annotation.*;
 31 import java.lang.reflect.*;
 32 import java.util.*;
 33 
 34 import static java.lang.reflect.AccessFlag.*;
 35 
 36 /*
 37  * Class access flags that can directly or indirectly declared in
 38  * source include:
 39  * public, private, protected, static, final, interface, abstract,
 40  * annotation, enum.
 41  *
 42  * Additionally, the access flags super and synthetic cannot be
 43  * explicitly applied.
 44  *
 45  * This test is written on top of the facilities of core reflection.
 46  *
 47  * Note that core reflection does not offer a supported mechanism to
 48  * return the Class object created from a module-info.class
 49  * file. Therefore, this test does not attempt to probe the setting of
 50  * that access flag.
 51  */
 52 @ExpectedClassFlags({PUBLIC, FINAL, SUPER})
 53 public final class ClassAccessFlagTest {
 54     public static void main(String... args) {
 55         // Top-level and auxiliary classes; i.e. non-inner classes
 56         Class<?>[] testClasses = {
 57             ClassAccessFlagTest.class,
 58             TestInterface.class,
 59             ExpectedClassFlags.class,
 60             TestOuterEnum.class
 61         };
 62         checkClasses(testClasses);
 63 
 64         // Nested classes of ClassAccessFlagTest
 65         checkClasses(ClassAccessFlagTest.class.getDeclaredClasses());
 66 
 67         checkPrimitives();
 68         checkArrays();
 69     }
 70 
 71     private static void checkClasses(Class<?>[] classes) {
 72         for (var clazz : classes) {
 73             checkClass(clazz);
 74         }
 75     }
 76 
 77     private static void checkClass(Class<?> clazz) {
 78         ExpectedClassFlags expected =
 79             clazz.getAnnotation(ExpectedClassFlags.class);
 80         if (expected != null) {
 81             Set<AccessFlag> base = EnumSet.noneOf(AccessFlag.class);
 82             Collections.addAll(base, expected.value());
 83             Set<AccessFlag> actual = clazz.accessFlags();
 84             if (!base.equals(actual)) {
 85                 throw new RuntimeException("On " + clazz +
 86                                            " expected " + base +
 87                                            " got " + actual);
 88             }
 89         }
 90     }
 91 
 92     private static void checkPrimitives() {
 93         final Class<?>[] primitives = {
 94             byte.class,
 95             int.class,
 96             long.class,
 97             short.class,
 98             char.class,
 99             float.class,
100             double.class,
101             boolean.class,
102             void.class // same access flag rules
103         };
104 
105         var expected = Set.of(PUBLIC,
106                               AccessFlag.FINAL,
107                               AccessFlag.ABSTRACT);
108 
109         for(var primClass : primitives) {
110             var accessFlags = primClass.accessFlags();
111             if (!accessFlags.equals(expected)) {
112                 throw new RuntimeException("Unexpected flags on " +
113                                            primClass);
114             }
115         }
116     }
117 
118     private static boolean containsAny(Set<AccessFlag> input,
119                                        Set<AccessFlag> test) {
120         var copy = new HashSet<>(input);
121         return copy.removeAll(test);
122     }
123 
124     private static void checkArrays() {
125         Class<?>[] accessClasses = {
126             PublicInterface.class,
127             ProtectedInterface.class,
128             PrivateInterface.class,
129         };
130 
131         for (var accessClass : accessClasses) {
132             AccessFlag accessLevel;
133             var flags = accessClass.accessFlags();
134             if (flags.contains(PUBLIC))
135                 accessLevel = PUBLIC;
136             else if (flags.contains(AccessFlag.PROTECTED))
137                 accessLevel = AccessFlag.PROTECTED;
138             else if (flags.contains(AccessFlag.PRIVATE))
139                 accessLevel = AccessFlag.PRIVATE;
140             else
141                 accessLevel = null;
142 
143             var arrayClass = accessClass.arrayType();
144             // Access modifier must match on the array type
145             if (accessLevel != null) {
146                 if (!arrayClass.accessFlags().contains(accessLevel)) {
147                     throw new RuntimeException("Mismatched access flags on " +
148                                                arrayClass);
149                 }
150             } else {
151                 if (containsAny(arrayClass.accessFlags(),
152                                 Set.of(PUBLIC,
153                                        AccessFlag.PROTECTED,
154                                        AccessFlag.PRIVATE))) {
155                     throw new RuntimeException("Unexpected access flags on " +
156                                                arrayClass);
157                 }
158             }
159         }
160 
161     }
162 
163     // inner classes and interfaces; possible flags on INNER_CLASS
164     // locations:
165     // PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, INTERFACE, ABSTRACT,
166     // SYNTHETIC, ANNOTATION, ENUM.
167 
168     @ExpectedClassFlags({PUBLIC, STATIC, INTERFACE, ABSTRACT})
169     public      interface PublicInterface {}
170     @ExpectedClassFlags({PROTECTED, STATIC, INTERFACE, ABSTRACT})
171     protected   interface ProtectedInterface {}
172     @ExpectedClassFlags({PRIVATE, STATIC, INTERFACE, ABSTRACT})
173     private     interface PrivateInterface {}
174     @ExpectedClassFlags({STATIC, INTERFACE, ABSTRACT})
175     /*package*/ interface PackageInterface {}
176 
177     @ExpectedClassFlags({FINAL})
178     /*package*/ final class TestFinalClass {}
179 
180     @ExpectedClassFlags({ABSTRACT})
181     /*package*/ abstract class TestAbstractClass {}
182 
183     @ExpectedClassFlags({STATIC, INTERFACE, ABSTRACT, ANNOTATION})
184     /*package*/ @interface TestMarkerAnnotation {}
185 
186     @ExpectedClassFlags({PUBLIC, STATIC, FINAL, ENUM})
187     public enum MetaSynVar {
188         QUUX;
189     }
190 
191     // Is there is at least one special enum constant, the enum class
192     // itself is implicitly abstract rather than final.
193     @ExpectedClassFlags({PROTECTED, STATIC, ABSTRACT, ENUM})
194     protected enum MetaSynVar2 {
195         WOMBAT{
196             @Override
197             public int foo() {return 42;}
198         };
199         public abstract int foo();
200     }
201 
202     @ExpectedClassFlags({PRIVATE, ABSTRACT})
203     private abstract class Foo {}
204 
205     @ExpectedClassFlags({STATIC, INTERFACE, ABSTRACT})
206     interface StaticTestInterface {}
207 }
208 
209 @Retention(RetentionPolicy.RUNTIME)
210 @ExpectedClassFlags({INTERFACE, ABSTRACT, ANNOTATION})
211 @interface ExpectedClassFlags {
212     AccessFlag[] value();
213 }
214 
215 @ExpectedClassFlags({INTERFACE, ABSTRACT})
216 interface TestInterface {}
217 
218 
219 @ExpectedClassFlags({FINAL, SUPER, ENUM})
220 enum TestOuterEnum {
221     INSTANCE;
222 }