1 /* 2 * Copyright (c) 2018, 2019, 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 */ of(String name)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 */ of(String packageName, String className)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 (packageName.length() > 0 ? "/" : "") + 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 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 */ ofDescriptor(String descriptor)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 ClassDesc} would have an array rank of greater than 255 152 * @jvms 4.4.1 The CONSTANT_Class_info Structure 153 */ arrayType()154 default ClassDesc arrayType() { 155 int depth = ConstantUtils.arrayDepth(descriptorString()); 156 if (depth >= ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) { 157 throw new IllegalStateException( 158 "Cannot create an array type descriptor with more than " + 159 ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS + " dimensions"); 160 } 161 return arrayType(1); 162 } 163 164 /** 165 * Returns a {@linkplain ClassDesc} for an array type of the specified rank, 166 * whose component type is described by this {@linkplain ClassDesc}. 167 * 168 * @param rank the rank of the array 169 * @return a {@linkplain ClassDesc} describing the array type 170 * @throws IllegalArgumentException if the rank is less than or equal to zero or if the rank of the resulting array type is 171 * greater than 255 172 * @jvms 4.4.1 The CONSTANT_Class_info Structure 173 */ arrayType(int rank)174 default ClassDesc arrayType(int rank) { 175 int currentDepth = ConstantUtils.arrayDepth(descriptorString()); 176 if (rank <= 0 || currentDepth + rank > ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) 177 throw new IllegalArgumentException("rank: " + currentDepth + rank); 178 return ClassDesc.ofDescriptor("[".repeat(rank) + descriptorString()); 179 } 180 181 /** 182 * Returns a {@linkplain ClassDesc} for a nested class of the class or 183 * interface type described by this {@linkplain ClassDesc}. 184 * 185 * @apiNote 186 * 187 * Example: If descriptor {@code d} describes the class {@code java.util.Map}, a 188 * descriptor for the class {@code java.util.Map.Entry} could be obtained 189 * by {@code d.nested("Entry")}. 190 * 191 * @param nestedName the unqualified name of the nested class 192 * @return a {@linkplain ClassDesc} describing the nested class 193 * @throws NullPointerException if the argument is {@code null} 194 * @throws IllegalStateException if this {@linkplain ClassDesc} does not 195 * describe a class or interface type 196 * @throws IllegalArgumentException if the nested class name is invalid 197 */ nested(String nestedName)198 default ClassDesc nested(String nestedName) { 199 validateMemberName(nestedName, false); 200 if (!isClassOrInterface()) 201 throw new IllegalStateException("Outer class is not a class or interface type"); 202 return ClassDesc.ofDescriptor(dropLastChar(descriptorString()) + "$" + nestedName + ";"); 203 } 204 205 /** 206 * Returns a {@linkplain ClassDesc} for a nested class of the class or 207 * interface type described by this {@linkplain ClassDesc}. 208 * 209 * @param firstNestedName the unqualified name of the first level of nested class 210 * @param moreNestedNames the unqualified name(s) of the remaining levels of 211 * nested class 212 * @return a {@linkplain ClassDesc} describing the nested class 213 * @throws NullPointerException if any argument or its contents is {@code null} 214 * @throws IllegalStateException if this {@linkplain ClassDesc} does not 215 * describe a class or interface type 216 * @throws IllegalArgumentException if the nested class name is invalid 217 */ nested(String firstNestedName, String... moreNestedNames)218 default ClassDesc nested(String firstNestedName, String... moreNestedNames) { 219 if (!isClassOrInterface()) 220 throw new IllegalStateException("Outer class is not a class or interface type"); 221 validateMemberName(firstNestedName, false); 222 requireNonNull(moreNestedNames); 223 for (String addNestedNames : moreNestedNames) { 224 validateMemberName(addNestedNames, false); 225 } 226 return moreNestedNames.length == 0 227 ? nested(firstNestedName) 228 : nested(firstNestedName + Stream.of(moreNestedNames).collect(joining("$", "$", ""))); 229 } 230 231 /** 232 * Returns whether this {@linkplain ClassDesc} describes an array type. 233 * 234 * @return whether this {@linkplain ClassDesc} describes an array type 235 */ isArray()236 default boolean isArray() { 237 return descriptorString().startsWith("["); 238 } 239 240 /** 241 * Returns whether this {@linkplain ClassDesc} describes a primitive type. 242 * 243 * @return whether this {@linkplain ClassDesc} describes a primitive type 244 */ isPrimitive()245 default boolean isPrimitive() { 246 return descriptorString().length() == 1; 247 } 248 249 /** 250 * Returns whether this {@linkplain ClassDesc} describes a class or interface type. 251 * 252 * @return whether this {@linkplain ClassDesc} describes a class or interface type 253 */ isClassOrInterface()254 default boolean isClassOrInterface() { 255 return descriptorString().startsWith("L"); 256 } 257 258 /** 259 * Returns the component type of this {@linkplain ClassDesc}, if it describes 260 * an array type, or {@code null} otherwise. 261 * 262 * @return a {@linkplain ClassDesc} describing the component type, or {@code null} 263 * if this descriptor does not describe an array type 264 */ componentType()265 default ClassDesc componentType() { 266 return isArray() ? ClassDesc.ofDescriptor(descriptorString().substring(1)) : null; 267 } 268 269 /** 270 * Returns the package name of this {@linkplain ClassDesc}, if it describes 271 * a class or interface type. 272 * 273 * @return the package name, or the empty string if the class is in the 274 * default package, or this {@linkplain ClassDesc} does not describe a class or interface type 275 */ packageName()276 default String packageName() { 277 if (!isClassOrInterface()) 278 return ""; 279 String className = internalToBinary(ConstantUtils.dropFirstAndLastChar(descriptorString())); 280 int index = className.lastIndexOf('.'); 281 return (index == -1) ? "" : className.substring(0, index); 282 } 283 284 /** 285 * Returns a human-readable name for the type described by this descriptor. 286 * 287 * @implSpec 288 * <p>The default implementation returns the simple name 289 * (e.g., {@code int}) for primitive types, the unqualified class name 290 * for class or interface types, or the display name of the component type 291 * suffixed with the appropriate number of {@code []} pairs for array types. 292 * 293 * @return the human-readable name 294 */ displayName()295 default String displayName() { 296 if (isPrimitive()) 297 return Wrapper.forBasicType(descriptorString().charAt(0)).primitiveSimpleName(); 298 else if (isClassOrInterface()) { 299 return descriptorString().substring(Math.max(1, descriptorString().lastIndexOf('/') + 1), 300 descriptorString().length() - 1); 301 } 302 else if (isArray()) { 303 int depth = ConstantUtils.arrayDepth(descriptorString()); 304 ClassDesc c = this; 305 for (int i=0; i<depth; i++) 306 c = c.componentType(); 307 return c.displayName() + "[]".repeat(depth); 308 } 309 else 310 throw new IllegalStateException(descriptorString()); 311 } 312 313 /** 314 * Returns a field type descriptor string for this type 315 * 316 * @return the descriptor string 317 * @jvms 4.3.2 Field Descriptors 318 */ descriptorString()319 String descriptorString(); 320 321 /** 322 * Compare the specified object with this descriptor for equality. Returns 323 * {@code true} if and only if the specified object is also a 324 * {@linkplain ClassDesc} and both describe the same type. 325 * 326 * @param o the other object 327 * @return whether this descriptor is equal to the other object 328 */ equals(Object o)329 boolean equals(Object o); 330 } 331