1 /******************************************************************************* 2 * Copyright (c) 2005, 2019 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.jdt.core; 15 16 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; 17 import org.eclipse.jdt.internal.core.util.KeyKind; 18 import org.eclipse.jdt.internal.core.util.KeyToSignature; 19 20 /** 21 * Utility class to decode or create a binding key. 22 * <p> 23 * This class is not intended to be subclassed by clients. 24 * </p> 25 * 26 * @see org.eclipse.jdt.core.dom.IBinding#getKey() 27 * @since 3.1 28 */ 29 public final class BindingKey { 30 31 private String key; 32 33 /** 34 * Creates a new binding key. 35 * 36 * @param key the key to decode 37 */ BindingKey(String key)38 public BindingKey(String key) { 39 this.key = key; 40 } 41 42 /** 43 * Creates a new array type binding key from the given type binding key and the given array dimension. 44 * <p> 45 * For example: 46 * <pre> 47 * <code> 48 * createArrayTypeBindingKey("Ljava/lang/Object;", 1) -> "[Ljava/lang/Object;" 49 * createArrayTypeBindingKey("I", 2) -> "[[I" 50 * </code> 51 * </pre> 52 * 53 * @param typeKey the binding key of the given type 54 * @param arrayDimension the given array dimension 55 * @return a new array type binding key 56 */ createArrayTypeBindingKey(String typeKey, int arrayDimension)57 public static String createArrayTypeBindingKey(String typeKey, int arrayDimension) { 58 // Note this implementation is heavily dependent on ArrayTypeBinding#computeUniqueKey() 59 StringBuffer buffer = new StringBuffer(); 60 while (arrayDimension-- > 0) 61 buffer.append('['); 62 buffer.append(typeKey); 63 return buffer.toString(); 64 } 65 66 /** 67 * Creates a new parameterized type binding key from the given generic type binding key and the given argument type binding keys. 68 * If the argument type keys array is empty, then a raw type binding key is created. 69 * <p> 70 * For example: 71 * <pre> 72 * <code> 73 * createParameterizedTypeBindingKey( 74 * "Ljava/util/Map<TK;TV;>;", 75 * new String[] {"Ljava/lang/String;", "Ljava/lang/Object;"}) -> 76 * "Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;" 77 * createParameterizedTypeBindingKey( 78 * "Ljava/util/List<TE;>;", new String[] {}) -> 79 * "Ljava/util/List<>;" 80 * </code> 81 * </pre> 82 * 83 * @param genericTypeKey the binding key of the generic type 84 * @param argumentTypeKeys the possibly empty list of binding keys of argument types 85 * @return a new parameterized type binding key 86 */ createParameterizedTypeBindingKey(String genericTypeKey, String[] argumentTypeKeys)87 public static String createParameterizedTypeBindingKey(String genericTypeKey, String[] argumentTypeKeys) { 88 // Note this implementation is heavily dependent on ParameterizedTypeBinding#computeUniqueKey() and its subclasses 89 StringBuffer buffer = new StringBuffer(); 90 buffer.append(Signature.getTypeErasure(genericTypeKey)); 91 buffer.insert(buffer.length()-1, '<'); 92 for (int i = 0, length = argumentTypeKeys.length; i < length; i++) { 93 String argumentTypeKey = argumentTypeKeys[i]; 94 buffer.insert(buffer.length()-1, argumentTypeKey); 95 } 96 buffer.insert(buffer.length()-1, '>'); 97 return buffer.toString(); 98 } 99 100 /** 101 * Creates a new type binding key from the given type name. The type name must be either 102 * a fully qualified name, an array type name or a primitive type name. 103 * If the type name is fully qualified, then it is expected to be dot-based. 104 * Note that inner types, generic types and parameterized types are not supported. 105 * <p> 106 * For example: 107 * <pre> 108 * <code> 109 * createTypeBindingKey("int") -> "I" 110 * createTypeBindingKey("java.lang.String") -> "Ljava/lang/String;" 111 * createTypeBindingKey("boolean[]") -> "[Z" 112 * </code> 113 * </pre> 114 * 115 * @param typeName the possibly qualified type name 116 * @return a new type binding key 117 */ createTypeBindingKey(String typeName)118 public static String createTypeBindingKey(String typeName) { 119 // Note this implementation is heavily dependent on TypeBinding#computeUniqueKey() and its subclasses 120 return Signature.createTypeSignature(typeName.replace('.', '/'), true/*resolved*/); 121 } 122 123 /** 124 * Creates a new type variable binding key from the given type variable name and the given declaring key. 125 * The declaring key can either be a type binding key or a method binding key. 126 * <p> 127 * For example: 128 * <pre> 129 * <code> 130 * createTypeVariableBindingKey("T", "Ljava/util/List<TE;>;") -> 131 * "Ljava/util/List<TE;>;:TT;" 132 * createTypeVariableBindingKey("SomeTypeVariable", "Lp/X;.foo()V") -> 133 * "Lp/X;.foo()V:TSomeTypeVariable;" 134 * </code> 135 * </pre> 136 * 137 * @param typeVariableName the name of the given type variable 138 * @param declaringKey the binding key of the type or method the type variable belongs to 139 * @return a new type variable binding key 140 */ createTypeVariableBindingKey(String typeVariableName, String declaringKey)141 public static String createTypeVariableBindingKey(String typeVariableName, String declaringKey) { 142 // Note this implementation is heavily dependent on TypeVariableBinding#computeUniqueKey() 143 StringBuffer buffer = new StringBuffer(); 144 buffer.append(declaringKey); 145 buffer.append(':'); 146 buffer.append('T'); 147 buffer.append(typeVariableName); 148 buffer.append(';'); 149 return buffer.toString(); 150 } 151 152 /** 153 * Creates a new wildcard type binding key from the given type binding key and the given wildcard kind 154 * (one of {@link Signature#C_STAR}, {@link Signature#C_SUPER}, or {@link Signature#C_EXTENDS}. 155 * If the wildcard is {@link Signature#C_STAR}, the given type binding key is ignored. 156 * <p> 157 * For example: 158 * <pre> 159 * <code> 160 * createWilcardTypeBindingKey(null, Signature.C_STAR) -> "*" 161 * createWilcardTypeBindingKey("Ljava/util/List<TE;>;", 162 * Signature.C_SUPER) -> "-Ljava/util/List<TE;>;" 163 * createWilcardTypeBindingKey("Ljava/util/ArrayList;", Signature.C_EXTENDS) -> 164 * "+Ljava/util/ArrayList;" 165 * </code> 166 * </pre> 167 * 168 * @param typeKey the binding key of the given type 169 * @param kind one of {@link Signature#C_STAR}, {@link Signature#C_SUPER}, or {@link Signature#C_EXTENDS} 170 * @return a new wildcard type binding key 171 * @deprecated This method is missing crucial information necessary for proper wildcard binding key creation. 172 * @see org.eclipse.jdt.core.BindingKey#createWildcardTypeBindingKey(String, char, String, int) 173 */ createWilcardTypeBindingKey(String typeKey, char kind)174 public static String createWilcardTypeBindingKey(String typeKey, char kind) { 175 // Note this implementation is supposed to closely follow the behavior in WildcardBinding#computeUniqueKey() 176 // but it doesn't and hence the deprecation. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=234609 177 switch (kind) { 178 case Signature.C_STAR: 179 return "*"; //$NON-NLS-1$ 180 case Signature.C_SUPER: 181 return '-' + typeKey; 182 case Signature.C_EXTENDS: 183 return '+' + typeKey; 184 } 185 return null; 186 } 187 188 /** 189 * Creates a new wildcard type binding key from the given generic type binding key, the given wildcard 190 * kind (one of {@link Signature#C_STAR}, {@link Signature#C_SUPER}, or {@link Signature#C_EXTENDS} 191 * the given bound type binding key and the given rank. If the wildcard kind is {@link Signature#C_STAR}, 192 * the given bound type binding key is ignored. 193 * <p> 194 * For example: 195 * <pre> 196 * <code> 197 * createWildcardTypeBindingKey("Ljava/util/ArrayList;", Signature.C_STAR, null, 0) -> "Ljava/util/ArrayList;{0}*" 198 * createWildcardTypeBindingKey("Ljava/util/ArrayList;", Signature.C_SUPER, "Ljava/lang/String;", 0) -> "Ljava/util/ArrayList;{0}-Ljava/lang/String;" 199 * createWildcardTypeBindingKey("Ljava/util/HashMap;", Signature.C_EXTENDS, "Ljava/lang/String;", 1) -> 200 * "Ljava/util/HashMap;{1}+Ljava/lang/String;" 201 * </code> 202 * </pre> 203 * 204 * @param genericTypeKey the binding key of the generic type 205 * @param boundKind one of {@link Signature#C_STAR}, {@link Signature#C_SUPER}, or {@link Signature#C_EXTENDS} 206 * @param boundTypeKey the binding key of the bounding type. 207 * @param rank the relative position of this wild card type in the parameterization of the generic type. 208 * @return a new wildcard type binding key 209 * @since 3.5 210 */ 211 createWildcardTypeBindingKey(String genericTypeKey, char boundKind, String boundTypeKey, int rank)212 public static String createWildcardTypeBindingKey(String genericTypeKey, char boundKind, String boundTypeKey, int rank) { 213 // Note this implementation is heavily dependent on WildcardBinding#computeUniqueKey() 214 String wildCardKey; 215 switch (boundKind) { 216 case Signature.C_STAR: 217 wildCardKey = new String(TypeConstants.WILDCARD_STAR); 218 break; 219 case Signature.C_SUPER: 220 wildCardKey = new String(TypeConstants.WILDCARD_MINUS) + boundTypeKey; 221 break; 222 case Signature.C_EXTENDS: 223 wildCardKey = new String(TypeConstants.WILDCARD_PLUS) + boundTypeKey; 224 break; 225 default: 226 return null; 227 } 228 return genericTypeKey + '{' + rank + '}' + wildCardKey; 229 } 230 231 /** 232 * Returns the binding key of the declaring type of the element represented by this binding key. If the binding key 233 * does not represent a member or if the member doesn't have a declaring type, returns <code>null</code>. 234 * 235 * <p> 236 * Note that only binding keys for references to methods and fields 237 * are fully supported. The binding keys for declarations will not have type parameters. 238 * 239 * @return the type binding key or <code>null</code> 240 * @since 3.7.1 241 */ getDeclaringType()242 public BindingKey getDeclaringType() { 243 int end = this.key.lastIndexOf(Signature.C_DOT); 244 if (end == -1) { 245 end = this.key.lastIndexOf(Signature.C_DOLLAR); // for inner types 246 if (end == -1) return null; 247 } 248 KeyKind kind = new KeyKind(this.key); 249 kind.parse(); 250 if ((kind.flags & KeyKind.F_LOCAL_VAR) != 0) { 251 // declaring type for locals doesn't make sense, hence return null. 252 return null; 253 } 254 String typeKey = this.key.substring(0, end); 255 if (typeKey.charAt(typeKey.length()-1) != Signature.C_SEMICOLON) { 256 typeKey += Signature.C_SEMICOLON; 257 } 258 return new BindingKey(typeKey); 259 } 260 /** 261 * Returns the thrown exception signatures of the element represented by this binding key. 262 * If this binding key does not represent a method or does not throw any exception, 263 * returns an empty array. 264 * 265 * @return the thrown exceptions signatures 266 * @since 3.3 267 */ getThrownExceptions()268 public String[] getThrownExceptions() { 269 KeyToSignature keyToSignature = new KeyToSignature(this.key, KeyToSignature.THROWN_EXCEPTIONS); 270 keyToSignature.parse(); 271 return keyToSignature.getThrownExceptions(); 272 } 273 274 /** 275 * Returns the type argument signatures of the element represented by this binding key. 276 * If this binding key doesn't represent a parameterized type or a parameterized method, 277 * returns an empty array. 278 * 279 * @return the type argument signatures 280 */ getTypeArguments()281 public String[] getTypeArguments() { 282 KeyToSignature keyToSignature = new KeyToSignature(this.key, KeyToSignature.TYPE_ARGUMENTS); 283 keyToSignature.parse(); 284 return keyToSignature.getTypeArguments(); 285 } 286 287 /** 288 * Returns whether this binding key represents a raw type. 289 * 290 * @return whether this binding key represents a raw type 291 */ isRawType()292 public boolean isRawType() { 293 KeyKind kind = new KeyKind(this.key); 294 kind.parse(); 295 return (kind.flags & KeyKind.F_RAW_TYPE) != 0; 296 } 297 298 /** 299 * Returns whether this binding key represents a parameterized type, or if its declaring type is a parameterized type. 300 * 301 * @return whether this binding key represents a parameterized type 302 */ isParameterizedType()303 public boolean isParameterizedType() { 304 KeyKind kind = new KeyKind(this.key); 305 kind.parse(); 306 return (kind.flags & KeyKind.F_PARAMETERIZED_TYPE) != 0; 307 } 308 309 /** 310 * Returns whether this binding key represents a parameterized method, or if its declaring method is a parameterized method. 311 * 312 * @return whether this binding key represents a parameterized method 313 */ isParameterizedMethod()314 public boolean isParameterizedMethod() { 315 KeyKind kind = new KeyKind(this.key); 316 kind.parse(); 317 return (kind.flags & KeyKind.F_PARAMETERIZED_METHOD) != 0; 318 } 319 320 /** 321 * Transforms this binding key into a resolved signature. 322 * If this binding key represents a field, the returned signature is 323 * the field type's signature. 324 * 325 * @return the resolved signature for this binding key 326 * @see Signature 327 * @since 3.2 328 */ toSignature()329 public String toSignature() { 330 KeyToSignature keyToSignature = new KeyToSignature(this.key, KeyToSignature.SIGNATURE); 331 keyToSignature.parse(); 332 return keyToSignature.signature.toString(); 333 } 334 335 @Override toString()336 public String toString() { 337 return this.key; 338 } 339 } 340