/******************************************************************************* | |
* Copyright (c) 2000, 2004 International Business Machines Corp. and others. | |
* All rights reserved. This program and the accompanying materials | |
* are made available under the terms of the Common Public License v0.5 | |
* which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/cpl-v05.html | |
* | |
* Contributors: | |
* IBM Corporation - initial API and implementation | |
******************************************************************************/ | |
package org.eclipse.wst.jsdt.internal.compiler.lookup; | |
import java.util.Map; | |
import org.eclipse.wst.jsdt.core.compiler.CharOperation; | |
import org.eclipse.wst.jsdt.internal.compiler.ast.Wildcard; | |
/** | |
* Binding for a type parameter, held by source/binary type or method. | |
*/ | |
public class TypeVariableBinding extends ReferenceBinding { | |
public Binding declaringElement; // binding of declaring type or method | |
public int rank; // declaration rank, can be used to match variable in parameterized type | |
/** | |
* Denote the first explicit (binding) bound amongst the supertypes (from declaration in source) | |
* If no superclass was specified, then it denotes the first superinterface, or null if none was specified. | |
*/ | |
public ReferenceBinding firstBound; | |
// actual resolved variable supertypes (if no superclass bound, then associated to Object) | |
public ReferenceBinding superclass; | |
public ReferenceBinding[] superInterfaces; | |
public char[] genericTypeSignature; | |
public TypeVariableBinding(char[] sourceName, Binding declaringElement, int rank) { | |
this.sourceName = sourceName; | |
this.declaringElement = declaringElement; | |
this.rank = rank; | |
this.modifiers = AccPublic | AccGenericSignature; // treat type var as public | |
this.tagBits |= HasTypeVariable; | |
} | |
/** | |
* Returns true if the argument type satisfies all bounds of the type parameter | |
*/ | |
public boolean boundCheck(Substitution substitution, TypeBinding argumentType) { | |
if (argumentType == NullBinding) | |
return true; | |
if (!(argumentType instanceof ReferenceBinding || argumentType.isArrayType())) | |
return false; | |
if (this.superclass.id != T_Object && !argumentType.isCompatibleWith(substitution.substitute(this.superclass))) { | |
return false; | |
} | |
for (int i = 0, length = this.superInterfaces.length; i < length; i++) { | |
if (!argumentType.isCompatibleWith(substitution.substitute(this.superInterfaces[i]))) { | |
return false; | |
} | |
} | |
if (argumentType.isWildcard()) { | |
WildcardBinding wildcard = (WildcardBinding) argumentType; | |
if (wildcard.kind == Wildcard.SUPER) { | |
if (!boundCheck(substitution, wildcard.bound)) return false; | |
} | |
} | |
return true; | |
} | |
/** | |
* Collect the substitutes into a map for certain type variables inside the receiver type | |
* e.g. Collection<T>.findSubstitute(T, Collection<List<X>>): T --> List<X> | |
*/ | |
public void collectSubstitutes(TypeBinding otherType, Map substitutes) { | |
// cannot infer anything from a null type | |
if (otherType == NullBinding) return; | |
TypeBinding[] variableSubstitutes = (TypeBinding[])substitutes.get(this); | |
if (variableSubstitutes != null) { | |
int length = variableSubstitutes.length; | |
for (int i = 0; i < length; i++) { | |
if (variableSubstitutes[i] == otherType) return; // already there | |
if (variableSubstitutes[i] == null) { | |
variableSubstitutes[i] = otherType; | |
return; | |
} | |
} | |
// no free spot found, need to grow | |
System.arraycopy(variableSubstitutes, 0, variableSubstitutes = new TypeBinding[2*length], 0, length); | |
variableSubstitutes[length] = otherType; | |
substitutes.put(this, variableSubstitutes); | |
} | |
} | |
public char[] constantPoolName() { /* java/lang/Object */ | |
if (this.firstBound != null) { | |
return this.firstBound.constantPoolName(); | |
} | |
return this.superclass.constantPoolName(); // java/lang/Object | |
} | |
/** | |
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding#debugName() | |
*/ | |
public String debugName() { | |
return new String(this.sourceName); | |
} | |
public TypeBinding erasure() { | |
if (this.firstBound != null) { | |
return this.firstBound.erasure(); | |
} | |
return this.superclass; // java/lang/Object | |
} | |
/** | |
* T::Ljava/util/Map;:Ljava/io/Serializable; | |
* T:LY<TT;> | |
*/ | |
public char[] genericSignature() { | |
StringBuffer sig = new StringBuffer(10); | |
sig.append(this.sourceName).append(':'); | |
int interfaceLength = this.superInterfaces.length; | |
if (interfaceLength == 0 || this.firstBound == this.superclass) { | |
sig.append(this.superclass.genericTypeSignature()); | |
} | |
for (int i = 0; i < interfaceLength; i++) { | |
sig.append(':').append(this.superInterfaces[i].genericTypeSignature()); | |
} | |
int sigLength = sig.length(); | |
char[] genericSignature = new char[sigLength]; | |
sig.getChars(0, sigLength, genericSignature, 0); | |
return genericSignature; | |
} | |
/** | |
* T::Ljava/util/Map;:Ljava/io/Serializable; | |
* T:LY<TT;> | |
*/ | |
public char[] genericTypeSignature() { | |
if (this.genericTypeSignature != null) return this.genericTypeSignature; | |
return this.genericTypeSignature = CharOperation.concat('T', this.sourceName, ';'); | |
} | |
/** | |
* Returns true if a type is identical to another one, | |
* or for generic types, true if compared to its raw type. | |
*/ | |
public boolean isEquivalentTo(TypeBinding otherType) { | |
if (this == otherType) return true; | |
if (otherType == null) return false; | |
if (otherType.isWildcard()) // wildcard | |
return ((WildcardBinding) otherType).boundCheck(this); | |
return false; | |
} | |
/** | |
* Returns true if the type variable is directly bound to a given type | |
*/ | |
public boolean isErasureBoundTo(TypeBinding type) { | |
if (this.superclass.erasure() == type) | |
return true; | |
for (int i = 0, length = this.superInterfaces.length; i < length; i++) { | |
if (this.superInterfaces[i].erasure() == type) | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Returns true if the type was declared as a type variable | |
*/ | |
public boolean isTypeVariable() { | |
return true; | |
} | |
/** | |
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding#readableName() | |
*/ | |
public char[] readableName() { | |
return this.sourceName; | |
} | |
/** | |
* @see org.eclipse.wst.jsdt.internal.compiler.lookup.ReferenceBinding#shortReadableName() | |
*/ | |
public char[] shortReadableName() { | |
return this.readableName(); | |
} | |
public ReferenceBinding superclass() { | |
return superclass; | |
} | |
public ReferenceBinding[] superInterfaces() { | |
return superInterfaces; | |
} | |
/** | |
* @see java.lang.Object#toString() | |
*/ | |
public String toString() { | |
StringBuffer buffer = new StringBuffer(10); | |
buffer.append('<').append(this.sourceName);//.append('[').append(this.rank).append(']'); | |
if (this.superclass != null && this.firstBound == this.superclass) { | |
buffer.append(" extends ").append(this.superclass.debugName()); //$NON-NLS-1$ | |
} | |
if (this.superInterfaces != null && this.superInterfaces != NoSuperInterfaces) { | |
if (this.firstBound != this.superclass) { | |
buffer.append(" extends "); //$NON-NLS-1$ | |
} | |
for (int i = 0, length = this.superInterfaces.length; i < length; i++) { | |
if (i > 0 || this.firstBound == this.superclass) { | |
buffer.append(" & "); //$NON-NLS-1$ | |
} | |
buffer.append(this.superInterfaces[i].debugName()); | |
} | |
} | |
buffer.append('>'); | |
return buffer.toString(); | |
} | |
} |