blob: 62bd4d23a4be9f950aa804b164f7228ca6773512 [file] [log] [blame]
/* *******************************************************************
* Copyright (c) 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.util.Map;
/**
* A BoundedReferenceType is the result of a generics wildcard expression ? extends String, ? super Foo etc..
*
* The "signature" for a bounded reference type follows the generic signature specification in section 4.4 of JVM spec: *,+,- plus
* signature strings.
*
* The bound may be a type variable (e.g. ? super T)
*
* @author Adrian Colyer
* @author Andy Clement
*/
public class BoundedReferenceType extends ReferenceType {
// possible kinds of BoundedReferenceType
public static final int UNBOUND = 0;
public static final int EXTENDS = 1;
public static final int SUPER = 2;
public int kind;
private ResolvedType lowerBound;
private ResolvedType upperBound;
protected ReferenceType[] additionalInterfaceBounds = ReferenceType.EMPTY_ARRAY;
public BoundedReferenceType(ReferenceType aBound, boolean isExtends, World world) {
super((isExtends ? "+" : "-") + aBound.signature, aBound.signatureErasure, world);
if (isExtends) {
this.kind = EXTENDS;
} else {
this.kind = SUPER;
}
if (isExtends) {
upperBound = aBound;
} else {
lowerBound = aBound;
upperBound = world.resolve(UnresolvedType.OBJECT);
}
setDelegate(new BoundedReferenceTypeDelegate((ReferenceType) getUpperBound()));
}
public BoundedReferenceType(ReferenceType aBound, boolean isExtends, World world, ReferenceType[] additionalInterfaces) {
this(aBound, isExtends, world);
this.additionalInterfaceBounds = additionalInterfaces;
}
/**
* only for use when resolving GenericsWildcardTypeX or a TypeVariableReferenceType
*/
protected BoundedReferenceType(String signature, String erasedSignature, World world) {
super(signature, erasedSignature, world);
if (signature.equals("*")) {
// pure wildcard
this.kind = UNBOUND;
upperBound = world.resolve(UnresolvedType.OBJECT);
} else {
upperBound = world.resolve(forSignature(erasedSignature));
}
setDelegate(new BoundedReferenceTypeDelegate((ReferenceType) upperBound));
}
/**
* Constructs the BoundedReferenceType representing an unbounded wildcard '?'. In this situation the signature is '*' and the
* erased signature is Ljava/lang/Object;
*/
public BoundedReferenceType(World world) {
super("*", "Ljava/lang/Object;", world);
this.kind = UNBOUND;
upperBound = world.resolve(UnresolvedType.OBJECT);
setDelegate(new BoundedReferenceTypeDelegate((ReferenceType) upperBound));
}
public UnresolvedType getUpperBound() {
return upperBound;
}
public UnresolvedType getLowerBound() {
return lowerBound;
}
public ReferenceType[] getAdditionalBounds() {
return additionalInterfaceBounds;
}
@Override
public UnresolvedType parameterize(Map<String, UnresolvedType> typeBindings) {
if (this.kind == UNBOUND) {
return this;
}
ReferenceType[] parameterizedAdditionalInterfaces = new ReferenceType[additionalInterfaceBounds == null ? 0
: additionalInterfaceBounds.length];
for (int i = 0; i < parameterizedAdditionalInterfaces.length; i++) {
parameterizedAdditionalInterfaces[i] = (ReferenceType) additionalInterfaceBounds[i].parameterize(typeBindings);
}
if (this.kind == EXTENDS) {
return new BoundedReferenceType((ReferenceType) getUpperBound().parameterize(typeBindings), true, world,
parameterizedAdditionalInterfaces);
} else {
// (this.kind == SUPER)
UnresolvedType parameterizedLowerBound = getLowerBound().parameterize(typeBindings);
if (!(parameterizedLowerBound instanceof ReferenceType)) {
throw new IllegalStateException("PR543023: Unexpectedly found a non reference type: "+
parameterizedLowerBound.getClass().getName()+" with signature "+parameterizedLowerBound.getSignature());
}
return new BoundedReferenceType((ReferenceType)parameterizedLowerBound , false, world,
parameterizedAdditionalInterfaces);
}
}
@Override
public String getSignatureForAttribute() {
StringBuilder ret = new StringBuilder();
if (kind==SUPER){
ret.append("-");
ret.append(lowerBound.getSignatureForAttribute());
for (int i=0;i<additionalInterfaceBounds.length;i++) {
ret.append(additionalInterfaceBounds[i].getSignatureForAttribute());
}
} else if (kind==EXTENDS) {
ret.append("+");
ret.append(upperBound.getSignatureForAttribute());
for (int i=0;i<additionalInterfaceBounds.length;i++) {
ret.append(additionalInterfaceBounds[i].getSignatureForAttribute());
}
} else if (kind==UNBOUND) {
ret.append("*");
}
return ret.toString();
}
public boolean hasLowerBound() {
return lowerBound != null;
}
public boolean isExtends() {
return this.kind == EXTENDS;
}
public boolean isSuper() {
return this.kind == SUPER;
}
public boolean isUnbound() {
return this.kind == UNBOUND;
}
public boolean alwaysMatches(ResolvedType aCandidateType) {
if (isExtends()) {
// aCandidateType must be a subtype of upperBound
return ((ReferenceType) getUpperBound()).isAssignableFrom(aCandidateType);
} else if (isSuper()) {
// aCandidateType must be a supertype of lowerBound
return aCandidateType.isAssignableFrom((ReferenceType) getLowerBound());
} else {
return true; // straight '?'
}
}
// this "maybe matches" that
public boolean canBeCoercedTo(ResolvedType aCandidateType) {
if (alwaysMatches(aCandidateType)) {
return true;
}
if (aCandidateType.isGenericWildcard()) {
BoundedReferenceType boundedRT = (BoundedReferenceType) aCandidateType;
ResolvedType myUpperBound = (ResolvedType) getUpperBound();
ResolvedType myLowerBound = (ResolvedType) getLowerBound();
if (isExtends()) {
if (boundedRT.isExtends()) {
return myUpperBound.isAssignableFrom((ResolvedType) boundedRT.getUpperBound());
} else if (boundedRT.isSuper()) {
return myUpperBound == boundedRT.getLowerBound();
} else {
return true; // it's '?'
}
} else if (isSuper()) {
if (boundedRT.isSuper()) {
return ((ResolvedType) boundedRT.getLowerBound()).isAssignableFrom(myLowerBound);
} else if (boundedRT.isExtends()) {
return myLowerBound == boundedRT.getUpperBound();
} else {
return true;
}
} else {
return true;
}
} else {
return false;
}
}
@Override
public String getSimpleName() {
if (!isExtends() && !isSuper()) {
return "?";
}
if (isExtends()) {
return ("? extends " + getUpperBound().getSimpleName());
} else {
return ("? super " + getLowerBound().getSimpleName());
}
}
// override to include additional interface bounds...
@Override
public ResolvedType[] getDeclaredInterfaces() {
ResolvedType[] interfaces = super.getDeclaredInterfaces();
if (additionalInterfaceBounds.length > 0) {
ResolvedType[] allInterfaces = new ResolvedType[interfaces.length + additionalInterfaceBounds.length];
System.arraycopy(interfaces, 0, allInterfaces, 0, interfaces.length);
System.arraycopy(additionalInterfaceBounds, 0, allInterfaces, interfaces.length, additionalInterfaceBounds.length);
return allInterfaces;
} else {
return interfaces;
}
}
@Override
public boolean isGenericWildcard() {
return true;
}
}