/* * Copyright (c) 2013, 2024, 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 6298888 6992705 8161500 6304578 8322878 * @summary Check Class.toGenericString() */ import java.lang.reflect.*; import java.lang.annotation.*; import java.util.*; @ExpectedGenericString("public class GenericStringTest") public class GenericStringTest { public Map[] mixed = null; public Map[][] mixed2 = null; private static record PlatformTestCase(Class clazz, String expected) {} public static void main(String... args) throws ReflectiveOperationException { int failures = 0; String[][] nested = {{""}}; int[][] intArray = {{1}}; List platformTestCases = List.of(new PlatformTestCase(int.class, "int"), new PlatformTestCase(void.class, "void"), new PlatformTestCase(args.getClass(), "java.lang.String[]"), new PlatformTestCase(nested.getClass(), "java.lang.String[][]"), new PlatformTestCase(intArray.getClass(), "int[][]"), new PlatformTestCase(java.lang.Enum.class, "public abstract class java.lang.Enum>"), new PlatformTestCase(java.util.Map.class, "public abstract interface java.util.Map"), new PlatformTestCase(java.util.EnumMap.class, "public class java.util.EnumMap,V>"), new PlatformTestCase(java.util.EventListenerProxy.class, "public abstract class java.util.EventListenerProxy"), // Sealed class new PlatformTestCase(java.lang.ref.Reference.class, "public abstract sealed class java.lang.ref.Reference"), // non-sealed class new PlatformTestCase(java.lang.ref.WeakReference.class, "public non-sealed class java.lang.ref.WeakReference") ); for (PlatformTestCase platformTestCase : platformTestCases) { failures += checkToGenericString(platformTestCase.clazz, platformTestCase.expected); } Field f = GenericStringTest.class.getDeclaredField("mixed"); // The expected value includes "" rather than // "<...String,...Integer>" since the Class object rather than // Type objects is being queried. failures += checkToGenericString(f.getType(), "java.util.Map[]"); f = GenericStringTest.class.getDeclaredField("mixed2"); failures += checkToGenericString(f.getType(), "java.util.Map[][]"); for(Class clazz : List.of(GenericStringTest.class, AnInterface.class, LocalMap.class, AnEnum.class, AnotherEnum.class, SealedRootClass.class, SealedRootClass.ChildA.class, SealedRootClass.ChildB.class, SealedRootClass.ChildB.GrandChildAB.class, SealedRootClass.ChildC.class, SealedRootClass.ChildC.GrandChildACA.class, SealedRootClass.ChildC.GrandChildACB.class, SealedRootClass.ChildC.GrandChildACC.class, SealedRootClass.ChildC.GrandChildACC.GreatGrandChildACCA.class, SealedRootClass.ChildC.GrandChildACC.GreatGrandChildACCB.class, SealedRootIntf.class, SealedRootIntf.ChildA.class, SealedRootIntf.ChildB.class, SealedRootIntf.ChildB.GrandChildAB.class, SealedRootIntf.ChildC.class, SealedRootIntf.ChildC.GrandChildACA.class, SealedRootIntf.ChildC.GrandChildACB.class, SealedRootIntf.ChildC.GrandChildACC.class, SealedRootIntf.ChildC.GrandChildACC.GreatGrandChildACCA.class, SealedRootIntf.ChildC.GrandChildACC.GreatGrandChildACCB.class, SealedRootIntf.IntfA.class, SealedRootIntf.IntfA.IntfAImpl.class, SealedRootIntf.IntfB.class, SealedRootIntf.IntfB.IntfAImpl.class)) { failures += checkToGenericString(clazz, clazz.getAnnotation(ExpectedGenericString.class).value()); } if (failures > 0) { throw new RuntimeException(); } } private static int checkToGenericString(Class clazz, String expected) { String genericString = clazz.toGenericString(); if (!genericString.equals(expected)) { System.err.printf("Unexpected Class.toGenericString output; expected %n\t'%s',%n got %n\t'%s'.%n", expected, genericString); return 1; } else return 0; } } @Retention(RetentionPolicy.RUNTIME) @interface ExpectedGenericString { String value(); } @ExpectedGenericString("abstract interface AnInterface") strictfp interface AnInterface {} @ExpectedGenericString("abstract interface LocalMap") interface LocalMap {} @ExpectedGenericString("final enum AnEnum") enum AnEnum { FOO; } // If an enum class has a specialized enum constant, that is compiled // by having the enum class as being sealed rather than final. See JLS // 8.9 Enum Classes. @ExpectedGenericString("sealed enum AnotherEnum") enum AnotherEnum { BAR{}; } // Test cases for sealed/non-sealed _class_ hierarchy. @ExpectedGenericString("sealed class SealedRootClass") sealed class SealedRootClass permits SealedRootClass.ChildA, SealedRootClass.ChildB, SealedRootClass.ChildC { @ExpectedGenericString("final class SealedRootClass$ChildA") final class ChildA extends SealedRootClass {} @ExpectedGenericString("sealed class SealedRootClass$ChildB") sealed class ChildB extends SealedRootClass permits SealedRootClass.ChildB.GrandChildAB { @ExpectedGenericString("final class SealedRootClass$ChildB$GrandChildAB") final class GrandChildAB extends ChildB {} } @ExpectedGenericString("non-sealed class SealedRootClass$ChildC") non-sealed class ChildC extends SealedRootClass { // The subclasses of ChildC do not themselves have to be // sealed, non-sealed, or final. @ExpectedGenericString("class SealedRootClass$ChildC$GrandChildACA") class GrandChildACA extends ChildC {} @ExpectedGenericString("final class SealedRootClass$ChildC$GrandChildACB") final class GrandChildACB extends ChildC {} @ExpectedGenericString("sealed class SealedRootClass$ChildC$GrandChildACC") sealed class GrandChildACC extends ChildC { @ExpectedGenericString("final class SealedRootClass$ChildC$GrandChildACC$GreatGrandChildACCA") final class GreatGrandChildACCA extends GrandChildACC {} @ExpectedGenericString("non-sealed class SealedRootClass$ChildC$GrandChildACC$GreatGrandChildACCB") non-sealed class GreatGrandChildACCB extends GrandChildACC {} } } } // Test cases for sealed/non-sealed _interface_ hierarchy. @ExpectedGenericString("abstract sealed interface SealedRootIntf") sealed interface SealedRootIntf permits SealedRootIntf.ChildA, SealedRootIntf.ChildB, SealedRootIntf.ChildC, SealedRootIntf.IntfA, SealedRootIntf.IntfB { @ExpectedGenericString("public static final class SealedRootIntf$ChildA") final class ChildA implements SealedRootIntf {} @ExpectedGenericString("public static sealed class SealedRootIntf$ChildB") sealed class ChildB implements SealedRootIntf permits SealedRootIntf.ChildB.GrandChildAB { @ExpectedGenericString("final class SealedRootIntf$ChildB$GrandChildAB") final class GrandChildAB extends ChildB {} } @ExpectedGenericString("public static non-sealed class SealedRootIntf$ChildC") non-sealed class ChildC implements SealedRootIntf { // The subclasses of ChildC do not themselves have to be // sealed, non-sealed, or final. @ExpectedGenericString("class SealedRootIntf$ChildC$GrandChildACA") class GrandChildACA extends ChildC {} @ExpectedGenericString("final class SealedRootIntf$ChildC$GrandChildACB") final class GrandChildACB extends ChildC {} @ExpectedGenericString("sealed class SealedRootIntf$ChildC$GrandChildACC") sealed class GrandChildACC extends ChildC { @ExpectedGenericString("final class SealedRootIntf$ChildC$GrandChildACC$GreatGrandChildACCA") final class GreatGrandChildACCA extends GrandChildACC {} @ExpectedGenericString("non-sealed class SealedRootIntf$ChildC$GrandChildACC$GreatGrandChildACCB") non-sealed class GreatGrandChildACCB extends GrandChildACC {} } } @ExpectedGenericString("public abstract static sealed interface SealedRootIntf$IntfA") sealed interface IntfA extends SealedRootIntf { @ExpectedGenericString("public static non-sealed class SealedRootIntf$IntfA$IntfAImpl") non-sealed class IntfAImpl implements IntfA {} } @ExpectedGenericString("public abstract static non-sealed interface SealedRootIntf$IntfB") non-sealed interface IntfB extends SealedRootIntf { // Check that non-sealing can be allowed with a second superinterface being sealed. @ExpectedGenericString("public static non-sealed class SealedRootIntf$IntfB$IntfAImpl") non-sealed class IntfAImpl implements IntfB, IntfA {} } }