1 /* 2 * Copyright (c) 2018, 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 validateMemberName(requireNonNull(className)); 101 return ofDescriptor(String.format("L%s%s%s;", 102 binaryToInternal(packageName), 103 (packageName.length() > 0 ? "/" : ""), 104 className)); 105 } 106 107 /** 108 * Returns a {@linkplain ClassDesc} given a descriptor string for a class, 109 * interface, array, or primitive type. 110 * 111 * @apiNote 112 * 113 * A field type descriptor string for a non-array type is either 114 * a one-letter code corresponding to a primitive type 115 * ({@code "J", "I", "C", "S", "B", "D", "F", "Z", "V"}), or the letter {@code "L"}, followed 116 * by the fully qualified binary name of a class, followed by {@code ";"}. 117 * A field type descriptor for an array type is the character {@code "["} 118 * followed by the field descriptor for the component type. Examples of 119 * valid type descriptor strings include {@code "Ljava/lang/String;"}, {@code "I"}, 120 * {@code "[I"}, {@code "V"}, {@code "[Ljava/lang/String;"}, etc. 121 * See JVMS 4.3.2 ("Field Descriptors") for more detail. 122 * 123 * @param descriptor a field descriptor string 124 * @return a {@linkplain ClassDesc} describing the desired class 125 * @throws NullPointerException if any argument is {@code null} 126 * @throws IllegalArgumentException if the name string is not in the 127 * correct format 128 * @jvms 4.3.2 Field Descriptors 129 */ 130 static ClassDesc ofDescriptor(String descriptor) { 131 requireNonNull(descriptor); 132 return (descriptor.length() == 1) 133 ? new PrimitiveClassDescImpl(descriptor) 134 : new ReferenceClassDescImpl(descriptor); 135 } 136 137 /** 138 * Returns a {@linkplain ClassDesc} for an array type whose component type 139 * is described by this {@linkplain ClassDesc}. 140 * 141 * @return a {@linkplain ClassDesc} describing the array type 142 */ 143 default ClassDesc arrayType() { 144 return arrayType(1); 145 } 146 147 /** 148 * Returns a {@linkplain ClassDesc} for an array type of the specified rank, 149 * whose component type is described by this {@linkplain ClassDesc}. 150 * 151 * @param rank the rank of the array 152 * @return a {@linkplain ClassDesc} describing the array type 153 * @throws IllegalArgumentException if the rank is zero or negative 154 */ 155 default ClassDesc arrayType(int rank) { 156 if (rank <= 0) 157 throw new IllegalArgumentException("rank: " + rank); 158 return ClassDesc.ofDescriptor("[".repeat(rank) + descriptorString()); 159 } 160 161 /** 162 * Returns a {@linkplain ClassDesc} for a nested class of the class or 163 * interface type described by this {@linkplain ClassDesc}. 164 * 165 * @apiNote 166 * 167 * Example: If descriptor {@code d} describes the class {@code java.util.Map}, a 168 * descriptor for the class {@code java.util.Map.Entry} could be obtained 169 * by {@code d.nested("Entry")}. 170 * 171 * @param nestedName the unqualified name of the nested class 172 * @return a {@linkplain ClassDesc} describing the nested class 173 * @throws NullPointerException if any argument is {@code null} 174 * @throws IllegalStateException if this {@linkplain ClassDesc} does not 175 * describe a class or interface type 176 * @throws IllegalArgumentException if the nested class name is invalid 177 */ 178 default ClassDesc nested(String nestedName) { 179 validateMemberName(nestedName); 180 if (!isClassOrInterface()) 181 throw new IllegalStateException("Outer class is not a class or interface type"); 182 return ClassDesc.ofDescriptor(String.format("%s$%s;", dropLastChar(descriptorString()), nestedName)); 183 } 184 185 /** 186 * Returns a {@linkplain ClassDesc} for a nested class of the class or 187 * interface type described by this {@linkplain ClassDesc}. 188 * 189 * @param firstNestedName the unqualified name of the first level of nested class 190 * @param moreNestedNames the unqualified name(s) of the remaining levels of 191 * nested class 192 * @return a {@linkplain ClassDesc} describing the nested class 193 * @throws NullPointerException if any 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 */ 198 default ClassDesc nested(String firstNestedName, String... moreNestedNames) { 199 if (!isClassOrInterface()) 200 throw new IllegalStateException("Outer class is not a class or interface type"); 201 return moreNestedNames.length == 0 202 ? nested(firstNestedName) 203 : nested(firstNestedName + Stream.of(moreNestedNames).collect(joining("$", "$", ""))); 204 } 205 206 /** 207 * Returns whether this {@linkplain ClassDesc} describes an array type. 208 * 209 * @return whether this {@linkplain ClassDesc} describes an array type 210 */ 211 default boolean isArray() { 212 return descriptorString().startsWith("["); 213 } 214 215 /** 216 * Returns whether this {@linkplain ClassDesc} describes a primitive type. 217 * 218 * @return whether this {@linkplain ClassDesc} describes a primitive type 219 */ 220 default boolean isPrimitive() { 221 return descriptorString().length() == 1; 222 } 223 224 /** 225 * Returns whether this {@linkplain ClassDesc} describes a class or interface type. 226 * 227 * @return whether this {@linkplain ClassDesc} describes a class or interface type 228 */ 229 default boolean isClassOrInterface() { 230 return descriptorString().startsWith("L"); 231 } 232 233 /** 234 * Returns the component type of this {@linkplain ClassDesc}, if it describes 235 * an array type, or {@code null} otherwise. 236 * 237 * @return a {@linkplain ClassDesc} describing the component type, or {@code null} 238 * if this descriptor does not describe an array type 239 */ 240 default ClassDesc componentType() { 241 return isArray() ? ClassDesc.ofDescriptor(descriptorString().substring(1)) : null; 242 } 243 244 /** 245 * Returns the package name of this {@linkplain ClassDesc}, if it describes 246 * a class or interface type. 247 * 248 * @return the package name, or the empty string if the class is in the 249 * default package, or this {@linkplain ClassDesc} does not describe a class or interface type 250 */ 251 default String packageName() { 252 if (!isClassOrInterface()) 253 return ""; 254 String className = internalToBinary(ConstantUtils.dropFirstAndLastChar(descriptorString())); 255 int index = className.lastIndexOf('.'); 256 return (index == -1) ? "" : className.substring(0, index); 257 } 258 259 /** 260 * Returns a human-readable name for the type described by this descriptor. 261 * 262 * @implSpec 263 * <p>The default implementation returns the simple name 264 * (e.g., {@code int}) for primitive types, the unqualified class name 265 * for class or interface types, or the display name of the component type 266 * suffixed with the appropriate number of {@code []} pairs for array types. 267 * 268 * @return the human-readable name 269 */ 270 default String displayName() { 271 if (isPrimitive()) 272 return Wrapper.forBasicType(descriptorString().charAt(0)).primitiveSimpleName(); 273 else if (isClassOrInterface()) { 274 return descriptorString().substring(Math.max(1, descriptorString().lastIndexOf('/') + 1), 275 descriptorString().length() - 1); 276 } 277 else if (isArray()) { 278 int depth = ConstantUtils.arrayDepth(descriptorString()); 279 ClassDesc c = this; 280 for (int i=0; i<depth; i++) 281 c = c.componentType(); 282 return c.displayName() + "[]".repeat(depth); 283 } 284 else 285 throw new IllegalStateException(descriptorString()); 286 } 287 288 /** 289 * Returns a field type descriptor string for this type 290 * 291 * @return the descriptor string 292 * @jvms 4.3.2 Field Descriptors 293 */ 294 String descriptorString(); 295 296 /** 297 * Compare the specified object with this descriptor for equality. Returns 298 * {@code true} if and only if the specified object is also a 299 * {@linkplain ClassDesc} and both describe the same type. 300 * 301 * @param o the other object 302 * @return whether this descriptor is equal to the other object 303 */ 304 boolean equals(Object o); 305 }