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