/*
 * Copyright (c) 2018, 2019, 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * 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.
 */
package java.lang.constant;

import java.lang.invoke.TypeDescriptor;
import java.util.stream.Stream;

import sun.invoke.util.Wrapper;

import static java.lang.constant.ConstantUtils.binaryToInternal;
import static java.lang.constant.ConstantUtils.dropLastChar;
import static java.lang.constant.ConstantUtils.internalToBinary;
import static java.lang.constant.ConstantUtils.validateMemberName;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.joining;

A nominal descriptor for a Class constant.

For common system types, including all the primitive types, there are predefined ClassDesc constants in ConstantDescs. (The java.lang.constant APIs consider void to be a primitive type.) To create a ClassDesc for a class or interface type, use of or ofDescriptor(String); to create a ClassDesc for an array type, use ofDescriptor(String), or first obtain a ClassDesc for the component type and then call the arrayType() or arrayType(int) methods.

See Also:
API Note:In the future, if the Java language permits, ClassDesc may become a sealed interface, which would prohibit subclassing except by explicitly permitted types. Non-platform classes should not implement ClassDesc directly.
Since:12
/** * A <a href="package-summary.html#nominal">nominal descriptor</a> for a * {@link Class} constant. * * <p>For common system types, including all the primitive types, there are * predefined {@linkplain ClassDesc} constants in {@link ConstantDescs}. * (The {@code java.lang.constant} APIs consider {@code void} to be a primitive type.) * To create a {@linkplain ClassDesc} for a class or interface type, use {@link #of} or * {@link #ofDescriptor(String)}; to create a {@linkplain ClassDesc} for an array * type, use {@link #ofDescriptor(String)}, or first obtain a * {@linkplain ClassDesc} for the component type and then call the {@link #arrayType()} * or {@link #arrayType(int)} methods. * * @apiNote In the future, if the Java language permits, {@linkplain ClassDesc} * may become a {@code sealed} interface, which would prohibit subclassing except * by explicitly permitted types. Non-platform classes should not implement * {@linkplain ClassDesc} directly. * * @see ConstantDescs * * @since 12 */
public interface ClassDesc extends ConstantDesc, TypeDescriptor.OfField<ClassDesc> {
Returns a ClassDesc for a class or interface type, given the name of the class or interface, such as "java.lang.String". (To create a descriptor for an array type, either use ofDescriptor(String) or arrayType(); to create a descriptor for a primitive type, use ofDescriptor(String) or use the predefined constants in ConstantDescs).
Params:
  • name – the fully qualified (dot-separated) binary class name
Throws:
Returns:a ClassDesc describing the desired class
/** * Returns a {@linkplain ClassDesc} for a class or interface type, * given the name of the class or interface, such as {@code "java.lang.String"}. * (To create a descriptor for an array type, either use {@link #ofDescriptor(String)} * or {@link #arrayType()}; to create a descriptor for a primitive type, use * {@link #ofDescriptor(String)} or use the predefined constants in * {@link ConstantDescs}). * * @param name the fully qualified (dot-separated) binary class name * @return a {@linkplain ClassDesc} describing the desired class * @throws NullPointerException if the argument is {@code null} * @throws IllegalArgumentException if the name string is not in the * correct format */
static ClassDesc of(String name) { ConstantUtils.validateBinaryClassName(requireNonNull(name)); return ClassDesc.ofDescriptor("L" + binaryToInternal(name) + ";"); }
Returns a ClassDesc for a class or interface type, given a package name and the unqualified (simple) name for the class or interface.
Params:
  • packageName – the package name (dot-separated); if the package name is the empty string, the class is considered to be in the unnamed package
  • className – the unqualified (simple) class name
Throws:
Returns:a ClassDesc describing the desired class
/** * Returns a {@linkplain ClassDesc} for a class or interface type, * given a package name and the unqualified (simple) name for the * class or interface. * * @param packageName the package name (dot-separated); if the package * name is the empty string, the class is considered to * be in the unnamed package * @param className the unqualified (simple) class name * @return a {@linkplain ClassDesc} describing the desired class * @throws NullPointerException if any argument is {@code null} * @throws IllegalArgumentException if the package name or class name are * not in the correct format */
static ClassDesc of(String packageName, String className) { ConstantUtils.validateBinaryClassName(requireNonNull(packageName)); if (packageName.isEmpty()) { return of(className); } validateMemberName(requireNonNull(className), false); return ofDescriptor("L" + binaryToInternal(packageName) + (packageName.length() > 0 ? "/" : "") + className + ";"); }
Returns a ClassDesc given a descriptor string for a class, interface, array, or primitive type.
Params:
  • descriptor – a field descriptor string
Throws:
API Note: A field type descriptor string for a non-array type is either a one-letter code corresponding to a primitive type ("J", "I", "C", "S", "B", "D", "F", "Z", "V"), or the letter "L", followed by the fully qualified binary name of a class, followed by ";". A field type descriptor for an array type is the character "[" followed by the field descriptor for the component type. Examples of valid type descriptor strings include "Ljava/lang/String;", "I", "[I", "V", "[Ljava/lang/String;", etc. See JVMS 4.3.2 ("Field Descriptors") for more detail.
Returns:a ClassDesc describing the desired class
@jvms4.3.2 Field Descriptors
@jvms4.4.1 The CONSTANT_Class_info Structure
/** * Returns a {@linkplain ClassDesc} given a descriptor string for a class, * interface, array, or primitive type. * * @apiNote * * A field type descriptor string for a non-array type is either * a one-letter code corresponding to a primitive type * ({@code "J", "I", "C", "S", "B", "D", "F", "Z", "V"}), or the letter {@code "L"}, followed * by the fully qualified binary name of a class, followed by {@code ";"}. * A field type descriptor for an array type is the character {@code "["} * followed by the field descriptor for the component type. Examples of * valid type descriptor strings include {@code "Ljava/lang/String;"}, {@code "I"}, * {@code "[I"}, {@code "V"}, {@code "[Ljava/lang/String;"}, etc. * See JVMS 4.3.2 ("Field Descriptors") for more detail. * * @param descriptor a field descriptor string * @return a {@linkplain ClassDesc} describing the desired class * @throws NullPointerException if the argument is {@code null} * @throws IllegalArgumentException if the name string is not in the * correct format * @jvms 4.3.2 Field Descriptors * @jvms 4.4.1 The CONSTANT_Class_info Structure */
static ClassDesc ofDescriptor(String descriptor) { requireNonNull(descriptor); if (descriptor.isEmpty()) { throw new IllegalArgumentException( "not a valid reference type descriptor: " + descriptor); } int depth = ConstantUtils.arrayDepth(descriptor); if (depth > ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) { throw new IllegalArgumentException( "Cannot create an array type descriptor with more than " + ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS + " dimensions"); } return (descriptor.length() == 1) ? new PrimitiveClassDescImpl(descriptor) : new ReferenceClassDescImpl(descriptor); }
Returns a ClassDesc for an array type whose component type is described by this ClassDesc.
Throws:
Returns:a ClassDesc describing the array type
@jvms4.4.1 The CONSTANT_Class_info Structure
/** * Returns a {@linkplain ClassDesc} for an array type whose component type * is described by this {@linkplain ClassDesc}. * * @return a {@linkplain ClassDesc} describing the array type * @throws IllegalStateException if the resulting {@linkplain ClassDesc} would have an array rank of greater than 255 * @jvms 4.4.1 The CONSTANT_Class_info Structure */
default ClassDesc arrayType() { int depth = ConstantUtils.arrayDepth(descriptorString()); if (depth >= ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) { throw new IllegalStateException( "Cannot create an array type descriptor with more than " + ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS + " dimensions"); } return arrayType(1); }
Returns a ClassDesc for an array type of the specified rank, whose component type is described by this ClassDesc.
Params:
  • rank – the rank of the array
Throws:
  • IllegalArgumentException – if the rank is less than or equal to zero or if the rank of the resulting array type is greater than 255
Returns:a ClassDesc describing the array type
@jvms4.4.1 The CONSTANT_Class_info Structure
/** * Returns a {@linkplain ClassDesc} for an array type of the specified rank, * whose component type is described by this {@linkplain ClassDesc}. * * @param rank the rank of the array * @return a {@linkplain ClassDesc} describing the array type * @throws IllegalArgumentException if the rank is less than or equal to zero or if the rank of the resulting array type is * greater than 255 * @jvms 4.4.1 The CONSTANT_Class_info Structure */
default ClassDesc arrayType(int rank) { int currentDepth = ConstantUtils.arrayDepth(descriptorString()); if (rank <= 0 || currentDepth + rank > ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) throw new IllegalArgumentException("rank: " + currentDepth + rank); return ClassDesc.ofDescriptor("[".repeat(rank) + descriptorString()); }
Returns a ClassDesc for a nested class of the class or interface type described by this ClassDesc.
Params:
  • nestedName – the unqualified name of the nested class
Throws:
API Note: Example: If descriptor d describes the class java.util.Map, a descriptor for the class java.util.Map.Entry could be obtained by d.nested("Entry").
Returns:a ClassDesc describing the nested class
/** * Returns a {@linkplain ClassDesc} for a nested class of the class or * interface type described by this {@linkplain ClassDesc}. * * @apiNote * * Example: If descriptor {@code d} describes the class {@code java.util.Map}, a * descriptor for the class {@code java.util.Map.Entry} could be obtained * by {@code d.nested("Entry")}. * * @param nestedName the unqualified name of the nested class * @return a {@linkplain ClassDesc} describing the nested class * @throws NullPointerException if the argument is {@code null} * @throws IllegalStateException if this {@linkplain ClassDesc} does not * describe a class or interface type * @throws IllegalArgumentException if the nested class name is invalid */
default ClassDesc nested(String nestedName) { validateMemberName(nestedName, false); if (!isClassOrInterface()) throw new IllegalStateException("Outer class is not a class or interface type"); return ClassDesc.ofDescriptor(dropLastChar(descriptorString()) + "$" + nestedName + ";"); }
Returns a ClassDesc for a nested class of the class or interface type described by this ClassDesc.
Params:
  • firstNestedName – the unqualified name of the first level of nested class
  • moreNestedNames – the unqualified name(s) of the remaining levels of nested class
Throws:
Returns:a ClassDesc describing the nested class
/** * Returns a {@linkplain ClassDesc} for a nested class of the class or * interface type described by this {@linkplain ClassDesc}. * * @param firstNestedName the unqualified name of the first level of nested class * @param moreNestedNames the unqualified name(s) of the remaining levels of * nested class * @return a {@linkplain ClassDesc} describing the nested class * @throws NullPointerException if any argument or its contents is {@code null} * @throws IllegalStateException if this {@linkplain ClassDesc} does not * describe a class or interface type * @throws IllegalArgumentException if the nested class name is invalid */
default ClassDesc nested(String firstNestedName, String... moreNestedNames) { if (!isClassOrInterface()) throw new IllegalStateException("Outer class is not a class or interface type"); validateMemberName(firstNestedName, false); requireNonNull(moreNestedNames); for (String addNestedNames : moreNestedNames) { validateMemberName(addNestedNames, false); } return moreNestedNames.length == 0 ? nested(firstNestedName) : nested(firstNestedName + Stream.of(moreNestedNames).collect(joining("$", "$", ""))); }
Returns whether this ClassDesc describes an array type.
Returns:whether this ClassDesc describes an array type
/** * Returns whether this {@linkplain ClassDesc} describes an array type. * * @return whether this {@linkplain ClassDesc} describes an array type */
default boolean isArray() { return descriptorString().startsWith("["); }
Returns whether this ClassDesc describes a primitive type.
Returns:whether this ClassDesc describes a primitive type
/** * Returns whether this {@linkplain ClassDesc} describes a primitive type. * * @return whether this {@linkplain ClassDesc} describes a primitive type */
default boolean isPrimitive() { return descriptorString().length() == 1; }
Returns whether this ClassDesc describes a class or interface type.
Returns:whether this ClassDesc describes a class or interface type
/** * Returns whether this {@linkplain ClassDesc} describes a class or interface type. * * @return whether this {@linkplain ClassDesc} describes a class or interface type */
default boolean isClassOrInterface() { return descriptorString().startsWith("L"); }
Returns the component type of this ClassDesc, if it describes an array type, or null otherwise.
Returns:a ClassDesc describing the component type, or null if this descriptor does not describe an array type
/** * Returns the component type of this {@linkplain ClassDesc}, if it describes * an array type, or {@code null} otherwise. * * @return a {@linkplain ClassDesc} describing the component type, or {@code null} * if this descriptor does not describe an array type */
default ClassDesc componentType() { return isArray() ? ClassDesc.ofDescriptor(descriptorString().substring(1)) : null; }
Returns the package name of this ClassDesc, if it describes a class or interface type.
Returns:the package name, or the empty string if the class is in the default package, or this ClassDesc does not describe a class or interface type
/** * Returns the package name of this {@linkplain ClassDesc}, if it describes * a class or interface type. * * @return the package name, or the empty string if the class is in the * default package, or this {@linkplain ClassDesc} does not describe a class or interface type */
default String packageName() { if (!isClassOrInterface()) return ""; String className = internalToBinary(ConstantUtils.dropFirstAndLastChar(descriptorString())); int index = className.lastIndexOf('.'); return (index == -1) ? "" : className.substring(0, index); }
Returns a human-readable name for the type described by this descriptor.
Implementation Requirements:

The default implementation returns the simple name (e.g., int) for primitive types, the unqualified class name for class or interface types, or the display name of the component type suffixed with the appropriate number of [] pairs for array types.

Returns:the human-readable name
/** * Returns a human-readable name for the type described by this descriptor. * * @implSpec * <p>The default implementation returns the simple name * (e.g., {@code int}) for primitive types, the unqualified class name * for class or interface types, or the display name of the component type * suffixed with the appropriate number of {@code []} pairs for array types. * * @return the human-readable name */
default String displayName() { if (isPrimitive()) return Wrapper.forBasicType(descriptorString().charAt(0)).primitiveSimpleName(); else if (isClassOrInterface()) { return descriptorString().substring(Math.max(1, descriptorString().lastIndexOf('/') + 1), descriptorString().length() - 1); } else if (isArray()) { int depth = ConstantUtils.arrayDepth(descriptorString()); ClassDesc c = this; for (int i=0; i<depth; i++) c = c.componentType(); return c.displayName() + "[]".repeat(depth); } else throw new IllegalStateException(descriptorString()); }
Returns a field type descriptor string for this type
Returns:the descriptor string
@jvms4.3.2 Field Descriptors
/** * Returns a field type descriptor string for this type * * @return the descriptor string * @jvms 4.3.2 Field Descriptors */
String descriptorString();
Compare the specified object with this descriptor for equality. Returns true if and only if the specified object is also a ClassDesc and both describe the same type.
Params:
  • o – the other object
Returns:whether this descriptor is equal to the other object
/** * Compare the specified object with this descriptor for equality. Returns * {@code true} if and only if the specified object is also a * {@linkplain ClassDesc} and both describe the same type. * * @param o the other object * @return whether this descriptor is equal to the other object */
boolean equals(Object o); }