| /******************************************************************************* |
| * Copyright (c) 2000, 2005 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.lookup.*; |
| |
| /** |
| * Syntactic representation of a reference to a generic type. |
| * Note that it might also have a dimension. |
| */ |
| public class ParameterizedQualifiedTypeReference extends ArrayQualifiedTypeReference { |
| |
| public TypeReference[][] typeArguments; |
| private boolean didResolve = false; |
| |
| /** |
| * @param tokens |
| * @param dim |
| * @param positions |
| */ |
| public ParameterizedQualifiedTypeReference(char[][] tokens, TypeReference[][] typeArguments, int dim, long[] positions) { |
| |
| super(tokens, dim, positions); |
| this.typeArguments = typeArguments; |
| } |
| public void checkBounds(Scope scope) { |
| if (this.resolvedType == null) return; |
| |
| checkBounds( |
| (ReferenceBinding) this.resolvedType.leafComponentType(), |
| scope, |
| this.typeArguments.length - 1); |
| } |
| public void checkBounds(ReferenceBinding type, Scope scope, int index) { |
| if (type.enclosingType() != null) |
| checkBounds(type.enclosingType(), scope, index - 1); |
| |
| if (type.isParameterizedType()) { |
| ParameterizedTypeBinding parameterizedType = (ParameterizedTypeBinding) type; |
| ReferenceBinding currentType = parameterizedType.type; |
| TypeVariableBinding[] typeVariables = currentType.typeVariables(); |
| TypeBinding[] argTypes = parameterizedType.arguments; |
| if (argTypes != null && typeVariables != null) { // argTypes may be null in error cases |
| parameterizedType.boundCheck(scope, this.typeArguments[index]); |
| } |
| } |
| } |
| public TypeReference copyDims(int dim){ |
| return new ParameterizedQualifiedTypeReference(this.tokens, this.typeArguments, dim, this.sourcePositions); |
| } |
| |
| /** |
| * @return char[][] |
| */ |
| public char [][] getParameterizedTypeName(){ |
| int length = this.tokens.length; |
| char[][] qParamName = new char[length][]; |
| for (int i = 0; i < length; i++) { |
| TypeReference[] arguments = this.typeArguments[i]; |
| if (arguments == null) { |
| qParamName[i] = this.tokens[i]; |
| } else { |
| StringBuffer buffer = new StringBuffer(5); |
| buffer.append(this.tokens[i]); |
| buffer.append('<'); |
| for (int j = 0, argLength =arguments.length; j < argLength; j++) { |
| if (j > 0) buffer.append(','); |
| buffer.append(CharOperation.concatWith(arguments[j].getParameterizedTypeName(), '.')); |
| } |
| buffer.append('>'); |
| int nameLength = buffer.length(); |
| qParamName[i] = new char[nameLength]; |
| buffer.getChars(0, nameLength, qParamName[i], 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] = ']'; |
| } |
| qParamName[length-1] = CharOperation.concat(qParamName[length-1], dimChars); |
| } |
| return qParamName; |
| } |
| |
| /* (non-Javadoc) |
| * @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, boolean checkBounds) { |
| |
| // handle the error here |
| this.constant = NotAConstant; |
| if (this.didResolve) { // is a shared type reference which was already resolved |
| if (this.resolvedType != null && !this.resolvedType.isValidBinding()) |
| return null; // already reported error |
| return this.resolvedType; |
| } |
| this.didResolve = true; |
| Binding binding = scope.getPackage(this.tokens); |
| if (binding != null && !binding.isValidBinding()) { |
| this.resolvedType = (ReferenceBinding) binding; |
| reportInvalidType(scope); |
| return null; |
| } |
| |
| PackageBinding packageBinding = binding == null ? null : (PackageBinding) binding; |
| boolean isClassScope = scope.kind == Scope.CLASS_SCOPE; |
| boolean typeIsConsistent = true; |
| ReferenceBinding qualifiedType = null; |
| for (int i = packageBinding == null ? 0 : packageBinding.compoundName.length, max = this.tokens.length; i < max; i++) { |
| findNextTypeBinding(i, scope, packageBinding); |
| if (!(this.resolvedType.isValidBinding())) { |
| reportInvalidType(scope); |
| return null; |
| } |
| ReferenceBinding currentType = (ReferenceBinding) this.resolvedType; |
| if (qualifiedType == null) { |
| qualifiedType = currentType.enclosingType(); // if member type |
| if (qualifiedType != null && currentType.isStatic() && (qualifiedType.isGenericType() || qualifiedType.isParameterizedType())) { |
| qualifiedType = scope.environment().createRawType((ReferenceBinding)qualifiedType.erasure(), qualifiedType.enclosingType()); |
| } |
| } |
| if (typeIsConsistent && currentType.isStatic() && qualifiedType != null && (qualifiedType.isParameterizedType() || qualifiedType.isGenericType())) { |
| scope.problemReporter().staticMemberOfParameterizedType(this, scope.environment().createParameterizedType((ReferenceBinding)currentType.erasure(), null, qualifiedType)); |
| typeIsConsistent = false; |
| } |
| // check generic and arity |
| TypeReference[] args = this.typeArguments[i]; |
| if (args != null) { |
| TypeReference keep = null; |
| if (isClassScope) { |
| keep = ((ClassScope) scope).superTypeReference; |
| ((ClassScope) scope).superTypeReference = null; |
| } |
| int argLength = args.length; |
| TypeBinding[] argTypes = new TypeBinding[argLength]; |
| boolean argHasError = false; |
| for (int j = 0; j < argLength; j++) { |
| TypeReference arg = args[j]; |
| TypeBinding argType = isClassScope |
| ? arg.resolveTypeArgument((ClassScope) scope, currentType, j) |
| : arg.resolveTypeArgument((BlockScope) scope, currentType, j); |
| if (argType == null) { |
| argHasError = true; |
| } else { |
| argTypes[j] = argType; |
| } |
| } |
| if (argHasError) { |
| return null; |
| } |
| if (isClassScope) { |
| ((ClassScope) scope).superTypeReference = keep; |
| if (((ClassScope) scope).detectHierarchyCycle(currentType, this, argTypes)) |
| return null; |
| } |
| |
| TypeVariableBinding[] typeVariables = currentType.typeVariables(); |
| if (typeVariables == NoTypeVariables) { // check generic |
| scope.problemReporter().nonGenericTypeCannotBeParameterized(this, currentType, argTypes); |
| return null; |
| } else if (argLength != typeVariables.length) { // check arity |
| scope.problemReporter().incorrectArityForParameterizedType(this, currentType, argTypes); |
| return null; |
| } |
| // check parameterizing non-static member type of raw type |
| if (typeIsConsistent && !currentType.isStatic() && qualifiedType != null && qualifiedType.isRawType()) { |
| scope.problemReporter().rawMemberTypeCannotBeParameterized( |
| this, scope.environment().createRawType((ReferenceBinding)currentType.erasure(), qualifiedType), argTypes); |
| typeIsConsistent = false; |
| } |
| // if generic type X<T> is referred to as parameterized X<T>, then answer itself |
| boolean isIdentical = (qualifiedType == null) || (qualifiedType instanceof SourceTypeBinding); |
| if (isIdentical) { |
| for (int j = 0; j < argLength; j++) { |
| if (typeVariables[j] != argTypes[j]) { |
| isIdentical = false; |
| break; |
| } |
| } |
| } |
| if (isIdentical) { |
| qualifiedType = (ReferenceBinding) currentType.erasure(); |
| } else { |
| ParameterizedTypeBinding parameterizedType = scope.environment().createParameterizedType((ReferenceBinding)currentType.erasure(), argTypes, qualifiedType); |
| // check argument type compatibility |
| if (checkBounds) // otherwise will do it in Scope.connectTypeVariables() or generic method resolution |
| parameterizedType.boundCheck(scope, args); |
| qualifiedType = parameterizedType; |
| } |
| } else { |
| if (isClassScope) |
| if (((ClassScope) scope).detectHierarchyCycle(currentType, this, null)) |
| return null; |
| if (currentType.isGenericType()) { |
| if (typeIsConsistent && qualifiedType != null && qualifiedType.isParameterizedType()) { |
| scope.problemReporter().parameterizedMemberTypeMissingArguments(this, scope.environment().createParameterizedType((ReferenceBinding)currentType.erasure(), null, qualifiedType)); |
| typeIsConsistent = false; |
| } |
| qualifiedType = scope.environment().createRawType(currentType, qualifiedType); // raw type |
| } else { |
| qualifiedType = (qualifiedType != null && qualifiedType.isParameterizedType()) |
| ? scope.environment().createParameterizedType((ReferenceBinding)currentType.erasure(), null, qualifiedType) |
| : currentType; |
| } |
| } |
| } |
| this.resolvedType = qualifiedType; |
| if (isTypeUseDeprecated(this.resolvedType, scope)) |
| reportDeprecatedType(scope); |
| // array type ? |
| if (this.dimensions > 0) { |
| if (dimensions > 255) |
| scope.problemReporter().tooManyDimensions(this); |
| this.resolvedType = scope.createArrayType(this.resolvedType, dimensions); |
| } |
| return this.resolvedType; |
| } |
| |
| public StringBuffer printExpression(int indent, StringBuffer output) { |
| int length = tokens.length; |
| for (int i = 0; i < length - 1; i++) { |
| output.append(tokens[i]); |
| TypeReference[] typeArgument = typeArguments[i]; |
| if (typeArgument != null) { |
| output.append('<');//$NON-NLS-1$ |
| int max = typeArgument.length - 1; |
| for (int j = 0; j < max; j++) { |
| typeArgument[j].print(0, output); |
| output.append(", ");//$NON-NLS-1$ |
| } |
| typeArgument[max].print(0, output); |
| output.append('>'); |
| } |
| output.append('.'); |
| } |
| output.append(tokens[length - 1]); |
| TypeReference[] typeArgument = typeArguments[length - 1]; |
| if (typeArgument != null) { |
| output.append('<');//$NON-NLS-1$ |
| int max = typeArgument.length - 1; |
| for (int j = 0; j < max; j++) { |
| typeArgument[j].print(0, output); |
| output.append(", ");//$NON-NLS-1$ |
| } |
| typeArgument[max].print(0, output); |
| output.append('>'); |
| } |
| if ((this.bits & IsVarArgs) != 0) { |
| for (int i= 0 ; i < dimensions - 1; i++) { |
| output.append("[]"); //$NON-NLS-1$ |
| } |
| output.append("..."); //$NON-NLS-1$ |
| } else { |
| for (int i= 0 ; i < dimensions; i++) { |
| output.append("[]"); //$NON-NLS-1$ |
| } |
| } |
| return output; |
| } |
| |
| public TypeBinding resolveType(BlockScope scope, boolean checkBounds) { |
| return internalResolveType(scope, checkBounds); |
| } |
| public TypeBinding resolveType(ClassScope scope) { |
| return internalResolveType(scope, false); |
| } |
| public void traverse(ASTVisitor visitor, BlockScope scope) { |
| if (visitor.visit(this, scope)) { |
| for (int i = 0, max = this.typeArguments.length; i < max; i++) { |
| if (this.typeArguments[i] != null) { |
| for (int j = 0, max2 = this.typeArguments[i].length; j < max2; j++) { |
| this.typeArguments[i][j].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++) { |
| if (this.typeArguments[i] != null) { |
| for (int j = 0, max2 = this.typeArguments[i].length; j < max2; j++) { |
| this.typeArguments[i][j].traverse(visitor, scope); |
| } |
| } |
| } |
| } |
| visitor.endVisit(this, scope); |
| } |
| |
| } |