| /******************************************************************************* |
| * Copyright (c) 2011, 2013 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 |
| * Stephan Herrmann - Contribution for |
| * Bug 429958 - [1.8][null] evaluate new DefaultLocation attribute of @NonNullByDefault |
| * Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for |
| * Bug 409236 - [1.8][compiler] Type annotations on intersection cast types dropped by code generator |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler.ast; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.eclipse.jdt.internal.compiler.ASTVisitor; |
| import org.eclipse.jdt.internal.compiler.lookup.BlockScope; |
| import org.eclipse.jdt.internal.compiler.lookup.ClassScope; |
| import org.eclipse.jdt.internal.compiler.lookup.IntersectionTypeBinding18; |
| 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; |
| |
| public class IntersectionCastTypeReference extends TypeReference { |
| public TypeReference[] typeReferences; |
| |
| public IntersectionCastTypeReference(TypeReference[] typeReferences) { |
| this.typeReferences = typeReferences; |
| this.sourceStart = typeReferences[0].sourceStart; |
| int length = typeReferences.length; |
| this.sourceEnd = typeReferences[length - 1].sourceEnd; |
| for (int i = 0, max = typeReferences.length; i < max; i++) { |
| if ((typeReferences[i].bits & ASTNode.HasTypeAnnotations) != 0) { |
| this.bits |= ASTNode.HasTypeAnnotations; |
| break; |
| } |
| } |
| } |
| |
| @Override |
| public TypeReference augmentTypeWithAdditionalDimensions(int additionalDimensions, Annotation[][] additionalAnnotations, boolean isVarargs) { |
| throw new UnsupportedOperationException(); // no syntax for this. |
| } |
| |
| @Override |
| public char[] getLastToken() { |
| return null; |
| } |
| |
| /** |
| * @see org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference#getTypeBinding(org.eclipse.jdt.internal.compiler.lookup.Scope) |
| */ |
| @Override |
| protected TypeBinding getTypeBinding(Scope scope) { |
| return null; // not supported here - combined with resolveType(...) |
| } |
| |
| @Override |
| public TypeReference[] getTypeReferences() { |
| return this.typeReferences; |
| } |
| |
| @Override |
| public TypeBinding resolveType(BlockScope scope, boolean checkBounds, int location) { |
| |
| int length = this.typeReferences.length; |
| ReferenceBinding[] intersectingTypes = new ReferenceBinding[length]; |
| boolean hasError = false; |
| |
| int typeCount = 0; |
| nextType: |
| for (int i = 0; i < length; i++) { |
| final TypeReference typeReference = this.typeReferences[i]; |
| TypeBinding type = typeReference.resolveType(scope, checkBounds, location); |
| if (type == null || ((type.tagBits & TagBits.HasMissingType) != 0)) { |
| hasError = true; |
| continue; |
| } |
| if (i == 0) { |
| if (type.isBaseType()) { // rejected in grammar for i > 0 |
| scope.problemReporter().onlyReferenceTypesInIntersectionCast(typeReference); |
| hasError = true; |
| continue; |
| } |
| if (type.isArrayType()) { // javac rejects the pedantic cast: (X[] & Serializable & Cloneable) new X[0], what is good for the goose ... |
| scope.problemReporter().illegalArrayTypeInIntersectionCast(typeReference); |
| hasError = true; |
| continue; |
| } |
| } else if (!type.isInterface()) { |
| scope.problemReporter().boundMustBeAnInterface(typeReference, type); |
| hasError = true; |
| continue; |
| } |
| for (int j = 0; j < typeCount; j++) { |
| final ReferenceBinding priorType = intersectingTypes[j]; |
| if (TypeBinding.equalsEquals(priorType, type)) { |
| scope.problemReporter().duplicateBoundInIntersectionCast(typeReference); |
| hasError = true; |
| continue; |
| } |
| if (!priorType.isInterface()) |
| continue; |
| if (TypeBinding.equalsEquals(type.findSuperTypeOriginatingFrom(priorType), priorType)) { |
| intersectingTypes[j] = (ReferenceBinding) type; |
| continue nextType; |
| } |
| if (TypeBinding.equalsEquals(priorType.findSuperTypeOriginatingFrom(type), type)) |
| continue nextType; |
| } |
| intersectingTypes[typeCount++] = (ReferenceBinding) type; |
| } |
| |
| if (hasError) { |
| return null; |
| } |
| if (typeCount != length) { |
| if (typeCount == 1) { |
| return this.resolvedType = intersectingTypes[0]; |
| } |
| System.arraycopy(intersectingTypes, 0, intersectingTypes = new ReferenceBinding[typeCount], 0, typeCount); |
| } |
| IntersectionTypeBinding18 intersectionType = (IntersectionTypeBinding18) scope.environment().createIntersectionType18(intersectingTypes); |
| // check for parameterized interface collisions (when different parameterizations occur) |
| ReferenceBinding itsSuperclass = null; |
| ReferenceBinding[] interfaces = intersectingTypes; |
| ReferenceBinding firstType = intersectingTypes[0]; |
| if (firstType.isClass()) { |
| itsSuperclass = firstType.superclass(); |
| System.arraycopy(intersectingTypes, 1, interfaces = new ReferenceBinding[typeCount - 1], 0, typeCount - 1); |
| } |
| |
| Map invocations = new HashMap(2); |
| nextInterface: for (int i = 0, interfaceCount = interfaces.length; i < interfaceCount; i++) { |
| ReferenceBinding one = interfaces[i]; |
| if (one == null) continue nextInterface; |
| if (itsSuperclass != null && scope.hasErasedCandidatesCollisions(itsSuperclass, one, invocations, intersectionType, this)) |
| continue nextInterface; |
| nextOtherInterface: for (int j = 0; j < i; j++) { |
| ReferenceBinding two = interfaces[j]; |
| if (two == null) continue nextOtherInterface; |
| if (scope.hasErasedCandidatesCollisions(one, two, invocations, intersectionType, this)) |
| continue nextInterface; |
| } |
| } |
| if ((intersectionType.tagBits & TagBits.HierarchyHasProblems) != 0) |
| return null; |
| |
| return (this.resolvedType = intersectionType); |
| } |
| |
| @Override |
| public char[][] getTypeName() { |
| // we need to keep a return value that is a char[][] |
| return this.typeReferences[0].getTypeName(); |
| } |
| |
| @Override |
| public void traverse(ASTVisitor visitor, BlockScope scope) { |
| if (visitor.visit(this, scope)) { |
| int length = this.typeReferences == null ? 0 : this.typeReferences.length; |
| for (int i = 0; i < length; i++) { |
| this.typeReferences[i].traverse(visitor, scope); |
| } |
| } |
| visitor.endVisit(this, scope); |
| } |
| |
| @Override |
| public void traverse(ASTVisitor visitor, ClassScope scope) { |
| throw new UnsupportedOperationException("Unexpected traversal request: IntersectionTypeReference in class scope"); //$NON-NLS-1$ |
| } |
| |
| @Override |
| public StringBuffer printExpression(int indent, StringBuffer output) { |
| int length = this.typeReferences == null ? 0 : this.typeReferences.length; |
| printIndent(indent, output); |
| for (int i = 0; i < length; i++) { |
| this.typeReferences[i].printExpression(0, output); |
| if (i != length - 1) { |
| output.append(" & "); //$NON-NLS-1$ |
| } |
| } |
| return output; |
| } |
| } |