1 /*
  2  * Copyright (c) 2018, 2022, 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.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 package java.lang.constant;
 26 
 27 import java.lang.invoke.TypeDescriptor;
 28 import java.util.stream.Stream;
 29 
 30 import sun.invoke.util.Wrapper;
 31 
 32 import static java.lang.constant.ConstantUtils.binaryToInternal;
 33 import static java.lang.constant.ConstantUtils.dropLastChar;
 34 import static java.lang.constant.ConstantUtils.internalToBinary;
 35 import static java.lang.constant.ConstantUtils.validateMemberName;
 36 import static java.util.Objects.requireNonNull;
 37 import static java.util.stream.Collectors.joining;
 38 
 39 /**
 40  * A <a href="package-summary.html#nominal">nominal descriptor</a> for a
 41  * {@link Class} constant.
 42  *
 43  * <p>For common system types, including all the primitive types, there are
 44  * predefined {@linkplain ClassDesc} constants in {@link ConstantDescs}.
 45  * (The {@code java.lang.constant} APIs consider {@code void} to be a primitive type.)
 46  * To create a {@linkplain ClassDesc} for a class or interface type, use {@link #of} or
 47  * {@link #ofDescriptor(String)}; to create a {@linkplain ClassDesc} for an array
 48  * type, use {@link #ofDescriptor(String)}, or first obtain a
 49  * {@linkplain ClassDesc} for the component type and then call the {@link #arrayType()}
 50  * or {@link #arrayType(int)} methods.
 51  *
 52  * @see ConstantDescs
 53  *
 54  * @since 12
 55  */
 56 public sealed interface ClassDesc
 57         extends ConstantDesc,
 58                 TypeDescriptor.OfField<ClassDesc>
 59         permits PrimitiveClassDescImpl,
 60                 ReferenceClassDescImpl {
 61 
 62     /**
 63      * Returns a {@linkplain ClassDesc} for a class or interface type,
 64      * given the name of the class or interface, such as {@code "java.lang.String"}.
 65      * (To create a descriptor for an array type, either use {@link #ofDescriptor(String)}
 66      * or {@link #arrayType()}; to create a descriptor for a primitive type, use
 67      * {@link #ofDescriptor(String)} or use the predefined constants in
 68      * {@link ConstantDescs}).
 69      *
 70      * @param name the fully qualified (dot-separated) binary class name
 71      * @return a {@linkplain ClassDesc} describing the desired class
 72      * @throws NullPointerException if the argument is {@code null}
 73      * @throws IllegalArgumentException if the name string is not in the
 74      * correct format
 75      */
 76     static ClassDesc of(String name) {
 77         ConstantUtils.validateBinaryClassName(requireNonNull(name));
 78         return ClassDesc.ofDescriptor("L" + binaryToInternal(name) + ";");
 79     }
 80 
 81     /**
 82      * Returns a {@linkplain ClassDesc} for a class or interface type,
 83      * given a package name and the unqualified (simple) name for the
 84      * class or interface.
 85      *
 86      * @param packageName the package name (dot-separated); if the package
 87      *                    name is the empty string, the class is considered to
 88      *                    be in the unnamed package
 89      * @param className the unqualified (simple) class name
 90      * @return a {@linkplain ClassDesc} describing the desired class
 91      * @throws NullPointerException if any argument is {@code null}
 92      * @throws IllegalArgumentException if the package name or class name are
 93      * not in the correct format
 94      */
 95     static ClassDesc of(String packageName, String className) {
 96         ConstantUtils.validateBinaryClassName(requireNonNull(packageName));
 97         if (packageName.isEmpty()) {
 98             return of(className);
 99         }
100         validateMemberName(requireNonNull(className), false);
101         return ofDescriptor("L" + binaryToInternal(packageName) +
102                 "/" + className + ";");
103     }
104 
105     /**
106      * Returns a {@linkplain ClassDesc} given a descriptor string for a class,
107      * interface, array, or primitive type.
108      *
109      * @apiNote
110      *
111      * A field type descriptor string for a non-array type is either
112      * a one-letter code corresponding to a primitive type
113      * ({@code "J", "I", "C", "S", "B", "D", "F", "Z", "V"}), or the letter {@code "L"}, followed
114      * by the fully qualified binary name of a class, followed by {@code ";"}.
115      * A field type descriptor for an array type is the character {@code "["}
116      * followed by the field descriptor for the component type.  Examples of
117      * valid type descriptor strings include {@code "Ljava/lang/String;"}, {@code "I"},
118      * {@code "[I"}, {@code "V"}, {@code "[Ljava/lang/String;"}, etc.
119      * See JVMS {@jvms 4.3.2 }("Field Descriptors") for more detail.
120      *
121      * @param descriptor a field descriptor string
122      * @return a {@linkplain ClassDesc} describing the desired class
123      * @throws NullPointerException if the argument is {@code null}
124      * @throws IllegalArgumentException if the name string is not in the
125      * correct format
126      * @jvms 4.3.2 Field Descriptors
127      * @jvms 4.4.1 The CONSTANT_Class_info Structure
128      */
129     static ClassDesc ofDescriptor(String descriptor) {
130         requireNonNull(descriptor);
131         if (descriptor.isEmpty()) {
132             throw new IllegalArgumentException(
133                     "not a valid reference type descriptor: " + descriptor);
134         }
135         int depth = ConstantUtils.arrayDepth(descriptor);
136         if (depth > ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) {
137             throw new IllegalArgumentException(
138                     "Cannot create an array type descriptor with more than " +
139                     ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS + " dimensions");
140         }
141         return (descriptor.length() == 1)
142                ? new PrimitiveClassDescImpl(descriptor)
143                : new ReferenceClassDescImpl(descriptor);
144     }
145 
146     /**
147      * Returns a {@linkplain ClassDesc} for an array type whose component type
148      * is described by this {@linkplain ClassDesc}.
149      *
150      * @return a {@linkplain ClassDesc} describing the array type
151      * @throws IllegalStateException if the resulting {@linkplain
152      * ClassDesc} would have an array rank of greater than 255
153      * @jvms 4.4.1 The CONSTANT_Class_info Structure
154      */
155     default ClassDesc arrayType() {
156         int depth = ConstantUtils.arrayDepth(descriptorString());
157         if (depth >= ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) {
158             throw new IllegalStateException(
159                     "Cannot create an array type descriptor with more than " +
160                     ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS + " dimensions");
161         }
162         return arrayType(1);
163     }
164 
165     /**
166      * Returns a {@linkplain ClassDesc} for an array type of the specified rank,
167      * whose component type is described by this {@linkplain ClassDesc}.
168      *
169      * @param rank the rank of the array
170      * @return a {@linkplain ClassDesc} describing the array type
171      * @throws IllegalArgumentException if the rank is less than or
172      * equal to zero or if the rank of the resulting array type is
173      * greater than 255
174      * @jvms 4.4.1 The CONSTANT_Class_info Structure
175      */
176     default ClassDesc arrayType(int rank) {
177         int netRank;
178         if (rank <= 0) {
179             throw new IllegalArgumentException("rank " + rank + " is not a positive value");
180         }
181         try {
182             int currentDepth = ConstantUtils.arrayDepth(descriptorString());
183             netRank = Math.addExact(currentDepth, rank);
184             if (netRank > ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) {
185                 throw new IllegalArgumentException("rank: " + netRank +
186                                                    " exceeds maximum supported dimension of " +
187                                                    ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS);
188             }
189         } catch (ArithmeticException ae) {
190             throw new IllegalArgumentException("Integer overflow in rank computation");
191         }
192         return ClassDesc.ofDescriptor("[".repeat(rank) + descriptorString());
193     }
194 
195     /**
196      * Returns a {@linkplain ClassDesc} for a nested class of the class or
197      * interface type described by this {@linkplain ClassDesc}.
198      *
199      * @apiNote
200      *
201      * Example: If descriptor {@code d} describes the class {@code java.util.Map}, a
202      * descriptor for the class {@code java.util.Map.Entry} could be obtained
203      * by {@code d.nested("Entry")}.
204      *
205      * @param nestedName the unqualified name of the nested class
206      * @return a {@linkplain ClassDesc} describing the nested class
207      * @throws NullPointerException if the argument is {@code null}
208      * @throws IllegalStateException if this {@linkplain ClassDesc} does not
209      * describe a class or interface type
210      * @throws IllegalArgumentException if the nested class name is invalid
211      */
212     default ClassDesc nested(String nestedName) {
213         validateMemberName(nestedName, false);
214         if (!isClassOrInterface())
215             throw new IllegalStateException("Outer class is not a class or interface type");
216         return ClassDesc.ofDescriptor(dropLastChar(descriptorString()) + "$" + nestedName + ";");
217     }
218 
219     /**
220      * Returns a {@linkplain ClassDesc} for a nested class of the class or
221      * interface type described by this {@linkplain ClassDesc}.
222      *
223      * @param firstNestedName the unqualified name of the first level of nested class
224      * @param moreNestedNames the unqualified name(s) of the remaining levels of
225      *                       nested class
226      * @return a {@linkplain ClassDesc} describing the nested class
227      * @throws NullPointerException if any argument or its contents is {@code null}
228      * @throws IllegalStateException if this {@linkplain ClassDesc} does not
229      * describe a class or interface type
230      * @throws IllegalArgumentException if the nested class name is invalid
231      */
232     default ClassDesc nested(String firstNestedName, String... moreNestedNames) {
233         if (!isClassOrInterface())
234             throw new IllegalStateException("Outer class is not a class or interface type");
235         validateMemberName(firstNestedName, false);
236         requireNonNull(moreNestedNames);
237         for (String addNestedNames : moreNestedNames) {
238             validateMemberName(addNestedNames, false);
239         }
240         return moreNestedNames.length == 0
241                ? nested(firstNestedName)
242                : nested(firstNestedName + Stream.of(moreNestedNames).collect(joining("$", "$", "")));
243     }
244 
245     /**
246      * Returns whether this {@linkplain ClassDesc} describes an array type.
247      *
248      * @return whether this {@linkplain ClassDesc} describes an array type
249      */
250     default boolean isArray() {
251         return descriptorString().startsWith("[");
252     }
253 
254     /**
255      * Returns whether this {@linkplain ClassDesc} describes a primitive type.
256      *
257      * @return whether this {@linkplain ClassDesc} describes a primitive type
258      */
259     default boolean isPrimitive() {
260         return descriptorString().length() == 1;
261     }
262 
263     /**
264      * Returns whether this {@linkplain ClassDesc} describes a class or interface type.
265      *
266      * @return whether this {@linkplain ClassDesc} describes a class or interface type
267      */
268     default boolean isClassOrInterface() {
269         return descriptorString().startsWith("L");
270     }
271 
272     /**
273      * Returns the component type of this {@linkplain ClassDesc}, if it describes
274      * an array type, or {@code null} otherwise.
275      *
276      * @return a {@linkplain ClassDesc} describing the component type, or {@code null}
277      * if this descriptor does not describe an array type
278      */
279     default ClassDesc componentType() {
280         return isArray() ? ClassDesc.ofDescriptor(descriptorString().substring(1)) : null;
281     }
282 
283     /**
284      * Returns the package name of this {@linkplain ClassDesc}, if it describes
285      * a class or interface type.
286      *
287      * @return the package name, or the empty string if the class is in the
288      * default package, or this {@linkplain ClassDesc} does not describe a class or interface type
289      */
290     default String packageName() {
291         if (!isClassOrInterface())
292             return "";
293         String className = internalToBinary(ConstantUtils.dropFirstAndLastChar(descriptorString()));
294         int index = className.lastIndexOf('.');
295         return (index == -1) ? "" : className.substring(0, index);
296     }
297 
298     /**
299      * Returns a human-readable name for the type described by this descriptor.
300      *
301      * @implSpec
302      * <p>The default implementation returns the simple name
303      * (e.g., {@code int}) for primitive types, the unqualified class name
304      * for class or interface types, or the display name of the component type
305      * suffixed with the appropriate number of {@code []} pairs for array types.
306      *
307      * @return the human-readable name
308      */
309     default String displayName() {
310         if (isPrimitive())
311             return Wrapper.forBasicType(descriptorString().charAt(0)).primitiveSimpleName();
312         else if (isClassOrInterface()) {
313             return descriptorString().substring(Math.max(1, descriptorString().lastIndexOf('/') + 1),
314                                                 descriptorString().length() - 1);
315         }
316         else if (isArray()) {
317             int depth = ConstantUtils.arrayDepth(descriptorString());
318             ClassDesc c = this;
319             for (int i=0; i<depth; i++)
320                 c = c.componentType();
321             return c.displayName() + "[]".repeat(depth);
322         }
323         else
324             throw new IllegalStateException(descriptorString());
325     }
326 
327     /**
328      * Returns a field type descriptor string for this type
329      *
330      * @return the descriptor string
331      * @jvms 4.3.2 Field Descriptors
332      */
333     String descriptorString();
334 
335     /**
336      * Compare the specified object with this descriptor for equality.  Returns
337      * {@code true} if and only if the specified object is also a
338      * {@linkplain ClassDesc} and both describe the same type.
339      *
340      * @param o the other object
341      * @return whether this descriptor is equal to the other object
342      */
343     boolean equals(Object o);
344 }