/* * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test * @bug 8266670 8291734 8296743 * @summary Test expected AccessFlag's on classes. */ import java.lang.annotation.*; import java.lang.reflect.*; import java.util.*; /* * Class access flags that can directly or indirectly declared in * source include: * public, private, protected, static, final, interface, abstract, * annotation, enum. * * Additionally, the access flags super and synthetic cannot be * explicitly applied. * * This test is written on top of the facilities of core reflection. * * Note that core reflection does not offer a supported mechanism to * return the Class object created from a module-info.class * file. Therefore, this test does not attempt to probe the setting of * that access flag. */ @ExpectedClassFlags("[PUBLIC, FINAL, SUPER]") public final class ClassAccessFlagTest { public static void main(String... args) { // Top-level and auxiliary classes; i.e. non-inner classes Class[] testClasses = { ClassAccessFlagTest.class, TestInterface.class, ExpectedClassFlags.class, TestOuterEnum.class }; checkClasses(testClasses); // Nested classes of ClassAccessFlagTest checkClasses(ClassAccessFlagTest.class.getDeclaredClasses()); checkPrimitives(); checkArrays(); } private static void checkClasses(Class[] classes) { for (var clazz : classes) { checkClass(clazz); } } private static void checkClass(Class clazz) { ExpectedClassFlags expected = clazz.getAnnotation(ExpectedClassFlags.class); if (expected != null) { String actual = clazz.accessFlags().toString(); if (!expected.value().equals(actual)) { throw new RuntimeException("On " + clazz + " expected " + expected.value() + " got " + actual); } } } private static void checkPrimitives() { final Class[] primitives = { byte.class, int.class, long.class, short.class, char.class, float.class, double.class, boolean.class, void.class // same access flag rules }; var expected = Set.of(AccessFlag.PUBLIC, AccessFlag.FINAL, AccessFlag.ABSTRACT); for(var primClass : primitives) { var accessFlags = primClass.accessFlags(); if (!accessFlags.equals(expected)) { throw new RuntimeException("Unexpected flags on " + primClass); } } } private static boolean containsAny(Set input, Set test) { var copy = new HashSet<>(input); return copy.removeAll(test); } private static void checkArrays() { Class[] accessClasses = { PublicInterface.class, ProtectedInterface.class, PrivateInterface.class, }; for (var accessClass : accessClasses) { AccessFlag accessLevel; var flags = accessClass.accessFlags(); if (flags.contains(AccessFlag.PUBLIC)) accessLevel = AccessFlag.PUBLIC; else if (flags.contains(AccessFlag.PROTECTED)) accessLevel = AccessFlag.PROTECTED; else if (flags.contains(AccessFlag.PRIVATE)) accessLevel = AccessFlag.PRIVATE; else accessLevel = null; var arrayClass = accessClass.arrayType(); // Access modifier must match on the array type if (accessLevel != null) { if (!arrayClass.accessFlags().contains(accessLevel)) { throw new RuntimeException("Mismatched access flags on " + arrayClass); } } else { if (containsAny(arrayClass.accessFlags(), Set.of(AccessFlag.PUBLIC, AccessFlag.PROTECTED, AccessFlag.PRIVATE))) { throw new RuntimeException("Unexpected access flags on " + arrayClass); } } } } // inner classes and interfaces; possible flags on INNER_CLASS // locations: // PUBLIC, PRIVATE, PROTECTED, STATIC, FINAL, INTERFACE, ABSTRACT, // SYNTHETIC, ANNOTATION, ENUM. @ExpectedClassFlags("[PUBLIC, STATIC, INTERFACE, ABSTRACT]") public interface PublicInterface {} @ExpectedClassFlags("[PROTECTED, STATIC, INTERFACE, ABSTRACT]") protected interface ProtectedInterface {} @ExpectedClassFlags("[PRIVATE, STATIC, INTERFACE, ABSTRACT]") private interface PrivateInterface {} @ExpectedClassFlags("[STATIC, INTERFACE, ABSTRACT]") /*package*/ interface PackageInterface {} @ExpectedClassFlags("[FINAL]") /*package*/ final class TestFinalClass {} @ExpectedClassFlags("[ABSTRACT]") /*package*/ abstract class TestAbstractClass {} @ExpectedClassFlags("[STATIC, INTERFACE, ABSTRACT, ANNOTATION]") /*package*/ @interface TestMarkerAnnotation {} @ExpectedClassFlags("[PUBLIC, STATIC, FINAL, ENUM]") public enum MetaSynVar { QUUX; } // Is there is at least one special enum constant, the enum class // itself is implicitly abstract rather than final. @ExpectedClassFlags("[PROTECTED, STATIC, ABSTRACT, ENUM]") protected enum MetaSynVar2 { WOMBAT{ @Override public int foo() {return 42;} }; public abstract int foo(); } @ExpectedClassFlags("[PRIVATE, ABSTRACT]") private abstract class Foo {} @ExpectedClassFlags("[STATIC, INTERFACE, ABSTRACT]") interface StaticTestInterface {} } @Retention(RetentionPolicy.RUNTIME) @ExpectedClassFlags("[INTERFACE, ABSTRACT, ANNOTATION]") @interface ExpectedClassFlags { String value(); } @ExpectedClassFlags("[INTERFACE, ABSTRACT]") interface TestInterface {} @ExpectedClassFlags("[FINAL, SUPER, ENUM]") enum TestOuterEnum { INSTANCE; }