blob: d67bbd8dca3c156c32283e233ee1f0f3ac5072bd [file] [log] [blame]
/* *******************************************************************
* Copyright (c) 2005-2010 Contributors.
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v1.0
* which accompanies this distribution and is available at
* http://eclipse.org/legal/epl-v10.html
* ******************************************************************/
package org.aspectj.weaver;
import java.io.IOException;
/**
* Represents a type variable with possible bounds.
*
* @author Adrian Colyer
* @author Andy Clement
*/
public class TypeVariable {
public static final TypeVariable[] NONE = new TypeVariable[0];
// the name of the type variable as recorded in the generic signature
private String name;
// index
private int rank;
// computed as required: either ==superclass or ==superInterfaces[0] or is OBJECT
private UnresolvedType firstbound;
// the upper bound of the type variable. From the extends clause, eg. T extends Number
private UnresolvedType superclass;
// any additional upper (interface) bounds. from the extends clause, e.g. T extends Number & Comparable
private UnresolvedType[] superInterfaces = UnresolvedType.NONE;
// It would be nice to push this field onto the TypeVariableDeclaringElement
// interface (a getKind()) but at the moment we don't always guarantee
// to set the declaring element (eclipse seems to utilise the knowledge of
// what declared the type variable, but we dont yet...)
public static final int UNKNOWN = -1;
public static final int METHOD = 1;
public static final int TYPE = 2;
// What kind of element declared this type variable?
private int declaringElementKind = UNKNOWN;
private TypeVariableDeclaringElement declaringElement;
// whether or not the bounds of this type variable have been resolved
public boolean isResolved = false;
// Is this type variable in the process of being resolved (allows for something self-referential like Enum)
private boolean beingResolved = false;
/**
* Constructor for an unbound type variable, eg. 'T'
*/
public TypeVariable(String name) {
this.name = name;
}
public TypeVariable(String name, UnresolvedType anUpperBound) {
this(name);
this.superclass = anUpperBound;
}
public TypeVariable(String name, UnresolvedType anUpperBound, UnresolvedType[] superInterfaces) {
this(name, anUpperBound);
this.superInterfaces = superInterfaces;
}
/**
* @return the first bound, either the superclass or if non is specified the first interface or if non are specified then OBJECT
*/
public UnresolvedType getFirstBound() {
if (firstbound != null) {
return firstbound;
}
if (superclass == null || superclass.getSignature().equals("Ljava/lang/Object;")) {
if (superInterfaces.length > 0) {
firstbound = superInterfaces[0];
} else {
firstbound = UnresolvedType.OBJECT;
}
} else {
firstbound = superclass;
}
return firstbound;
}
public UnresolvedType getUpperBound() {
return superclass;
}
public UnresolvedType[] getSuperInterfaces() {
return superInterfaces;
}
public String getName() {
return name;
}
/**
* resolve all the bounds of this type variable
*/
public TypeVariable resolve(World world) {
if (isResolved) {
return this;
}
if (beingResolved) {
return this;
}
beingResolved = true;
TypeVariable resolvedTVar = null;
if (declaringElement != null) {
// resolve by finding the real type var that we refer to...
if (declaringElementKind == TYPE) {
UnresolvedType declaring = (UnresolvedType) declaringElement;
ReferenceType rd = (ReferenceType) declaring.resolve(world);
TypeVariable[] tVars = rd.getTypeVariables();
for (int i = 0; i < tVars.length; i++) {
if (tVars[i].getName().equals(getName())) {
resolvedTVar = tVars[i];
break;
}
}
} else {
// look for type variable on method...
ResolvedMember declaring = (ResolvedMember) declaringElement;
TypeVariable[] tvrts = declaring.getTypeVariables();
for (int i = 0; i < tvrts.length; i++) {
if (tvrts[i].getName().equals(getName())) {
resolvedTVar = tvrts[i];
// if (tvrts[i].isTypeVariableReference()) {
// TypeVariableReferenceType tvrt = (TypeVariableReferenceType) tvrts[i].resolve(inSomeWorld);
// TypeVariable tv = tvrt.getTypeVariable();
// if (tv.getName().equals(getName())) resolvedTVar = tv;
// }
}
}
}
if (resolvedTVar == null) {
throw new IllegalStateException();
// well, this is bad... we didn't find the type variable on the member
// could be a separate compilation issue...
// should issue message, this is a workaround to get us going...
// resolvedTVar = this;
}
} else {
resolvedTVar = this;
}
superclass = resolvedTVar.superclass;
superInterfaces = resolvedTVar.superInterfaces;
if (superclass != null) {
ResolvedType rt = superclass.resolve(world);
// if (!superclass.isTypeVariableReference() && rt.isInterface()) {
// throw new IllegalStateException("Why is the type an interface? " + rt);
// }
superclass = rt;
}
firstbound = getFirstBound().resolve(world);
for (int i = 0; i < superInterfaces.length; i++) {
superInterfaces[i] = superInterfaces[i].resolve(world);
}
isResolved = true;
beingResolved = false;
return this;
}
/**
* answer true if the given type satisfies all of the bound constraints of this type variable. If type variable has not been
* resolved then throws IllegalStateException
*/
public boolean canBeBoundTo(ResolvedType candidate) {
if (!isResolved) {
throw new IllegalStateException("Can't answer binding questions prior to resolving");
}
// wildcard can accept any binding
if (candidate.isGenericWildcard()) {
return true;
}
// otherwise can be bound iff...
// candidate is a subtype of upperBound
if (superclass != null && !isASubtypeOf(superclass, candidate)) {
return false;
}
// candidate is a subtype of all superInterfaces
for (int i = 0; i < superInterfaces.length; i++) {
if (!isASubtypeOf(superInterfaces[i], candidate)) {
return false;
}
}
return true;
}
private boolean isASubtypeOf(UnresolvedType candidateSuperType, UnresolvedType candidateSubType) {
ResolvedType superType = (ResolvedType) candidateSuperType;
ResolvedType subType = (ResolvedType) candidateSubType;
return superType.isAssignableFrom(subType);
}
// only used when resolving
public void setUpperBound(UnresolvedType superclass) {
// if (isResolved) {
// throw new IllegalStateException("Why set this late?");
// }
this.firstbound = null;
this.superclass = superclass;
}
// only used when resolving
public void setAdditionalInterfaceBounds(UnresolvedType[] superInterfaces) {
// if (isResolved) {
// throw new IllegalStateException("Why set this late?");
// }
this.firstbound = null;
this.superInterfaces = superInterfaces;
}
public String toDebugString() {
return getDisplayName();
}
public String getDisplayName() {
StringBuffer ret = new StringBuffer();
ret.append(name);
if (!getFirstBound().getName().equals("java.lang.Object")) {
ret.append(" extends ");
ret.append(getFirstBound().getName());
if (superInterfaces != null) {
for (int i = 0; i < superInterfaces.length; i++) {
if (!getFirstBound().equals(superInterfaces[i])) {
ret.append(" & ");
ret.append(superInterfaces[i].getName());
}
}
}
}
return ret.toString();
}
@Override
public String toString() {
return "TypeVar " + getDisplayName();
}
/**
* Return complete signature, e.g. "T extends Number" would return "T:Ljava/lang/Number;" note: MAY INCLUDE P types if bounds
* are parameterized types
*/
public String getSignature() {
StringBuffer sb = new StringBuffer();
sb.append(name);
sb.append(":");
if (superInterfaces.length == 0 || !superclass.getSignature().equals(UnresolvedType.OBJECT.getSignature())) {
sb.append(superclass.getSignature());
}
if (superInterfaces.length != 0) {
for (int i = 0; i < superInterfaces.length; i++) {
sb.append(":");
UnresolvedType iBound = superInterfaces[i];
sb.append(iBound.getSignature());
}
}
return sb.toString();
}
/**
* @return signature for inclusion in an attribute, there must be no 'P' in it signatures
*/
public String getSignatureForAttribute() {
StringBuffer sb = new StringBuffer();
sb.append(name);
sb.append(":");
if (superInterfaces.length == 0 || !superclass.getSignature().equals(UnresolvedType.OBJECT.getSignature())) {
sb.append(((ReferenceType)superclass).getSignatureForAttribute());
}
if (superInterfaces.length != 0) {
for (int i = 0; i < superInterfaces.length; i++) {
sb.append(":");
ResolvedType iBound = (ResolvedType) superInterfaces[i];
sb.append(iBound.getSignatureForAttribute());
}
}
return sb.toString();
}
public void setRank(int rank) {
this.rank = rank;
}
public int getRank() {
return rank;
}
public void setDeclaringElement(TypeVariableDeclaringElement element) {
this.declaringElement = element;
if (element instanceof UnresolvedType) {
this.declaringElementKind = TYPE;
} else {
this.declaringElementKind = METHOD;
}
}
public TypeVariableDeclaringElement getDeclaringElement() {
return declaringElement;
}
public void setDeclaringElementKind(int kind) {
this.declaringElementKind = kind;
}
public int getDeclaringElementKind() {
// if (declaringElementKind==UNKNOWN) throw new RuntimeException("Dont know declarer of this tvar : "+this);
return declaringElementKind;
}
public void write(CompressingDataOutputStream s) throws IOException {
// name, upperbound, additionalInterfaceBounds, lowerbound
s.writeUTF(name);
superclass.write(s);
if (superInterfaces.length == 0) {
s.writeInt(0);
} else {
s.writeInt(superInterfaces.length);
for (int i = 0; i < superInterfaces.length; i++) {
UnresolvedType ibound = superInterfaces[i];
ibound.write(s);
}
}
}
public static TypeVariable read(VersionedDataInputStream s) throws IOException {
// if (s.getMajorVersion()>=AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) {
String name = s.readUTF();
UnresolvedType ubound = UnresolvedType.read(s);
int iboundcount = s.readInt();
UnresolvedType[] ibounds = UnresolvedType.NONE;
if (iboundcount > 0) {
ibounds = new UnresolvedType[iboundcount];
for (int i = 0; i < iboundcount; i++) {
ibounds[i] = UnresolvedType.read(s);
}
}
TypeVariable newVariable = new TypeVariable(name, ubound, ibounds);
return newVariable;
}
public String getGenericSignature() {
return "T" + name + ";";
}
public String getErasureSignature() {
return getFirstBound().getErasureSignature();
}
public UnresolvedType getSuperclass() {
return superclass;
}
public void setSuperclass(UnresolvedType superclass) {
this.firstbound = null;
this.superclass = superclass;
}
}