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 * 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 * @param nestedName the unqualified name of the nested class 166 * @return a {@linkplain ClassDesc} describing the nested class 167 * @throws NullPointerException if any argument is {@code null} 168 * @throws IllegalStateException if this {@linkplain ClassDesc} does not 169 * describe a class or interface type 170 * @throws IllegalArgumentException if the nested class name is invalid 171 */ 172 default ClassDesc nested(String nestedName) { 173 validateMemberName(nestedName); 174 if (!isClassOrInterface()) 175 throw new IllegalStateException("Outer class is not a class or interface type"); 176 return ClassDesc.ofDescriptor(String.format("%s$%s;", dropLastChar(descriptorString()), nestedName)); 177 } 178 179 /** 180 * Returns a {@linkplain ClassDesc} for a nested class of the class or 181 * interface type described by this {@linkplain ClassDesc}. 182 * 183 * @param firstNestedName the unqualified name of the first level of nested class 184 * @param moreNestedNames the unqualified name(s) of the remaining levels of 185 * nested class 186 * @return a {@linkplain ClassDesc} describing the nested class 187 * @throws NullPointerException if any argument is {@code null} 188 * @throws IllegalStateException if this {@linkplain ClassDesc} does not 189 * describe a class or interface type 190 * @throws IllegalArgumentException if the nested class name is invalid 191 */ 192 default ClassDesc nested(String firstNestedName, String... moreNestedNames) { 193 if (!isClassOrInterface()) 194 throw new IllegalStateException("Outer class is not a class or interface type"); 195 return moreNestedNames.length == 0 196 ? nested(firstNestedName) 197 : nested(firstNestedName + Stream.of(moreNestedNames).collect(joining("$", "$", ""))); 198 } 199 200 /** 201 * Returns whether this {@linkplain ClassDesc} describes an array type. 202 * 203 * @return whether this {@linkplain ClassDesc} describes an array type 204 */ 205 default boolean isArray() { 206 return descriptorString().startsWith("["); 207 } 208 209 /** 210 * Returns whether this {@linkplain ClassDesc} describes a primitive type. 211 * 212 * @return whether this {@linkplain ClassDesc} describes a primitive type 213 */ 214 default boolean isPrimitive() { 215 return descriptorString().length() == 1; 216 } 217 218 /** 219 * Returns whether this {@linkplain ClassDesc} describes a class or interface type. 220 * 221 * @return whether this {@linkplain ClassDesc} describes a class or interface type 222 */ 223 default boolean isClassOrInterface() { 224 return descriptorString().startsWith("L"); 225 } 226 227 /** 228 * Returns the component type of this {@linkplain ClassDesc}, if it describes 229 * an array type, or {@code null} otherwise. 230 * 231 * @return a {@linkplain ClassDesc} describing the component type, or {@code null} 232 * if this descriptor does not describe an array type 233 */ 234 default ClassDesc componentType() { 235 return isArray() ? ClassDesc.ofDescriptor(descriptorString().substring(1)) : null; 236 } 237 238 /** 239 * Returns the package name of this {@linkplain ClassDesc}, if it describes 240 * a class or interface type. 241 * 242 * @return the package name, or the empty string if the class is in the 243 * default package, or this {@linkplain ClassDesc} does not describe a class or interface type 244 */ 245 default String packageName() { 246 if (!isClassOrInterface()) 247 return ""; 248 String className = internalToBinary(ConstantUtils.dropFirstAndLastChar(descriptorString())); 249 int index = className.lastIndexOf('.'); 250 return (index == -1) ? "" : className.substring(0, index); 251 } 252 253 /** 254 * Returns a human-readable name for the type described by this descriptor. 255 * 256 * @implSpec 257 * <p>The default implementation returns the simple name 258 * (e.g., {@code int}) for primitive types, the unqualified class name 259 * for class or interface types, or the display name of the component type 260 * suffixed with the appropriate number of {@code []} pairs for array types. 261 * 262 * @return the human-readable name 263 */ 264 default String displayName() { 265 if (isPrimitive()) 266 return Wrapper.forBasicType(descriptorString().charAt(0)).primitiveSimpleName(); 267 else if (isClassOrInterface()) { 268 return descriptorString().substring(Math.max(1, descriptorString().lastIndexOf('/') + 1), 269 descriptorString().length() - 1); 270 } 271 else if (isArray()) { 272 int depth = ConstantUtils.arrayDepth(descriptorString()); 273 ClassDesc c = this; 274 for (int i=0; i<depth; i++) 275 c = c.componentType(); 276 return c.displayName() + "[]".repeat(depth); 277 } 278 else 279 throw new IllegalStateException(descriptorString()); 280 } 281 282 /** 283 * Returns a field type descriptor string for this type 284 * 285 * @return the descriptor string 286 * @jvms 4.3.2 Field Descriptors 287 */ 288 String descriptorString(); 289 290 /** 291 * Compare the specified object with this descriptor for equality. Returns 292 * {@code true} if and only if the specified object is also a 293 * {@linkplain ClassDesc} and both describe the same type. 294 * 295 * @param o the other object 296 * @return whether this descriptor is equal to the other object 297 */ 298 boolean equals(Object o); 299 }