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 }