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 }