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