1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 International Business Machines Corp. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v0.5
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v05.html
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.lookup;
14 import org.eclipse.jdt.core.compiler.CharOperation;
15 import org.eclipse.jdt.internal.compiler.ast.Wildcard;
18 * Binding for a type parameter, held by source/binary type or method.
20 public class TypeVariableBinding extends ReferenceBinding {
22 public Binding declaringElement; // binding of declaring type or method
23 public int rank; // declaration rank, can be used to match variable in parameterized type
26 * Denote the first explicit (binding) bound amongst the supertypes (from declaration in source)
27 * If no superclass was specified, then it denotes the first superinterface, or null if none was specified.
29 public ReferenceBinding firstBound;
31 // actual resolved variable supertypes (if no superclass bound, then associated to Object)
32 public ReferenceBinding superclass;
33 public ReferenceBinding[] superInterfaces;
34 public char[] genericTypeSignature;
36 public TypeVariableBinding(char[] sourceName, Binding declaringElement, int rank) {
37 this.sourceName = sourceName;
38 this.declaringElement = declaringElement;
40 this.modifiers = AccPublic | AccGenericSignature; // treat type var as public
41 this.tagBits |= HasTypeVariable;
45 return TYPE_PARAMETER;
49 * Returns true if the argument type satisfies all bounds of the type parameter
51 public boolean boundCheck(Substitution substitution, TypeBinding argumentType) {
52 if (argumentType == NullBinding || this == argumentType)
54 if (!(argumentType instanceof ReferenceBinding || argumentType.isArrayType()))
57 if (argumentType.isWildcard()) {
58 WildcardBinding wildcard = (WildcardBinding) argumentType;
59 switch (wildcard.kind) {
61 if (!boundCheck(substitution, wildcard.bound)) return false;
63 case Wildcard.UNBOUND :
64 if (this == wildcard.typeVariable())
69 // if (this == argumentType)
71 boolean hasSubstitution = substitution != null;
72 if (this.superclass.id != T_JavaLangObject && !argumentType.isCompatibleWith(hasSubstitution ? substitution.substitute(this.superclass) : this.superclass)) {
75 for (int i = 0, length = this.superInterfaces.length; i < length; i++) {
76 if (!argumentType.isCompatibleWith(hasSubstitution ? substitution.substitute(this.superInterfaces[i]) : this.superInterfaces[i])) {
84 * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#canBeInstantiated()
86 public boolean canBeInstantiated() {
90 * Collect the substitutes into a map for certain type variables inside the receiver type
91 * e.g. Collection<T>.findSubstitute(T, Collection<List<X>>): T --> List<X>
93 public void collectSubstitutes(TypeBinding otherType, Map substitutes) {
94 // cannot infer anything from a null type
95 if (otherType == NullBinding) return;
97 TypeBinding[] variableSubstitutes = (TypeBinding[])substitutes.get(this);
98 if (variableSubstitutes != null) {
99 int length = variableSubstitutes.length;
100 for (int i = 0; i < length; i++) {
101 TypeBinding substitute = variableSubstitutes[i];
102 if (substitute == otherType) return; // already there
103 if (substitute == null) {
104 variableSubstitutes[i] = otherType;
108 // no free spot found, need to grow
109 System.arraycopy(variableSubstitutes, 0, variableSubstitutes = new TypeBinding[2*length], 0, length);
110 variableSubstitutes[length] = otherType;
111 substitutes.put(this, variableSubstitutes);
115 public char[] constantPoolName() { /* java/lang/Object */
116 if (this.firstBound != null) {
117 return this.firstBound.constantPoolName();
119 return this.superclass.constantPoolName(); // java/lang/Object
122 * declaringUniqueKey : genericTypeSignature
123 * p.X<T> { ... } --> Lp/X<TT;>;:TT;
125 public char[] computeUniqueKey() {
126 char[] declaringKey = this.declaringElement.computeUniqueKey();
127 int declaringLength = declaringKey.length;
128 char[] sig = genericTypeSignature();
129 int sigLength = sig.length;
130 char[] uniqueKey = new char[declaringLength + 1 + sigLength];
131 System.arraycopy(declaringKey, 0, uniqueKey, 0, declaringLength);
132 uniqueKey[declaringLength] = ':';
133 System.arraycopy(sig, 0, uniqueKey, declaringLength+1, sigLength);
137 * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#debugName()
139 public String debugName() {
140 return new String(this.sourceName);
142 public TypeBinding erasure() {
143 if (this.firstBound != null) {
144 return this.firstBound.erasure();
146 return this.superclass; // java/lang/Object
150 * Find supertype which erases to a given well-known type, or null if not found
151 * (using id avoids triggering the load of well-known type: 73740)
152 * NOTE: only works for erasures of well-known types, as random other types may share
153 * same id though being distincts.
154 * Override super-method since erasure() is answering firstBound (first supertype) already
156 public ReferenceBinding findSuperTypeErasingTo(int erasureId, boolean erasureIsClass) {
158 // if (this.id == erasureId) return this; // no ID for type variable
159 ReferenceBinding currentType = this;
160 // iterate superclass to avoid recording interfaces if searched supertype is class
161 if (erasureIsClass) {
162 while ((currentType = currentType.superclass()) != null) {
163 if (currentType.erasure().id == erasureId) return currentType;
167 ReferenceBinding[][] interfacesToVisit = new ReferenceBinding[5][];
168 int lastPosition = -1;
170 ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
171 if (itsInterfaces != NoSuperInterfaces) {
172 if (++lastPosition == interfacesToVisit.length)
173 System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[lastPosition * 2][], 0, lastPosition);
174 interfacesToVisit[lastPosition] = itsInterfaces;
176 } while ((currentType = currentType.superclass()) != null);
178 for (int i = 0; i <= lastPosition; i++) {
179 ReferenceBinding[] interfaces = interfacesToVisit[i];
180 for (int j = 0, length = interfaces.length; j < length; j++) {
181 if ((currentType = interfaces[j]).erasure().id == erasureId)
184 ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
185 if (itsInterfaces != NoSuperInterfaces) {
186 if (++lastPosition == interfacesToVisit.length)
187 System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[lastPosition * 2][], 0, lastPosition);
188 interfacesToVisit[lastPosition] = itsInterfaces;
195 * Find supertype which erases to a given type, or null if not found
196 * Override super-method since erasure() is answering firstBound (first supertype) already
198 public ReferenceBinding findSuperTypeErasingTo(ReferenceBinding erasure) {
200 if (this == erasure) return this;
201 ReferenceBinding currentType = this;
202 if (erasure.isClass()) {
203 while ((currentType = currentType.superclass()) != null) {
204 if (currentType.erasure() == erasure) return currentType;
208 ReferenceBinding[][] interfacesToVisit = new ReferenceBinding[5][];
209 int lastPosition = -1;
211 ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
212 if (itsInterfaces != NoSuperInterfaces) {
213 if (++lastPosition == interfacesToVisit.length)
214 System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[lastPosition * 2][], 0, lastPosition);
215 interfacesToVisit[lastPosition] = itsInterfaces;
217 } while ((currentType = currentType.superclass()) != null);
219 for (int i = 0; i <= lastPosition; i++) {
220 ReferenceBinding[] interfaces = interfacesToVisit[i];
221 for (int j = 0, length = interfaces.length; j < length; j++) {
222 if ((currentType = interfaces[j]).erasure() == erasure)
225 ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
226 if (itsInterfaces != NoSuperInterfaces) {
227 if (++lastPosition == interfacesToVisit.length)
228 System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[lastPosition * 2][], 0, lastPosition);
229 interfacesToVisit[lastPosition] = itsInterfaces;
237 * T::Ljava/util/Map;:Ljava/io/Serializable;
240 public char[] genericSignature() {
241 StringBuffer sig = new StringBuffer(10);
242 sig.append(this.sourceName).append(':');
243 int interfaceLength = this.superInterfaces.length;
244 if (interfaceLength == 0 || this.firstBound == this.superclass) {
245 sig.append(this.superclass.genericTypeSignature());
247 for (int i = 0; i < interfaceLength; i++) {
248 sig.append(':').append(this.superInterfaces[i].genericTypeSignature());
250 int sigLength = sig.length();
251 char[] genericSignature = new char[sigLength];
252 sig.getChars(0, sigLength, genericSignature, 0);
253 return genericSignature;
256 * T::Ljava/util/Map;:Ljava/io/Serializable;
259 public char[] genericTypeSignature() {
260 if (this.genericTypeSignature != null) return this.genericTypeSignature;
261 return this.genericTypeSignature = CharOperation.concat('T', this.sourceName, ';');
265 * Returns true if the type variable is directly bound to a given type
267 public boolean isErasureBoundTo(TypeBinding type) {
268 if (this.superclass.erasure() == type)
270 for (int i = 0, length = this.superInterfaces.length; i < length; i++) {
271 if (this.superInterfaces[i].erasure() == type)
277 * Returns true if the type was declared as a type variable
279 public boolean isTypeVariable() {
283 * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#readableName()
285 public char[] readableName() {
286 return this.sourceName;
289 ReferenceBinding resolve(LookupEnvironment environment) {
290 if ((this.modifiers & AccUnresolved) == 0)
293 if (this.superclass != null)
294 this.superclass = BinaryTypeBinding.resolveUnresolvedType(this.superclass, environment, true);
295 if (this.firstBound != null)
296 this.firstBound = BinaryTypeBinding.resolveUnresolvedType(this.firstBound, environment, true);
297 ReferenceBinding[] interfaces = this.superInterfaces;
298 for (int i = interfaces.length; --i >= 0;)
299 interfaces[i] = BinaryTypeBinding.resolveUnresolvedType(interfaces[i], environment, true);
300 this.modifiers &= ~AccUnresolved;
302 // finish resolving the types
303 if (this.superclass != null)
304 this.superclass = BinaryTypeBinding.resolveType(this.superclass, environment, true);
305 if (this.firstBound != null)
306 this.firstBound = BinaryTypeBinding.resolveType(this.firstBound, environment, true);
307 for (int i = interfaces.length; --i >= 0;)
308 interfaces[i] = BinaryTypeBinding.resolveType(interfaces[i], environment, true);
313 * @see org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding#shortReadableName()
315 public char[] shortReadableName() {
316 return this.readableName();
318 public ReferenceBinding superclass() {
321 public ReferenceBinding[] superInterfaces() {
322 return superInterfaces;
325 * @see java.lang.Object#toString()
327 public String toString() {
328 StringBuffer buffer = new StringBuffer(10);
329 buffer.append('<').append(this.sourceName);//.append('[').append(this.rank).append(']');
330 if (this.superclass != null && this.firstBound == this.superclass) {
331 buffer.append(" extends ").append(this.superclass.debugName()); //$NON-NLS-1$
333 if (this.superInterfaces != null && this.superInterfaces != NoSuperInterfaces) {
334 if (this.firstBound != this.superclass) {
335 buffer.append(" extends "); //$NON-NLS-1$
337 for (int i = 0, length = this.superInterfaces.length; i < length; i++) {
338 if (i > 0 || this.firstBound == this.superclass) {
339 buffer.append(" & "); //$NON-NLS-1$
341 buffer.append(this.superInterfaces[i].debugName());
345 return buffer.toString();