1 /*
  2  * Copyright (c) 2013, 2024, 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 6298888 6992705 8161500 6304578 8322878
 27  * @summary Check Class.toGenericString()
 28  * @enablePreview
 29  * @compile GenericStringTest.java
 30  * @run main/othervm GenericStringTest
 31  */
 32 
 33 import java.lang.reflect.*;
 34 import java.lang.annotation.*;
 35 import java.util.*;
 36 
 37 @ExpectedGenericString("public class GenericStringTest")
 38 public class GenericStringTest {
 39     public Map<String, Integer>[] mixed = null;
 40     public Map<String, Integer>[][] mixed2 = null;
 41 
 42     private static record PlatformTestCase(Class<?> clazz, String expected) {}
 43 
 44     public static void main(String... args) throws ReflectiveOperationException {
 45         int failures = 0;
 46 
 47         String[][] nested = {{""}};
 48         int[][]    intArray = {{1}};
 49 
 50        List<PlatformTestCase> platformTestCases =
 51            List.of(new PlatformTestCase(int.class,           "int"),
 52                    new PlatformTestCase(void.class,          "void"),
 53                    new PlatformTestCase(args.getClass(),     "java.lang.String[]"),
 54                    new PlatformTestCase(nested.getClass(),   "java.lang.String[][]"),
 55                    new PlatformTestCase(intArray.getClass(), "int[][]"),
 56 
 57                    new PlatformTestCase(java.lang.Enum.class,
 58                                         "public abstract class java.lang.Enum<E extends java.lang.Enum<E>>"),
 59                    new PlatformTestCase(java.util.Map.class,
 60                                         "public abstract interface java.util.Map<K,V>"),
 61                    new PlatformTestCase(java.util.EnumMap.class,
 62                                         "public class java.util.EnumMap<K extends java.lang.Enum<K>,V>"),
 63                    new PlatformTestCase(java.util.EventListenerProxy.class,
 64                                         "public abstract class java.util.EventListenerProxy<T extends java.util.EventListener>"),
 65 
 66                    // Sealed class
 67                    new PlatformTestCase(java.lang.ref.Reference.class,
 68                                      "public abstract sealed class java.lang.ref.Reference<T>"),
 69                    // non-sealed class
 70                    new PlatformTestCase(java.lang.ref.WeakReference.class,
 71                                      "public non-sealed class java.lang.ref.WeakReference<T>")
 72                    );
 73 
 74         for (PlatformTestCase platformTestCase : platformTestCases) {
 75             failures += checkToGenericString(platformTestCase.clazz,
 76                                              platformTestCase.expected);
 77         }
 78 
 79         Field f = GenericStringTest.class.getDeclaredField("mixed");
 80         // The expected value includes "<K,V>" rather than
 81         // "<...String,...Integer>" since the Class object rather than
 82         // Type objects is being queried.
 83         failures += checkToGenericString(f.getType(), "java.util.Map<K,V>[]");
 84         f = GenericStringTest.class.getDeclaredField("mixed2");
 85         failures += checkToGenericString(f.getType(), "java.util.Map<K,V>[][]");
 86 
 87         for(Class<?> clazz : List.of(GenericStringTest.class,
 88                                      AnInterface.class,
 89                                      LocalMap.class,
 90                                      AnEnum.class,
 91                                      AnotherEnum.class,
 92                                      AValueClass.class,
 93 
 94                                      SealedRootClass.class,
 95                                      SealedRootClass.ChildA.class,
 96                                      SealedRootClass.ChildB.class,
 97                                      SealedRootClass.ChildB.GrandChildAB.class,
 98                                      SealedRootClass.ChildC.class,
 99                                      SealedRootClass.ChildC.GrandChildACA.class,
100                                      SealedRootClass.ChildC.GrandChildACB.class,
101                                      SealedRootClass.ChildC.GrandChildACC.class,
102                                      SealedRootClass.ChildC.GrandChildACC.GreatGrandChildACCA.class,
103                                      SealedRootClass.ChildC.GrandChildACC.GreatGrandChildACCB.class,
104 
105                                      SealedRootIntf.class,
106                                      SealedRootIntf.ChildA.class,
107                                      SealedRootIntf.ChildB.class,
108                                      SealedRootIntf.ChildB.GrandChildAB.class,
109                                      SealedRootIntf.ChildC.class,
110                                      SealedRootIntf.ChildC.GrandChildACA.class,
111                                      SealedRootIntf.ChildC.GrandChildACB.class,
112                                      SealedRootIntf.ChildC.GrandChildACC.class,
113                                      SealedRootIntf.ChildC.GrandChildACC.GreatGrandChildACCA.class,
114                                      SealedRootIntf.ChildC.GrandChildACC.GreatGrandChildACCB.class,
115                                      SealedRootIntf.IntfA.class,
116                                      SealedRootIntf.IntfA.IntfAImpl.class,
117                                      SealedRootIntf.IntfB.class,
118                                      SealedRootIntf.IntfB.IntfAImpl.class)) {
119             failures += checkToGenericString(clazz, clazz.getAnnotation(ExpectedGenericString.class).value());
120         }
121 
122         if (failures > 0) {
123             throw new RuntimeException();
124         }
125     }
126 
127     private static int checkToGenericString(Class<?> clazz, String expected) {
128         String genericString = clazz.toGenericString();
129         if (!genericString.equals(expected)) {
130             System.err.printf("Unexpected Class.toGenericString output; expected %n\t'%s',%n got %n\t'%s'.%n",
131                               expected,
132                               genericString);
133             return 1;
134         } else
135             return 0;
136     }
137 }
138 
139 @Retention(RetentionPolicy.RUNTIME)
140 @interface ExpectedGenericString {
141     String value();
142 }
143 
144 @ExpectedGenericString("abstract interface AnInterface")
145 strictfp interface AnInterface {}
146 
147 @ExpectedGenericString("abstract interface LocalMap<K,V>")
148 interface LocalMap<K,V> {}
149 
150 @ExpectedGenericString("final enum AnEnum")
151 enum AnEnum {
152     FOO;
153 }
154 
155 // If an enum class has a specialized enum constant, that is compiled
156 // by having the enum class as being sealed rather than final. See JLS
157 // 8.9 Enum Classes.
158 @ExpectedGenericString("sealed enum AnotherEnum")
159 enum AnotherEnum {
160     BAR{};
161 }
162 
163 @ExpectedGenericString("final value class AValueClass<E>")
164 value class AValueClass<E> {}
165 
166 // Test cases for sealed/non-sealed _class_ hierarchy.
167 @ExpectedGenericString("sealed class SealedRootClass")
168 sealed class SealedRootClass
169     permits
170     SealedRootClass.ChildA,
171     SealedRootClass.ChildB,
172     SealedRootClass.ChildC {
173 
174     @ExpectedGenericString("final class SealedRootClass$ChildA")
175     final class ChildA extends SealedRootClass {}
176 
177     @ExpectedGenericString("sealed class SealedRootClass$ChildB")
178     sealed class ChildB extends SealedRootClass permits SealedRootClass.ChildB.GrandChildAB {
179         @ExpectedGenericString("final class SealedRootClass$ChildB$GrandChildAB")
180         final class GrandChildAB extends ChildB {}
181     }
182 
183     @ExpectedGenericString("non-sealed class SealedRootClass$ChildC")
184     non-sealed class ChildC extends SealedRootClass {
185         // The subclasses of ChildC do not themselves have to be
186         // sealed, non-sealed, or final.
187         @ExpectedGenericString("class SealedRootClass$ChildC$GrandChildACA")
188         class GrandChildACA extends ChildC {}
189 
190         @ExpectedGenericString("final class SealedRootClass$ChildC$GrandChildACB")
191         final class GrandChildACB extends ChildC {}
192 
193         @ExpectedGenericString("sealed class SealedRootClass$ChildC$GrandChildACC")
194         sealed class GrandChildACC extends ChildC {
195             @ExpectedGenericString("final class SealedRootClass$ChildC$GrandChildACC$GreatGrandChildACCA")
196             final class GreatGrandChildACCA extends GrandChildACC {}
197 
198             @ExpectedGenericString("non-sealed class SealedRootClass$ChildC$GrandChildACC$GreatGrandChildACCB")
199             non-sealed class GreatGrandChildACCB extends GrandChildACC {}
200         }
201     }
202 }
203 
204 // Test cases for sealed/non-sealed _interface_ hierarchy.
205 @ExpectedGenericString("abstract sealed interface SealedRootIntf")
206 sealed interface SealedRootIntf
207     permits
208     SealedRootIntf.ChildA,
209     SealedRootIntf.ChildB,
210     SealedRootIntf.ChildC,
211 
212     SealedRootIntf.IntfA,
213     SealedRootIntf.IntfB {
214 
215     @ExpectedGenericString("public static final class SealedRootIntf$ChildA")
216     final class ChildA implements SealedRootIntf {}
217 
218     @ExpectedGenericString("public static sealed class SealedRootIntf$ChildB")
219     sealed class ChildB implements SealedRootIntf permits SealedRootIntf.ChildB.GrandChildAB {
220         @ExpectedGenericString("final class SealedRootIntf$ChildB$GrandChildAB")
221         final class GrandChildAB extends ChildB {}
222     }
223 
224     @ExpectedGenericString("public static non-sealed class SealedRootIntf$ChildC")
225     non-sealed class ChildC implements SealedRootIntf {
226         // The subclasses of ChildC do not themselves have to be
227         // sealed, non-sealed, or final.
228         @ExpectedGenericString("class SealedRootIntf$ChildC$GrandChildACA")
229         class GrandChildACA extends ChildC {}
230 
231         @ExpectedGenericString("final class SealedRootIntf$ChildC$GrandChildACB")
232         final class GrandChildACB extends ChildC {}
233 
234         @ExpectedGenericString("sealed class SealedRootIntf$ChildC$GrandChildACC")
235         sealed class GrandChildACC extends ChildC {
236             @ExpectedGenericString("final class SealedRootIntf$ChildC$GrandChildACC$GreatGrandChildACCA")
237             final class GreatGrandChildACCA extends GrandChildACC {}
238 
239             @ExpectedGenericString("non-sealed class SealedRootIntf$ChildC$GrandChildACC$GreatGrandChildACCB")
240             non-sealed class GreatGrandChildACCB extends GrandChildACC {}
241         }
242     }
243 
244     @ExpectedGenericString("public abstract static sealed interface SealedRootIntf$IntfA")
245     sealed interface IntfA extends  SealedRootIntf {
246         @ExpectedGenericString("public static non-sealed class SealedRootIntf$IntfA$IntfAImpl")
247         non-sealed class IntfAImpl implements IntfA {}
248     }
249 
250     @ExpectedGenericString("public abstract static non-sealed interface SealedRootIntf$IntfB")
251     non-sealed interface IntfB extends  SealedRootIntf {
252         // Check that non-sealing can be allowed with a second superinterface being sealed.
253         @ExpectedGenericString("public static non-sealed class SealedRootIntf$IntfB$IntfAImpl")
254         non-sealed class IntfAImpl implements IntfB, IntfA  {}
255     }
256 }