1 /*
  2  * Copyright (c) 2022, 2026, 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  * @requires !java.enablePreview
 28  * @summary Test expected AccessFlag's on classes.
 29  */
 30 
 31 import java.lang.annotation.*;
 32 import java.lang.reflect.*;
 33 import java.util.*;
 34 
 35 import static java.lang.reflect.AccessFlag.*;
 36 
 37 /*
 38  * Class access flags that can directly or indirectly declared in
 39  * source include:
 40  * public, private, protected, static, final, interface, abstract,
 41  * annotation, enum.
 42  *
 43  * Additionally, the access flags super and synthetic cannot be
 44  * explicitly applied.
 45  *
 46  * This test is written on top of the facilities of core reflection.
 47  *
 48  * Note that core reflection does not offer a supported mechanism to
 49  * return the Class object created from a module-info.class
 50  * file. Therefore, this test does not attempt to probe the setting of
 51  * that access flag.
 52  */
 53 @ExpectedClassFlags({PUBLIC, FINAL, SUPER})
 54 public final class ClassAccessFlagTest {
 55     public static void main(String... args) {
 56         // Top-level and auxiliary classes; i.e. non-inner classes
 57         Class<?>[] testClasses = {
 58             ClassAccessFlagTest.class,
 59             TestInterface.class,
 60             ExpectedClassFlags.class,
 61             TestOuterEnum.class
 62         };
 63         checkClasses(testClasses);
 64 
 65         // Nested classes of ClassAccessFlagTest
 66         checkClasses(ClassAccessFlagTest.class.getDeclaredClasses());
 67 
 68         checkPrimitives();
 69         checkArrays();
 70     }
 71 
 72     private static void checkClasses(Class<?>[] classes) {
 73         for (var clazz : classes) {
 74             checkClass(clazz);
 75         }
 76     }
 77 
 78     private static void checkClass(Class<?> clazz) {
 79         ExpectedClassFlags expected =
 80             clazz.getAnnotation(ExpectedClassFlags.class);
 81         if (expected != null) {
 82             Set<AccessFlag> base = EnumSet.noneOf(AccessFlag.class);
 83             Collections.addAll(base, expected.value());
 84             Set<AccessFlag> actual = clazz.accessFlags();
 85             if (!base.equals(actual)) {
 86                 throw new RuntimeException("On " + clazz +
 87                                            " expected " + base +
 88                                            " got " + actual);
 89             }
 90         }
 91     }
 92 
 93     private static void checkPrimitives() {
 94         final Class<?>[] primitives = {
 95             byte.class,
 96             int.class,
 97             long.class,
 98             short.class,
 99             char.class,
100             float.class,
101             double.class,
102             boolean.class,
103             void.class // same access flag rules
104         };
105 
106         var expected = Set.of(PUBLIC,
107                               AccessFlag.FINAL,
108                               AccessFlag.ABSTRACT);
109 
110         for(var primClass : primitives) {
111             var accessFlags = primClass.accessFlags();
112             if (!accessFlags.equals(expected)) {
113                 throw new RuntimeException("Unexpected flags on " +
114                                            primClass);
115             }
116         }
117     }
118 
119     private static boolean containsAny(Set<AccessFlag> input,
120                                        Set<AccessFlag> test) {
121         var copy = new HashSet<>(input);
122         return copy.removeAll(test);
123     }
124 
125     private static void checkArrays() {
126         Class<?>[] accessClasses = {
127             PublicInterface.class,
128             ProtectedInterface.class,
129             PrivateInterface.class,
130         };
131 
132         for (var accessClass : accessClasses) {
133             AccessFlag accessLevel;
134             var flags = accessClass.accessFlags();
135             if (flags.contains(PUBLIC))
136                 accessLevel = PUBLIC;
137             else if (flags.contains(AccessFlag.PROTECTED))
138                 accessLevel = AccessFlag.PROTECTED;
139             else if (flags.contains(AccessFlag.PRIVATE))
140                 accessLevel = AccessFlag.PRIVATE;
141             else
142                 accessLevel = null;
143 
144             var arrayClass = accessClass.arrayType();
145             // Access modifier must match on the array type
146             if (accessLevel != null) {
147                 if (!arrayClass.accessFlags().contains(accessLevel)) {
148                     throw new RuntimeException("Mismatched access flags on " +
149                                                arrayClass);
150                 }
151             } else {
152                 if (containsAny(arrayClass.accessFlags(),
153                                 Set.of(PUBLIC,
154                                        AccessFlag.PROTECTED,
155                                        AccessFlag.PRIVATE))) {
156                     throw new RuntimeException("Unexpected access flags on " +
157                                                arrayClass);
158                 }
159             }
160         }
161 
162     }
163 
164     // inner classes and interfaces; possible flags on INNER_CLASS
165     // locations:
166     // PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, INTERFACE, ABSTRACT,
167     // SYNTHETIC, ANNOTATION, ENUM.
168 
169     @ExpectedClassFlags({PUBLIC, STATIC, INTERFACE, ABSTRACT})
170     public      interface PublicInterface {}
171     @ExpectedClassFlags({PROTECTED, STATIC, INTERFACE, ABSTRACT})
172     protected   interface ProtectedInterface {}
173     @ExpectedClassFlags({PRIVATE, STATIC, INTERFACE, ABSTRACT})
174     private     interface PrivateInterface {}
175     @ExpectedClassFlags({STATIC, INTERFACE, ABSTRACT})
176     /*package*/ interface PackageInterface {}
177 
178     @ExpectedClassFlags({FINAL})
179     /*package*/ final class TestFinalClass {}
180 
181     @ExpectedClassFlags({ABSTRACT})
182     /*package*/ abstract class TestAbstractClass {}
183 
184     @ExpectedClassFlags({STATIC, INTERFACE, ABSTRACT, ANNOTATION})
185     /*package*/ @interface TestMarkerAnnotation {}
186 
187     @ExpectedClassFlags({PUBLIC, STATIC, FINAL, ENUM})
188     public enum MetaSynVar {
189         QUUX;
190     }
191 
192     // Is there is at least one special enum constant, the enum class
193     // itself is implicitly abstract rather than final.
194     @ExpectedClassFlags({PROTECTED, STATIC, ABSTRACT, ENUM})
195     protected enum MetaSynVar2 {
196         WOMBAT{
197             @Override
198             public int foo() {return 42;}
199         };
200         public abstract int foo();
201     }
202 
203     @ExpectedClassFlags({PRIVATE, ABSTRACT})
204     private abstract class Foo {}
205 
206     @ExpectedClassFlags({STATIC, INTERFACE, ABSTRACT})
207     interface StaticTestInterface {}
208 }
209 
210 @Retention(RetentionPolicy.RUNTIME)
211 @ExpectedClassFlags({INTERFACE, ABSTRACT, ANNOTATION})
212 @interface ExpectedClassFlags {
213     AccessFlag[] value();
214 }
215 
216 @ExpectedClassFlags({INTERFACE, ABSTRACT})
217 interface TestInterface {}
218 
219 
220 @ExpectedClassFlags({FINAL, SUPER, ENUM})
221 enum TestOuterEnum {
222     INSTANCE;
223 }