/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation and others.
* 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://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
/**
* Syntactic representation of a reference to a generic type. Note that it might also have a
* dimension.
*/
public class ParameterizedSingleTypeReference extends ArrayTypeReference {
public TypeReference[] typeArguments;
public ParameterizedSingleTypeReference(char[] name, TypeReference[] typeArguments, int dim, long pos) {
super(name, dim, pos);
this.originalSourceEnd= this.sourceEnd;
this.typeArguments= typeArguments;
}
public void checkBounds(Scope scope) {
if (this.resolvedType == null)
return;
if (this.resolvedType.leafComponentType() instanceof ParameterizedTypeBinding) {
ParameterizedTypeBinding parameterizedType= (ParameterizedTypeBinding)this.resolvedType.leafComponentType();
ReferenceBinding currentType= parameterizedType.genericType();
TypeVariableBinding[] typeVariables= currentType.typeVariables();
TypeBinding[] argTypes= parameterizedType.arguments;
if (argTypes != null && typeVariables != null) { // may be null in error cases
parameterizedType.boundCheck(scope, this.typeArguments);
}
}
}
/**
* @see org.eclipse.jdt.internal.compiler.ast.TypeReference#copyDims(int)
*/
public TypeReference copyDims(int dim) {
return new ParameterizedSingleTypeReference(this.token, this.typeArguments, dim, (((long)this.sourceStart) << 32) + this.sourceEnd);
}
/**
* @return char[][]
*/
public char[][] getParameterizedTypeName() {
StringBuffer buffer= new StringBuffer(5);
buffer.append(this.token).append('<');
for (int i= 0, length= this.typeArguments.length; i < length; i++) {
if (i > 0)
buffer.append(',');
buffer.append(CharOperation.concatWith(this.typeArguments[i].getParameterizedTypeName(), '.'));
}
buffer.append('>');
int nameLength= buffer.length();
char[] name= new char[nameLength];
buffer.getChars(0, nameLength, name, 0);
int dim= this.dimensions;
if (dim > 0) {
char[] dimChars= new char[dim * 2];
for (int i= 0; i < dim; i++) {
int index= i * 2;
dimChars[index]= '[';
dimChars[index + 1]= ']';
}
name= CharOperation.concat(name, dimChars);
}
return new char[][] { name };
}
/**
* @see org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference#getTypeBinding(org.eclipse.jdt.internal.compiler.lookup.Scope)
*/
protected TypeBinding getTypeBinding(Scope scope) {
return null; // not supported here - combined with resolveType(...)
}
/*
* No need to check for reference to raw type per construction
*/
private TypeBinding internalResolveType(Scope scope, ReferenceBinding enclosingType, boolean checkBounds) {
// handle the error here
this.constant= Constant.NotAConstant;
if ((this.bits & ASTNode.DidResolve) != 0) { // is a shared type reference which was already resolved
if (this.resolvedType != null) { // is a shared type reference which was already resolved
if (this.resolvedType.isValidBinding()) {
return this.resolvedType;
} else {
switch (this.resolvedType.problemId()) {
case ProblemReasons.NotFound:
case ProblemReasons.NotVisible:
case ProblemReasons.InheritedNameHidesEnclosingName:
TypeBinding type= this.resolvedType.closestMatch();
return type;
default:
return null;
}
}
}
}
boolean hasGenericError= false;
ReferenceBinding currentType;
this.bits|= ASTNode.DidResolve;
if (enclosingType == null) {
this.resolvedType= scope.getType(this.token);
if (this.resolvedType.isValidBinding()) {
currentType= (ReferenceBinding)this.resolvedType;
} else {
hasGenericError= true;
reportInvalidType(scope);
switch (this.resolvedType.problemId()) {
case ProblemReasons.NotFound:
case ProblemReasons.NotVisible:
case ProblemReasons.InheritedNameHidesEnclosingName:
TypeBinding type= this.resolvedType.closestMatch();
if (type instanceof ReferenceBinding) {
currentType= (ReferenceBinding)type;
break;
}
//$FALL-THROUGH$ - unable to complete type binding, but still resolve type arguments
default:
boolean isClassScope= scope.kind == Scope.CLASS_SCOPE;
int argLength= this.typeArguments.length;
for (int i= 0; i < argLength; i++) {
TypeReference typeArgument= this.typeArguments[i];
if (isClassScope) {
typeArgument.resolveType((ClassScope)scope);
} else {
typeArgument.resolveType((BlockScope)scope, checkBounds);
}
}
return null;
}
// be resilient, still attempt resolving arguments
}
enclosingType= currentType.enclosingType(); // if member type
if (enclosingType != null) {
enclosingType= currentType.isStatic()
? (ReferenceBinding)scope.environment().convertToRawType(enclosingType, false /*do not force conversion of enclosing types*/)
: scope.environment().convertToParameterizedType(enclosingType);
currentType= scope.environment().createParameterizedType((ReferenceBinding)currentType.erasure(), null /* no arg */, enclosingType);
}
} else { // resolving member type (relatively to enclosingType)
this.resolvedType= currentType= scope.getMemberType(this.token, enclosingType);
if (!this.resolvedType.isValidBinding()) {
hasGenericError= true;
scope.problemReporter().invalidEnclosingType(this, currentType, enclosingType);
return null;
}
if (isTypeUseDeprecated(currentType, scope))
scope.problemReporter().deprecatedType(currentType, this);
ReferenceBinding currentEnclosing= currentType.enclosingType();
if (currentEnclosing != null && currentEnclosing.erasure() != enclosingType.erasure()) {
enclosingType= currentEnclosing; // inherited member type, leave it associated with its enclosing rather than subtype
}
}
// check generic and arity
boolean isClassScope= scope.kind == Scope.CLASS_SCOPE;
TypeReference keep= null;
if (isClassScope) {
keep= ((ClassScope)scope).superTypeReference;
((ClassScope)scope).superTypeReference= null;
}
int argLength= this.typeArguments.length;
TypeBinding[] argTypes= new TypeBinding[argLength];
boolean argHasError= false;
ReferenceBinding currentOriginal= (ReferenceBinding)currentType.original();
for (int i= 0; i < argLength; i++) {
TypeReference typeArgument= this.typeArguments[i];
TypeBinding argType= isClassScope
? typeArgument.resolveTypeArgument((ClassScope)scope, currentOriginal, i)
: typeArgument.resolveTypeArgument((BlockScope)scope, currentOriginal, i);
if (argType == null) {
argHasError= true;
} else {
argTypes[i]= argType;
}
}
if (argHasError) {
return null;
}
if (isClassScope) {
((ClassScope)scope).superTypeReference= keep;
if (((ClassScope)scope).detectHierarchyCycle(currentOriginal, this))
return null;
}
TypeVariableBinding[] typeVariables= currentOriginal.typeVariables();
if (typeVariables == Binding.NO_TYPE_VARIABLES) { // non generic invoked with arguments
boolean isCompliant15= scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5;
if ((currentOriginal.tagBits & TagBits.HasMissingType) == 0) {
if (isCompliant15) { // below 1.5, already reported as syntax error
this.resolvedType= currentType;
scope.problemReporter().nonGenericTypeCannotBeParameterized(0, this, currentType, argTypes);
return null;
}
}
// resilience do not rebuild a parameterized type unless compliance is allowing it
if (!isCompliant15) {
// array type ?
TypeBinding type= currentType;
if (this.dimensions > 0) {
if (this.dimensions > 255)
scope.problemReporter().tooManyDimensions(this);
type= scope.createArrayType(type, this.dimensions);
}
if (hasGenericError)
return type;
return this.resolvedType= type;
}
// if missing generic type, and compliance >= 1.5, then will rebuild a parameterized binding
} else if (argLength != typeVariables.length) { // check arity
scope.problemReporter().incorrectArityForParameterizedType(this, currentType, argTypes);
return null;
} else if (!currentType.isStatic()) {
ReferenceBinding actualEnclosing= currentType.enclosingType();
if (actualEnclosing != null && actualEnclosing.isRawType()) {
scope.problemReporter().rawMemberTypeCannotBeParameterized(
this, scope.environment().createRawType(currentOriginal, actualEnclosing), argTypes);
return null;
}
}
ParameterizedTypeBinding parameterizedType= scope.environment().createParameterizedType(currentOriginal, argTypes, enclosingType);
// check argument type compatibility
if (checkBounds) // otherwise will do it in Scope.connectTypeVariables() or generic method resolution
parameterizedType.boundCheck(scope, this.typeArguments);
else
scope.deferBoundCheck(this);
if (isTypeUseDeprecated(parameterizedType, scope))
reportDeprecatedType(parameterizedType, scope);
TypeBinding type= parameterizedType;
// array type ?
if (this.dimensions > 0) {
if (this.dimensions > 255)
scope.problemReporter().tooManyDimensions(this);
type= scope.createArrayType(type, this.dimensions);
}
if (hasGenericError) {
return type;
}
return this.resolvedType= type;
}
public StringBuffer printExpression(int indent, StringBuffer output) {
output.append(this.token);
output.append("<"); //$NON-NLS-1$
int max= this.typeArguments.length - 1;
for (int i= 0; i < max; i++) {
this.typeArguments[i].print(0, output);
output.append(", ");//$NON-NLS-1$
}
this.typeArguments[max].print(0, output);
output.append(">"); //$NON-NLS-1$
if ((this.bits & IsVarArgs) != 0) {
for (int i= 0; i < this.dimensions - 1; i++) {
output.append("[]"); //$NON-NLS-1$
}
output.append("..."); //$NON-NLS-1$
} else {
for (int i= 0; i < this.dimensions; i++) {
output.append("[]"); //$NON-NLS-1$
}
}
return output;
}
public TypeBinding resolveType(BlockScope scope, boolean checkBounds) {
return internalResolveType(scope, null, checkBounds);
}
public TypeBinding resolveType(ClassScope scope) {
return internalResolveType(scope, null, false /*no bounds check in classScope*/);
}
public TypeBinding resolveTypeEnclosing(BlockScope scope, ReferenceBinding enclosingType) {
return internalResolveType(scope, enclosingType, true/*check bounds*/);
}
public void traverse(ASTVisitor visitor, BlockScope scope) {
if (visitor.visit(this, scope)) {
for (int i= 0, max= this.typeArguments.length; i < max; i++) {
this.typeArguments[i].traverse(visitor, scope);
}
}
visitor.endVisit(this, scope);
}
public void traverse(ASTVisitor visitor, ClassScope scope) {
if (visitor.visit(this, scope)) {
for (int i= 0, max= this.typeArguments.length; i < max; i++) {
this.typeArguments[i].traverse(visitor, scope);
}
}
visitor.endVisit(this, scope);
}
}