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&lt;TK;TV;&gt;;",
75 	 *     new String[] {"Ljava/lang/String;", "Ljava/lang/Object;"}) -&gt;
76 	 *       "Ljava/util/Map&lt;Ljava/lang/String;Ljava/lang/Object;&gt;;"
77 	 * createParameterizedTypeBindingKey(
78 	 *     "Ljava/util/List&lt;TE;&gt;;", new String[] {}) -&gt;
79 	 *       "Ljava/util/List&lt;&gt;;"
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&lt;TE;&gt;;") -&gt;
131 	 *   "Ljava/util/List&lt;TE;&gt;;:TT;"
132 	 * createTypeVariableBindingKey("SomeTypeVariable", "Lp/X;.foo()V") -&gt;
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) -&gt; "*"
161 	 * createWilcardTypeBindingKey("Ljava/util/List&lt;TE;&gt;;",
162 	 *    Signature.C_SUPER) -&gt; "-Ljava/util/List&lt;TE;&gt;;"
163 	 * createWilcardTypeBindingKey("Ljava/util/ArrayList;", Signature.C_EXTENDS) -&gt;
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) -&gt; "Ljava/util/ArrayList;{0}*"
198 	 * createWildcardTypeBindingKey("Ljava/util/ArrayList;", Signature.C_SUPER, "Ljava/lang/String;", 0) -&gt; "Ljava/util/ArrayList;{0}-Ljava/lang/String;"
199 	 * createWildcardTypeBindingKey("Ljava/util/HashMap;", Signature.C_EXTENDS, "Ljava/lang/String;", 1) -&gt;
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