| /* | |
| * 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<String, Integer>[] mixed = null; | |
| public Map<String, Integer>[][] 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<PlatformTestCase> 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<E extends java.lang.Enum<E>>"), | |
| new PlatformTestCase(java.util.Map.class, | |
| "public abstract interface java.util.Map<K,V>"), | |
| new PlatformTestCase(java.util.EnumMap.class, | |
| "public class java.util.EnumMap<K extends java.lang.Enum<K>,V>"), | |
| new PlatformTestCase(java.util.EventListenerProxy.class, | |
| "public abstract class java.util.EventListenerProxy<T extends java.util.EventListener>"), | |
| // Sealed class | |
| new PlatformTestCase(java.lang.ref.Reference.class, | |
| "public abstract sealed class java.lang.ref.Reference<T>"), | |
| // non-sealed class | |
| new PlatformTestCase(java.lang.ref.WeakReference.class, | |
| "public non-sealed class java.lang.ref.WeakReference<T>") | |
| ); | |
| for (PlatformTestCase platformTestCase : platformTestCases) { | |
| failures += checkToGenericString(platformTestCase.clazz, | |
| platformTestCase.expected); | |
| } | |
| Field f = GenericStringTest.class.getDeclaredField("mixed"); | |
| // The expected value includes "<K,V>" rather than | |
| // "<...String,...Integer>" since the Class object rather than | |
| // Type objects is being queried. | |
| failures += checkToGenericString(f.getType(), "java.util.Map<K,V>[]"); | |
| f = GenericStringTest.class.getDeclaredField("mixed2"); | |
| failures += checkToGenericString(f.getType(), "java.util.Map<K,V>[][]"); | |
| 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<K,V>") | |
| interface LocalMap<K,V> {} | |
| @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 {} | |
| } | |
| } |