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 * @see ClassDesc#ofDescriptor(String) 76 * @see ClassDesc#ofInternalName(String) 77 */ 78 static ClassDesc of(String name) { 79 ConstantUtils.validateBinaryClassName(requireNonNull(name)); 80 return ClassDesc.ofDescriptor("L" + binaryToInternal(name) + ";"); 81 } 82 83 /** 84 * Returns a {@linkplain ClassDesc} for a class or interface type, 85 * given the name of the class or interface in internal form, 86 * such as {@code "java/lang/String"}. 87 * 88 * @apiNote 89 * To create a descriptor for an array type, either use {@link #ofDescriptor(String)} 90 * or {@link #arrayType()}; to create a descriptor for a primitive type, use 91 * {@link #ofDescriptor(String)} or use the predefined constants in 92 * {@link ConstantDescs}. 93 * 94 * @param name the fully qualified class name, in internal (slash-separated) form 95 * @return a {@linkplain ClassDesc} describing the desired class 96 * @throws NullPointerException if the argument is {@code null} 97 * @throws IllegalArgumentException if the name string is not in the 98 * correct format 99 * @jvms 4.2.1 Binary Class and Interface Names 100 * @see ClassDesc#of(String) 101 * @see ClassDesc#ofDescriptor(String) 102 */ 103 static ClassDesc ofInternalName(String name) { 104 ConstantUtils.validateInternalClassName(requireNonNull(name)); 105 return ClassDesc.ofDescriptor("L" + name + ";"); 106 } 107 108 /** 109 * Returns a {@linkplain ClassDesc} for a class or interface type, 110 * given a package name and the unqualified (simple) name for the 111 * class or interface. 112 * 113 * @param packageName the package name (dot-separated); if the package 114 * name is the empty string, the class is considered to 115 * be in the unnamed package 116 * @param className the unqualified (simple) class name 117 * @return a {@linkplain ClassDesc} describing the desired class 118 * @throws NullPointerException if any argument is {@code null} 119 * @throws IllegalArgumentException if the package name or class name are 120 * not in the correct format 121 */ 122 static ClassDesc of(String packageName, String className) { 123 ConstantUtils.validateBinaryClassName(requireNonNull(packageName)); 124 if (packageName.isEmpty()) { 125 return of(className); 126 } 127 validateMemberName(requireNonNull(className), false); 128 return ofDescriptor("L" + binaryToInternal(packageName) + 129 "/" + className + ";"); 130 } 131 132 /** 133 * Returns a {@linkplain ClassDesc} given a descriptor string for a class, 134 * interface, array, or primitive type. 135 * 136 * @apiNote 137 * 138 * A field type descriptor string for a non-array type is either 139 * a one-letter code corresponding to a primitive type 140 * ({@code "J", "I", "C", "S", "B", "D", "F", "Z", "V"}), or the letter {@code "L"}, followed 141 * by the fully qualified binary name of a class, followed by {@code ";"}. 142 * A field type descriptor for an array type is the character {@code "["} 143 * followed by the field descriptor for the component type. Examples of 144 * valid type descriptor strings include {@code "Ljava/lang/String;"}, {@code "I"}, 145 * {@code "[I"}, {@code "V"}, {@code "[Ljava/lang/String;"}, etc. 146 * See JVMS {@jvms 4.3.2 }("Field Descriptors") for more detail. 147 * 148 * @param descriptor a field descriptor string 149 * @return a {@linkplain ClassDesc} describing the desired class 150 * @throws NullPointerException if the argument is {@code null} 151 * @throws IllegalArgumentException if the name string is not in the 152 * correct format 153 * @jvms 4.3.2 Field Descriptors 154 * @jvms 4.4.1 The CONSTANT_Class_info Structure 155 * @see ClassDesc#of(String) 156 * @see ClassDesc#ofInternalName(String) 157 */ 158 static ClassDesc ofDescriptor(String descriptor) { 159 requireNonNull(descriptor); 160 if (descriptor.isEmpty()) { 161 throw new IllegalArgumentException( 162 "not a valid reference type descriptor: " + descriptor); 163 } 164 int depth = ConstantUtils.arrayDepth(descriptor); 165 if (depth > ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) { 166 throw new IllegalArgumentException( 167 "Cannot create an array type descriptor with more than " + 168 ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS + " dimensions"); 169 } 170 return (descriptor.length() == 1) 171 ? new PrimitiveClassDescImpl(descriptor) 172 : new ReferenceClassDescImpl(descriptor); 173 } 174 175 /** 176 * Returns a {@linkplain ClassDesc} for an array type whose component type 177 * is described by this {@linkplain ClassDesc}. 178 * 179 * @return a {@linkplain ClassDesc} describing the array type 180 * @throws IllegalStateException if the resulting {@linkplain 181 * ClassDesc} would have an array rank of greater than 255 182 * @jvms 4.4.1 The CONSTANT_Class_info Structure 183 */ 184 default ClassDesc arrayType() { 185 int depth = ConstantUtils.arrayDepth(descriptorString()); 186 if (depth >= ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) { 187 throw new IllegalStateException( 188 "Cannot create an array type descriptor with more than " + 189 ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS + " dimensions"); 190 } 191 return arrayType(1); 192 } 193 194 /** 195 * Returns a {@linkplain ClassDesc} for an array type of the specified rank, 196 * whose component type is described by this {@linkplain ClassDesc}. 197 * 198 * @param rank the rank of the array 199 * @return a {@linkplain ClassDesc} describing the array type 200 * @throws IllegalArgumentException if the rank is less than or 201 * equal to zero or if the rank of the resulting array type is 202 * greater than 255 203 * @jvms 4.4.1 The CONSTANT_Class_info Structure 204 */ 205 default ClassDesc arrayType(int rank) { 206 int netRank; 207 if (rank <= 0) { 208 throw new IllegalArgumentException("rank " + rank + " is not a positive value"); 209 } 210 try { 211 int currentDepth = ConstantUtils.arrayDepth(descriptorString()); 212 netRank = Math.addExact(currentDepth, rank); 213 if (netRank > ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) { 214 throw new IllegalArgumentException("rank: " + netRank + 215 " exceeds maximum supported dimension of " + 216 ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS); 217 } 218 } catch (ArithmeticException ae) { 219 throw new IllegalArgumentException("Integer overflow in rank computation"); 220 } 221 return ClassDesc.ofDescriptor("[".repeat(rank) + descriptorString()); 222 } 223 224 /** 225 * Returns a {@linkplain ClassDesc} for a nested class of the class or 226 * interface type described by this {@linkplain ClassDesc}. 227 * 228 * @apiNote 229 * 230 * Example: If descriptor {@code d} describes the class {@code java.util.Map}, a 231 * descriptor for the class {@code java.util.Map.Entry} could be obtained 232 * by {@code d.nested("Entry")}. 233 * 234 * @param nestedName the unqualified name of the nested class 235 * @return a {@linkplain ClassDesc} describing the nested class 236 * @throws NullPointerException if the argument is {@code null} 237 * @throws IllegalStateException if this {@linkplain ClassDesc} does not 238 * describe a class or interface type 239 * @throws IllegalArgumentException if the nested class name is invalid 240 */ 241 default ClassDesc nested(String nestedName) { 242 validateMemberName(nestedName, false); 243 if (!isClassOrInterface()) 244 throw new IllegalStateException("Outer class is not a class or interface type"); 245 return ClassDesc.ofDescriptor(dropLastChar(descriptorString()) + "$" + nestedName + ";"); 246 } 247 248 /** 249 * Returns a {@linkplain ClassDesc} for a nested class of the class or 250 * interface type described by this {@linkplain ClassDesc}. 251 * 252 * @param firstNestedName the unqualified name of the first level of nested class 253 * @param moreNestedNames the unqualified name(s) of the remaining levels of 254 * nested class 255 * @return a {@linkplain ClassDesc} describing the nested class 256 * @throws NullPointerException if any argument or its contents is {@code null} 257 * @throws IllegalStateException if this {@linkplain ClassDesc} does not 258 * describe a class or interface type 259 * @throws IllegalArgumentException if the nested class name is invalid 260 */ 261 default ClassDesc nested(String firstNestedName, String... moreNestedNames) { 262 if (!isClassOrInterface()) 263 throw new IllegalStateException("Outer class is not a class or interface type"); 264 validateMemberName(firstNestedName, false); 265 requireNonNull(moreNestedNames); 266 for (String addNestedNames : moreNestedNames) { 267 validateMemberName(addNestedNames, false); 268 } 269 return moreNestedNames.length == 0 270 ? nested(firstNestedName) 271 : nested(firstNestedName + Stream.of(moreNestedNames).collect(joining("$", "$", ""))); 272 } 273 274 /** 275 * Returns whether this {@linkplain ClassDesc} describes an array type. 276 * 277 * @return whether this {@linkplain ClassDesc} describes an array type 278 */ 279 default boolean isArray() { 280 return descriptorString().startsWith("["); 281 } 282 283 /** 284 * Returns whether this {@linkplain ClassDesc} describes a primitive type. 285 * 286 * @return whether this {@linkplain ClassDesc} describes a primitive type 287 */ 288 default boolean isPrimitive() { 289 return descriptorString().length() == 1; 290 } 291 292 /** 293 * Returns whether this {@linkplain ClassDesc} describes a class or interface type. 294 * 295 * @return whether this {@linkplain ClassDesc} describes a class or interface type 296 */ 297 default boolean isClassOrInterface() { 298 return descriptorString().startsWith("L"); 299 } 300 301 /** 302 * Returns the component type of this {@linkplain ClassDesc}, if it describes 303 * an array type, or {@code null} otherwise. 304 * 305 * @return a {@linkplain ClassDesc} describing the component type, or {@code null} 306 * if this descriptor does not describe an array type 307 */ 308 default ClassDesc componentType() { 309 return isArray() ? ClassDesc.ofDescriptor(descriptorString().substring(1)) : null; 310 } 311 312 /** 313 * Returns the package name of this {@linkplain ClassDesc}, if it describes 314 * a class or interface type. 315 * 316 * @return the package name, or the empty string if the class is in the 317 * default package, or this {@linkplain ClassDesc} does not describe a class or interface type 318 */ 319 default String packageName() { 320 if (!isClassOrInterface()) 321 return ""; 322 String className = internalToBinary(ConstantUtils.dropFirstAndLastChar(descriptorString())); 323 int index = className.lastIndexOf('.'); 324 return (index == -1) ? "" : className.substring(0, index); 325 } 326 327 /** 328 * Returns a human-readable name for the type described by this descriptor. 329 * 330 * @implSpec 331 * <p>The default implementation returns the simple name 332 * (e.g., {@code int}) for primitive types, the unqualified class name 333 * for class or interface types, or the display name of the component type 334 * suffixed with the appropriate number of {@code []} pairs for array types. 335 * 336 * @return the human-readable name 337 */ 338 default String displayName() { 339 if (isPrimitive()) 340 return Wrapper.forBasicType(descriptorString().charAt(0)).primitiveSimpleName(); 341 else if (isClassOrInterface()) { 342 return descriptorString().substring(Math.max(1, descriptorString().lastIndexOf('/') + 1), 343 descriptorString().length() - 1); 344 } 345 else if (isArray()) { 346 int depth = ConstantUtils.arrayDepth(descriptorString()); 347 ClassDesc c = this; 348 for (int i=0; i<depth; i++) 349 c = c.componentType(); 350 return c.displayName() + "[]".repeat(depth); 351 } 352 else 353 throw new IllegalStateException(descriptorString()); 354 } 355 356 /** 357 * Returns a field type descriptor string for this type 358 * 359 * @return the descriptor string 360 * @jvms 4.3.2 Field Descriptors 361 */ 362 String descriptorString(); 363 364 /** 365 * Compare the specified object with this descriptor for equality. Returns 366 * {@code true} if and only if the specified object is also a 367 * {@linkplain ClassDesc} and both describe the same type. 368 * 369 * @param o the other object 370 * @return whether this descriptor is equal to the other object 371 */ 372 boolean equals(Object o); 373 }