package org.eclipse.jdt.internal.compiler.lookup;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
public class CaptureBinding extends TypeVariableBinding {
public TypeBinding lowerBound;
public WildcardBinding wildcard;
public int captureID;
public ReferenceBinding sourceType;
public int start;
public int end;
public ASTNode cud;
TypeBinding pendingSubstitute;
public CaptureBinding(WildcardBinding wildcard, ReferenceBinding sourceType, int start, int end, ASTNode cud, int captureID) {
super(TypeConstants.WILDCARD_CAPTURE_NAME_PREFIX, wildcard.environment);
this.wildcard = wildcard;
this.modifiers = ClassFileConstants.AccPublic | ExtraCompilerModifiers.AccGenericSignature;
this.fPackage = wildcard.fPackage;
this.sourceType = sourceType;
this.start = start;
this.end = end;
this.captureID = captureID;
this.tagBits |= TagBits.HasCapturedWildcard;
this.cud = cud;
if (wildcard.hasTypeAnnotations()) {
CaptureBinding unannotated = (CaptureBinding) clone(null);
unannotated.wildcard = (WildcardBinding) this.wildcard.unannotated();
this.environment.getUnannotatedType(unannotated);
this.id = unannotated.id;
this.environment.typeSystem.cacheDerivedType(this, unannotated, this);
super.setTypeAnnotations(wildcard.getTypeAnnotations(), wildcard.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled);
if (wildcard.hasNullTypeAnnotations())
this.tagBits |= TagBits.HasNullTypeAnnotation;
} else {
computeId(this.environment);
if(wildcard.hasNullTypeAnnotations()) {
this.tagBits |= (wildcard.tagBits & TagBits.AnnotationNullMASK) | TagBits.HasNullTypeAnnotation;
}
}
}
protected CaptureBinding(ReferenceBinding sourceType, char[] sourceName, int start, int end, int captureID, LookupEnvironment environment) {
super(sourceName, null, 0, environment);
this.modifiers = ClassFileConstants.AccPublic | ExtraCompilerModifiers.AccGenericSignature;
this.sourceType = sourceType;
this.start = start;
this.end = end;
this.captureID = captureID;
}
public CaptureBinding(CaptureBinding prototype) {
super(prototype);
this.wildcard = prototype.wildcard;
this.sourceType = prototype.sourceType;
this.start = prototype.start;
this.end = prototype.end;
this.captureID = prototype.captureID;
this.lowerBound = prototype.lowerBound;
this.tagBits |= (prototype.tagBits & TagBits.HasCapturedWildcard);
this.cud = prototype.cud;
}
@Override
public TypeBinding clone(TypeBinding enclosingType) {
return new CaptureBinding(this);
}
@Override
public char[] computeUniqueKey(boolean isLeaf) {
StringBuffer buffer = new StringBuffer();
if (isLeaf) {
buffer.append(this.sourceType.computeUniqueKey(false));
buffer.append('&');
}
buffer.append(TypeConstants.WILDCARD_CAPTURE);
buffer.append(this.wildcard.computeUniqueKey(false));
buffer.append(this.end);
buffer.append(';');
int length = buffer.length();
char[] uniqueKey = new char[length];
buffer.getChars(0, length, uniqueKey, 0);
return uniqueKey;
}
@Override
public String debugName() {
if (this.wildcard != null) {
StringBuffer buffer = new StringBuffer(10);
AnnotationBinding [] annotations = getTypeAnnotations();
for (int i = 0, length = annotations == null ? 0 : annotations.length; i < length; i++) {
buffer.append(annotations[i]);
buffer.append(' ');
}
buffer
.append(TypeConstants.WILDCARD_CAPTURE_NAME_PREFIX)
.append(this.captureID)
.append(TypeConstants.WILDCARD_CAPTURE_NAME_SUFFIX)
.append(this.wildcard.debugName());
return buffer.toString();
}
return super.debugName();
}
@Override
public char[] genericTypeSignature() {
if (this.inRecursiveFunction) {
return CharOperation.concat(new char[] {'L'}, CharOperation.concatWith(TypeConstants.JAVA_LANG_OBJECT, '.'), new char[] {';'});
}
this.inRecursiveFunction = true;
try {
return erasure().genericTypeSignature();
} finally {
this.inRecursiveFunction = false;
}
}
public void initializeBounds(Scope scope, ParameterizedTypeBinding capturedParameterizedType) {
boolean is18plus = scope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_8;
TypeVariableBinding wildcardVariable = this.wildcard.typeVariable();
if (wildcardVariable == null) {
TypeBinding originalWildcardBound = this.wildcard.bound;
switch (this.wildcard.boundKind) {
case Wildcard.EXTENDS :
TypeBinding capturedWildcardBound = is18plus
? originalWildcardBound
: originalWildcardBound.capture(scope, this.start, this.end);
if (originalWildcardBound.isInterface()) {
this.setSuperClass(scope.getJavaLangObject());
this.setSuperInterfaces(new ReferenceBinding[] { (ReferenceBinding) capturedWildcardBound });
} else {
if (capturedWildcardBound.isArrayType() || TypeBinding.equalsEquals(capturedWildcardBound, this)) {
this.setSuperClass(scope.getJavaLangObject());
} else {
this.setSuperClass((ReferenceBinding) capturedWildcardBound);
}
this.setSuperInterfaces(Binding.NO_SUPERINTERFACES);
}
this.setFirstBound(capturedWildcardBound);
if ((capturedWildcardBound.tagBits & TagBits.HasTypeVariable) == 0)
this.tagBits &= ~TagBits.HasTypeVariable;
break;
case Wildcard.UNBOUND :
this.setSuperClass(scope.getJavaLangObject());
this.setSuperInterfaces(Binding.NO_SUPERINTERFACES);
this.tagBits &= ~TagBits.HasTypeVariable;
break;
case Wildcard.SUPER :
this.setSuperClass(scope.getJavaLangObject());
this.setSuperInterfaces(Binding.NO_SUPERINTERFACES);
this.lowerBound = this.wildcard.bound;
if ((originalWildcardBound.tagBits & TagBits.HasTypeVariable) == 0)
this.tagBits &= ~TagBits.HasTypeVariable;
break;
}
return;
}
ReferenceBinding originalVariableSuperclass = wildcardVariable.superclass;
ReferenceBinding substitutedVariableSuperclass = (ReferenceBinding) Scope.substitute(capturedParameterizedType, originalVariableSuperclass);
if (TypeBinding.equalsEquals(substitutedVariableSuperclass, this)) substitutedVariableSuperclass = originalVariableSuperclass;
ReferenceBinding[] originalVariableInterfaces = wildcardVariable.superInterfaces();
ReferenceBinding[] substitutedVariableInterfaces = Scope.substitute(capturedParameterizedType, originalVariableInterfaces);
if (substitutedVariableInterfaces != originalVariableInterfaces) {
for (int i = 0, length = substitutedVariableInterfaces.length; i < length; i++) {
if (TypeBinding.equalsEquals(substitutedVariableInterfaces[i], this)) substitutedVariableInterfaces[i] = originalVariableInterfaces[i];
}
}
TypeBinding originalWildcardBound = this.wildcard.bound;
switch (this.wildcard.boundKind) {
case Wildcard.EXTENDS :
TypeBinding capturedWildcardBound = is18plus
? originalWildcardBound
: originalWildcardBound.capture(scope, this.start, this.end);
if (originalWildcardBound.isInterface()) {
this.setSuperClass(substitutedVariableSuperclass);
if (substitutedVariableInterfaces == Binding.NO_SUPERINTERFACES) {
this.setSuperInterfaces(new ReferenceBinding[] { (ReferenceBinding) capturedWildcardBound });
} else {
int length = substitutedVariableInterfaces.length;
System.arraycopy(substitutedVariableInterfaces, 0, substitutedVariableInterfaces = new ReferenceBinding[length+1], 1, length);
substitutedVariableInterfaces[0] = (ReferenceBinding) capturedWildcardBound;
this.setSuperInterfaces(Scope.greaterLowerBound(substitutedVariableInterfaces));
}
} else {
if (capturedWildcardBound.isArrayType() || TypeBinding.equalsEquals(capturedWildcardBound, this)) {
this.setSuperClass(substitutedVariableSuperclass);
} else {
this.setSuperClass((ReferenceBinding) capturedWildcardBound);
if (this.superclass.isSuperclassOf(substitutedVariableSuperclass)) {
this.setSuperClass(substitutedVariableSuperclass);
}
}
this.setSuperInterfaces(substitutedVariableInterfaces);
}
this.setFirstBound(capturedWildcardBound);
if ((capturedWildcardBound.tagBits & TagBits.HasTypeVariable) == 0)
this.tagBits &= ~TagBits.HasTypeVariable;
break;
case Wildcard.UNBOUND :
this.setSuperClass(substitutedVariableSuperclass);
this.setSuperInterfaces(substitutedVariableInterfaces);
this.tagBits &= ~TagBits.HasTypeVariable;
break;
case Wildcard.SUPER :
this.setSuperClass(substitutedVariableSuperclass);
if (TypeBinding.equalsEquals(wildcardVariable.firstBound, substitutedVariableSuperclass) || TypeBinding.equalsEquals(originalWildcardBound, substitutedVariableSuperclass)) {
this.setFirstBound(substitutedVariableSuperclass);
}
this.setSuperInterfaces(substitutedVariableInterfaces);
this.lowerBound = originalWildcardBound;
if ((originalWildcardBound.tagBits & TagBits.HasTypeVariable) == 0)
this.tagBits &= ~TagBits.HasTypeVariable;
break;
}
if(scope.environment().usesNullTypeAnnotations()) {
evaluateNullAnnotations(scope, null);
}
}
@Override
public ReferenceBinding upwardsProjection(Scope scope, TypeBinding[] mentionedTypeVariables) {
if (enterRecursiveProjectionFunction()) {
try {
for (int i = 0; i < mentionedTypeVariables.length; ++i) {
if (TypeBinding.equalsEquals(this, mentionedTypeVariables[i])) {
TypeBinding upperBoundForProjection = this.upperBoundForProjection();
return ((ReferenceBinding)upperBoundForProjection).upwardsProjection(scope, mentionedTypeVariables);
}
}
return this;
} finally {
exitRecursiveProjectionFunction();
}
} else {
return scope.getJavaLangObject();
}
}
public TypeBinding upperBoundForProjection() {
TypeBinding upperBound = null;
if (this.wildcard != null) {
ReferenceBinding[] supers = this.superInterfaces();
if (this.wildcard.boundKind == Wildcard.EXTENDS) {
if (supers.length > 0) {
ReferenceBinding[] allBounds = new ReferenceBinding[supers.length + 1];
System.arraycopy(supers, 0, allBounds, 1, supers.length);
allBounds[0] = this.superclass();
ReferenceBinding[] glbs = Scope.greaterLowerBound(allBounds);
if (glbs == null) {
upperBound = new ProblemReferenceBinding(null, null, ProblemReasons.ParameterBoundMismatch);
} else if (glbs.length == 1) {
upperBound = glbs[0];
} else {
upperBound = this.environment.createIntersectionType18(glbs);
}
} else {
upperBound = this.superclass;
}
} else {
boolean superClassIsObject = TypeBinding.equalsEquals(this.superclass(), this.environment.getResolvedJavaBaseType(TypeConstants.JAVA_LANG_OBJECT, null));
if (supers.length == 0) {
upperBound = this.superclass();
} else if (supers.length == 1) {
upperBound = superClassIsObject ? supers[0] : this.environment.createIntersectionType18(new ReferenceBinding[] {this.superclass(), supers[0]});
} else {
if (superClassIsObject) {
upperBound = this.environment.createIntersectionType18(supers);
} else {
ReferenceBinding[] allBounds = new ReferenceBinding[supers.length + 1];
System.arraycopy(supers, 0, allBounds, 1, supers.length);
allBounds[0] = this.superclass();
upperBound = this.environment.createIntersectionType18(allBounds);
}
}
}
} else {
upperBound = super.upperBound();
}
return upperBound;
}
@Override
public boolean isCapture() {
return true;
}
@Override
public boolean isEquivalentTo(TypeBinding otherType) {
if (equalsEquals(this, otherType)) return true;
if (otherType == null) return false;
if (this.firstBound != null && this.firstBound.isArrayType()) {
if (this.firstBound.isCompatibleWith(otherType))
return true;
}
switch (otherType.kind()) {
case Binding.WILDCARD_TYPE :
case Binding.INTERSECTION_TYPE :
return ((WildcardBinding) otherType).boundCheck(this);
}
return false;
}
@Override
public boolean isProperType(boolean admitCapture18) {
if (this.lowerBound != null && !this.lowerBound.isProperType(admitCapture18))
return false;
if (this.wildcard != null && !this.wildcard.isProperType(admitCapture18))
return false;
return super.isProperType(admitCapture18);
}
@Override
public char[] readableName() {
if (this.wildcard != null) {
StringBuffer buffer = new StringBuffer(10);
buffer
.append(TypeConstants.WILDCARD_CAPTURE_NAME_PREFIX)
.append(this.captureID)
.append(TypeConstants.WILDCARD_CAPTURE_NAME_SUFFIX)
.append(this.wildcard.readableName());
int length = buffer.length();
char[] name = new char[length];
buffer.getChars(0, length, name, 0);
return name;
}
return super.readableName();
}
@Override
public char[] signableName() {
if (this.wildcard != null) {
StringBuffer buffer = new StringBuffer(10);
buffer
.append(TypeConstants.WILDCARD_CAPTURE_SIGNABLE_NAME_SUFFIX)
.append(this.wildcard.readableName());
int length = buffer.length();
char[] name = new char[length];
buffer.getChars(0, length, name, 0);
return name;
}
return super.readableName();
}
@Override
public char[] shortReadableName() {
if (this.wildcard != null) {
StringBuffer buffer = new StringBuffer(10);
buffer
.append(TypeConstants.WILDCARD_CAPTURE_NAME_PREFIX)
.append(this.captureID)
.append(TypeConstants.WILDCARD_CAPTURE_NAME_SUFFIX)
.append(this.wildcard.shortReadableName());
int length = buffer.length();
char[] name = new char[length];
buffer.getChars(0, length, name, 0);
return name;
}
return super.shortReadableName();
}
@Override
public char[] nullAnnotatedReadableName(CompilerOptions options, boolean shortNames) {
StringBuffer nameBuffer = new StringBuffer(10);
appendNullAnnotation(nameBuffer, options);
nameBuffer.append(this.sourceName());
if (!this.inRecursiveFunction) {
this.inRecursiveFunction = true;
try {
if (this.wildcard != null) {
nameBuffer.append("of ");
nameBuffer.append(this.wildcard.withoutToplevelNullAnnotation().nullAnnotatedReadableName(options, shortNames));
} else if (this.lowerBound != null) {
nameBuffer.append(" super ");
nameBuffer.append(this.lowerBound.nullAnnotatedReadableName(options, shortNames));
} else if (this.firstBound != null) {
nameBuffer.append(" extends ");
nameBuffer.append(this.firstBound.nullAnnotatedReadableName(options, shortNames));
TypeBinding[] otherUpperBounds = this.otherUpperBounds();
if (otherUpperBounds != NO_TYPES)
nameBuffer.append(" & ...");
}
} finally {
this.inRecursiveFunction = false;
}
}
int nameLength = nameBuffer.length();
char[] readableName = new char[nameLength];
nameBuffer.getChars(0, nameLength, readableName, 0);
return readableName;
}
@Override
public TypeBinding withoutToplevelNullAnnotation() {
if (!hasNullTypeAnnotations())
return this;
if (this.wildcard != null && this.wildcard.hasNullTypeAnnotations()) {
WildcardBinding newWildcard = (WildcardBinding) this.wildcard.withoutToplevelNullAnnotation();
if (newWildcard != this.wildcard) {
CaptureBinding newCapture = (CaptureBinding) this.environment.getUnannotatedType(this).clone(null);
if (newWildcard.hasTypeAnnotations())
newCapture.tagBits |= TagBits.HasTypeAnnotations;
newCapture.wildcard = newWildcard;
newCapture.superclass = this.superclass;
newCapture.superInterfaces = this.superInterfaces;
AnnotationBinding[] newAnnotations = this.environment.filterNullTypeAnnotations(this.typeAnnotations);
return this.environment.createAnnotatedType(newCapture, newAnnotations);
}
}
return super.withoutToplevelNullAnnotation();
}
@Override
TypeBinding substituteInferenceVariable(InferenceVariable var, TypeBinding substituteType) {
if (this.pendingSubstitute != null)
return this.pendingSubstitute;
try {
TypeBinding substitutedWildcard = this.wildcard.substituteInferenceVariable(var, substituteType);
if (substitutedWildcard != this.wildcard) {
CaptureBinding substitute = (CaptureBinding) clone(enclosingType());
substitute.wildcard = (WildcardBinding) substitutedWildcard;
this.pendingSubstitute = substitute;
if (this.lowerBound != null)
substitute.lowerBound = this.lowerBound.substituteInferenceVariable(var, substituteType);
if (this.firstBound != null)
substitute.firstBound = this.firstBound.substituteInferenceVariable(var, substituteType);
if (this.superclass != null)
substitute.superclass = (ReferenceBinding) this.superclass.substituteInferenceVariable(var, substituteType);
if (this.superInterfaces != null) {
int length = this.superInterfaces.length;
substitute.superInterfaces = new ReferenceBinding[length];
for (int i = 0; i < length; i++)
substitute.superInterfaces[i] = (ReferenceBinding) this.superInterfaces[i].substituteInferenceVariable(var, substituteType);
}
return substitute;
}
return this;
} finally {
this.pendingSubstitute = null;
}
}
@Override
public void setTypeAnnotations(AnnotationBinding[] annotations, boolean evalNullAnnotations) {
super.setTypeAnnotations(annotations, evalNullAnnotations);
if (annotations != Binding.NO_ANNOTATIONS && this.wildcard != null) {
this.wildcard = (WildcardBinding) this.wildcard.environment.createAnnotatedType(this.wildcard, annotations);
}
}
@Override
public TypeBinding uncapture(Scope scope) {
return this.wildcard.uncapture(scope);
}
@Override
public ReferenceBinding downwardsProjection(Scope scope, TypeBinding[] mentionedTypeVariables) {
ReferenceBinding result = null;
if (enterRecursiveProjectionFunction()) {
for (int i = 0; i < mentionedTypeVariables.length; ++i) {
if (TypeBinding.equalsEquals(this, mentionedTypeVariables[i])) {
if (this.lowerBound != null) {
result = (ReferenceBinding) this.lowerBound.downwardsProjection(scope, mentionedTypeVariables);
}
break;
}
}
exitRecursiveProjectionFunction();
}
return result;
}
@Override
protected TypeBinding[] getDerivedTypesForDeferredInitialization() {
TypeBinding[] derived = this.environment.typeSystem.getDerivedTypes(this);
if (derived.length > 0) {
int count = 0;
for (int i = 0; i < derived.length; i++) {
if (derived[i] != null && derived[i].id == this.id)
derived[count++] = derived[i];
}
if (count < derived.length)
System.arraycopy(derived, 0, derived = new TypeBinding[count], 0, count);
}
return derived;
}
@Override
public String toString() {
if (this.wildcard != null) {
StringBuffer buffer = new StringBuffer(10);
AnnotationBinding [] annotations = getTypeAnnotations();
for (int i = 0, length = annotations == null ? 0 : annotations.length; i < length; i++) {
buffer.append(annotations[i]);
buffer.append(' ');
}
buffer
.append(TypeConstants.WILDCARD_CAPTURE_NAME_PREFIX)
.append(this.captureID)
.append(TypeConstants.WILDCARD_CAPTURE_NAME_SUFFIX)
.append(this.wildcard);
return buffer.toString();
}
return super.toString();
}
}