/******************************************************************************* * 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); } }