/******************************************************************************* * Copyright (c) 2000, 2011 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.che.ide.ext.java.jdt.internal.corext.codemanipulation; import org.eclipse.che.ide.ext.java.jdt.core.dom.AST; import org.eclipse.che.ide.ext.java.jdt.core.dom.ASTNode; import org.eclipse.che.ide.ext.java.jdt.core.dom.ASTParser; import org.eclipse.che.ide.ext.java.jdt.core.dom.AbstractTypeDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.Annotation; import org.eclipse.che.ide.ext.java.jdt.core.dom.AnonymousClassDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.ArrayAccess; import org.eclipse.che.ide.ext.java.jdt.core.dom.ArrayCreation; import org.eclipse.che.ide.ext.java.jdt.core.dom.ArrayInitializer; import org.eclipse.che.ide.ext.java.jdt.core.dom.ArrayType; import org.eclipse.che.ide.ext.java.jdt.core.dom.AssertStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.Assignment; import org.eclipse.che.ide.ext.java.jdt.core.dom.BodyDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.CastExpression; import org.eclipse.che.ide.ext.java.jdt.core.dom.ClassInstanceCreation; import org.eclipse.che.ide.ext.java.jdt.core.dom.CompilationUnit; import org.eclipse.che.ide.ext.java.jdt.core.dom.ConditionalExpression; import org.eclipse.che.ide.ext.java.jdt.core.dom.ConstructorInvocation; import org.eclipse.che.ide.ext.java.jdt.core.dom.Expression; import org.eclipse.che.ide.ext.java.jdt.core.dom.FieldAccess; import org.eclipse.che.ide.ext.java.jdt.core.dom.FieldDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.IBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.IMethodBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.ITypeBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.IVariableBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.InfixExpression; import org.eclipse.che.ide.ext.java.jdt.core.dom.Initializer; import org.eclipse.che.ide.ext.java.jdt.core.dom.InstanceofExpression; import org.eclipse.che.ide.ext.java.jdt.core.dom.MemberValuePair; import org.eclipse.che.ide.ext.java.jdt.core.dom.MethodDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.MethodInvocation; import org.eclipse.che.ide.ext.java.jdt.core.dom.Modifier; import org.eclipse.che.ide.ext.java.jdt.core.dom.Name; import org.eclipse.che.ide.ext.java.jdt.core.dom.ParameterizedType; import org.eclipse.che.ide.ext.java.jdt.core.dom.PrefixExpression; import org.eclipse.che.ide.ext.java.jdt.core.dom.PrimitiveType; import org.eclipse.che.ide.ext.java.jdt.core.dom.PrimitiveType.Code; import org.eclipse.che.ide.ext.java.jdt.core.dom.QualifiedName; import org.eclipse.che.ide.ext.java.jdt.core.dom.QualifiedType; import org.eclipse.che.ide.ext.java.jdt.core.dom.SimpleName; import org.eclipse.che.ide.ext.java.jdt.core.dom.SimpleType; import org.eclipse.che.ide.ext.java.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.Statement; import org.eclipse.che.ide.ext.java.jdt.core.dom.StructuralPropertyDescriptor; import org.eclipse.che.ide.ext.java.jdt.core.dom.SuperConstructorInvocation; import org.eclipse.che.ide.ext.java.jdt.core.dom.SuperMethodInvocation; import org.eclipse.che.ide.ext.java.jdt.core.dom.SwitchCase; import org.eclipse.che.ide.ext.java.jdt.core.dom.SwitchStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.TagElement; import org.eclipse.che.ide.ext.java.jdt.core.dom.TryStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.Type; import org.eclipse.che.ide.ext.java.jdt.core.dom.TypeDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.TypeLiteral; import org.eclipse.che.ide.ext.java.jdt.core.dom.TypeParameter; import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclarationExpression; import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclarationStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.WildcardType; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.ASTNodeFactory; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.ASTNodes; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.Bindings; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.GenericVisitor; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.ScopeAnalyzer; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.TypeBindingVisitor; import org.eclipse.che.ide.ext.java.jdt.internal.ui.BindingLabelProvider; import org.eclipse.che.ide.ext.java.worker.WorkerMessageHandler; import org.eclipse.che.ide.ext.java.jdt.text.Document; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; public class ASTResolving { public static ITypeBinding guessBindingForReference(ASTNode node) { return Bindings.normalizeTypeBinding(getPossibleReferenceBinding(node)); } private static ITypeBinding getPossibleReferenceBinding(ASTNode node) { ASTNode parent = node.getParent(); switch (parent.getNodeType()) { case ASTNode.ASSIGNMENT: Assignment assignment = (Assignment)parent; if (node.equals(assignment.getLeftHandSide())) { // field write access: xx= expression return assignment.getRightHandSide().resolveTypeBinding(); } // read access return assignment.getLeftHandSide().resolveTypeBinding(); case ASTNode.INFIX_EXPRESSION: InfixExpression infix = (InfixExpression)parent; InfixExpression.Operator op = infix.getOperator(); if (op == InfixExpression.Operator.CONDITIONAL_AND || op == InfixExpression.Operator.CONDITIONAL_OR) { // boolean operation return infix.getAST().resolveWellKnownType("boolean"); //$NON-NLS-1$ } else if (op == InfixExpression.Operator.LEFT_SHIFT || op == InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED || op == InfixExpression.Operator.RIGHT_SHIFT_SIGNED) { // asymmetric operation return infix.getAST().resolveWellKnownType("int"); //$NON-NLS-1$ } if (node.equals(infix.getLeftOperand())) { // xx operation expression ITypeBinding rigthHandBinding = infix.getRightOperand().resolveTypeBinding(); if (rigthHandBinding != null) { return rigthHandBinding; } } else { // expression operation xx ITypeBinding leftHandBinding = infix.getLeftOperand().resolveTypeBinding(); if (leftHandBinding != null) { return leftHandBinding; } } if (op != InfixExpression.Operator.EQUALS && op != InfixExpression.Operator.NOT_EQUALS) { return infix.getAST().resolveWellKnownType("int"); //$NON-NLS-1$ } break; case ASTNode.INSTANCEOF_EXPRESSION: InstanceofExpression instanceofExpression = (InstanceofExpression)parent; return instanceofExpression.getRightOperand().resolveBinding(); case ASTNode.VARIABLE_DECLARATION_FRAGMENT: VariableDeclarationFragment frag = (VariableDeclarationFragment)parent; if (frag.getInitializer().equals(node)) { return frag.getName().resolveTypeBinding(); } break; case ASTNode.SUPER_METHOD_INVOCATION: SuperMethodInvocation superMethodInvocation = (SuperMethodInvocation)parent; IMethodBinding superMethodBinding = ASTNodes.getMethodBinding(superMethodInvocation.getName()); if (superMethodBinding != null) { return getParameterTypeBinding(node, superMethodInvocation.arguments(), superMethodBinding); } break; case ASTNode.METHOD_INVOCATION: MethodInvocation methodInvocation = (MethodInvocation)parent; IMethodBinding methodBinding = methodInvocation.resolveMethodBinding(); if (methodBinding != null) { return getParameterTypeBinding(node, methodInvocation.arguments(), methodBinding); } break; case ASTNode.SUPER_CONSTRUCTOR_INVOCATION: { SuperConstructorInvocation superInvocation = (SuperConstructorInvocation)parent; IMethodBinding superBinding = superInvocation.resolveConstructorBinding(); if (superBinding != null) { return getParameterTypeBinding(node, superInvocation.arguments(), superBinding); } break; } case ASTNode.CONSTRUCTOR_INVOCATION: { ConstructorInvocation constrInvocation = (ConstructorInvocation)parent; IMethodBinding constrBinding = constrInvocation.resolveConstructorBinding(); if (constrBinding != null) { return getParameterTypeBinding(node, constrInvocation.arguments(), constrBinding); } break; } case ASTNode.CLASS_INSTANCE_CREATION: { ClassInstanceCreation creation = (ClassInstanceCreation)parent; IMethodBinding creationBinding = creation.resolveConstructorBinding(); if (creationBinding != null) { return getParameterTypeBinding(node, creation.arguments(), creationBinding); } break; } case ASTNode.PARENTHESIZED_EXPRESSION: return guessBindingForReference(parent); case ASTNode.ARRAY_ACCESS: if (((ArrayAccess)parent).getIndex().equals(node)) { return parent.getAST().resolveWellKnownType("int"); //$NON-NLS-1$ } else { ITypeBinding parentBinding = getPossibleReferenceBinding(parent); if (parentBinding == null) { parentBinding = parent.getAST().resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$ } return parentBinding.createArrayType(1); } case ASTNode.ARRAY_CREATION: if (((ArrayCreation)parent).dimensions().contains(node)) { return parent.getAST().resolveWellKnownType("int"); //$NON-NLS-1$ } break; case ASTNode.ARRAY_INITIALIZER: ASTNode initializerParent = parent.getParent(); int dim = 1; while (initializerParent instanceof ArrayInitializer) { initializerParent = initializerParent.getParent(); dim++; } Type creationType = null; if (initializerParent instanceof ArrayCreation) { creationType = ((ArrayCreation)initializerParent).getType(); } else if (initializerParent instanceof VariableDeclaration) { VariableDeclaration varDecl = (VariableDeclaration)initializerParent; creationType = ASTNodes.getType(varDecl); dim -= varDecl.getExtraDimensions(); } else if (initializerParent instanceof MemberValuePair) { String name = ((MemberValuePair)initializerParent).getName().getIdentifier(); IMethodBinding annotMember = findAnnotationMember((Annotation)initializerParent.getParent(), name); if (annotMember != null) { return getReducedDimensionBinding(annotMember.getReturnType(), dim); } } if (creationType != null) { while ((creationType instanceof ArrayType) && dim > 0) { creationType = ((ArrayType)creationType).getComponentType(); dim--; } return creationType.resolveBinding(); } break; case ASTNode.CONDITIONAL_EXPRESSION: ConditionalExpression expression = (ConditionalExpression)parent; if (node.equals(expression.getExpression())) { return parent.getAST().resolveWellKnownType("boolean"); //$NON-NLS-1$ } if (node.equals(expression.getElseExpression())) { return expression.getThenExpression().resolveTypeBinding(); } return expression.getElseExpression().resolveTypeBinding(); case ASTNode.POSTFIX_EXPRESSION: return parent.getAST().resolveWellKnownType("int"); //$NON-NLS-1$ case ASTNode.PREFIX_EXPRESSION: if (((PrefixExpression)parent).getOperator() == PrefixExpression.Operator.NOT) { return parent.getAST().resolveWellKnownType("boolean"); //$NON-NLS-1$ } return parent.getAST().resolveWellKnownType("int"); //$NON-NLS-1$ case ASTNode.IF_STATEMENT: case ASTNode.WHILE_STATEMENT: case ASTNode.DO_STATEMENT: if (node instanceof Expression) { return parent.getAST().resolveWellKnownType("boolean"); //$NON-NLS-1$ } break; case ASTNode.SWITCH_STATEMENT: if (((SwitchStatement)parent).getExpression().equals(node)) { return parent.getAST().resolveWellKnownType("int"); //$NON-NLS-1$ } break; case ASTNode.RETURN_STATEMENT: MethodDeclaration decl = ASTResolving.findParentMethodDeclaration(parent); if (decl != null && !decl.isConstructor()) { return decl.getReturnType2().resolveBinding(); } break; case ASTNode.CAST_EXPRESSION: return ((CastExpression)parent).getType().resolveBinding(); case ASTNode.THROW_STATEMENT: case ASTNode.CATCH_CLAUSE: return parent.getAST().resolveWellKnownType("java.lang.Exception"); //$NON-NLS-1$ case ASTNode.FIELD_ACCESS: if (node.equals(((FieldAccess)parent).getName())) { return getPossibleReferenceBinding(parent); } break; case ASTNode.SUPER_FIELD_ACCESS: return getPossibleReferenceBinding(parent); case ASTNode.QUALIFIED_NAME: if (node.equals(((QualifiedName)parent).getName())) { return getPossibleReferenceBinding(parent); } break; case ASTNode.SWITCH_CASE: if (node.equals(((SwitchCase)parent).getExpression()) && parent.getParent() instanceof SwitchStatement) { return ((SwitchStatement)parent.getParent()).getExpression().resolveTypeBinding(); } break; case ASTNode.ASSERT_STATEMENT: if (node.getLocationInParent() == AssertStatement.EXPRESSION_PROPERTY) { return parent.getAST().resolveWellKnownType("boolean"); //$NON-NLS-1$ } return parent.getAST().resolveWellKnownType("java.lang.String"); //$NON-NLS-1$ case ASTNode.SINGLE_MEMBER_ANNOTATION: { IMethodBinding annotMember = findAnnotationMember((Annotation)parent, "value"); //$NON-NLS-1$ if (annotMember != null) { return annotMember.getReturnType(); } break; } case ASTNode.MEMBER_VALUE_PAIR: { String name = ((MemberValuePair)parent).getName().getIdentifier(); IMethodBinding annotMember = findAnnotationMember((Annotation)parent.getParent(), name); if (annotMember != null) { return annotMember.getReturnType(); } break; } default: // do nothing } return null; } private static IMethodBinding findAnnotationMember(Annotation annotation, String name) { ITypeBinding annotBinding = annotation.resolveTypeBinding(); if (annotBinding != null) { return Bindings.findMethodInType(annotBinding, name, (String[])null); } return null; } public static Type guessTypeForReference(AST ast, ASTNode node) { ASTNode parent = node.getParent(); while (parent != null) { switch (parent.getNodeType()) { case ASTNode.VARIABLE_DECLARATION_FRAGMENT: if (((VariableDeclarationFragment)parent).getInitializer() == node) { return ASTNodeFactory.newType(ast, (VariableDeclaration)parent); } return null; case ASTNode.SINGLE_VARIABLE_DECLARATION: if (((VariableDeclarationFragment)parent).getInitializer() == node) { return ASTNodeFactory.newType(ast, (VariableDeclaration)parent); } return null; case ASTNode.ARRAY_ACCESS: if (!((ArrayAccess)parent).getIndex().equals(node)) { Type type = guessTypeForReference(ast, parent); if (type != null) { return ast.newArrayType(type); } } return null; case ASTNode.FIELD_ACCESS: if (node.equals(((FieldAccess)parent).getName())) { node = parent; parent = parent.getParent(); } else { return null; } break; case ASTNode.SUPER_FIELD_ACCESS: case ASTNode.PARENTHESIZED_EXPRESSION: node = parent; parent = parent.getParent(); break; case ASTNode.QUALIFIED_NAME: if (node.equals(((QualifiedName)parent).getName())) { node = parent; parent = parent.getParent(); } else { return null; } break; default: return null; } } return null; } private static ITypeBinding getReducedDimensionBinding(ITypeBinding arrayBinding, int dimsToReduce) { while (dimsToReduce > 0) { arrayBinding = arrayBinding.getComponentType(); dimsToReduce--; } return arrayBinding; } private static ITypeBinding getParameterTypeBinding(ASTNode node, List<Expression> args, IMethodBinding binding) { ITypeBinding[] paramTypes = binding.getParameterTypes(); int index = args.indexOf(node); if (binding.isVarargs() && index >= paramTypes.length - 1) { return paramTypes[paramTypes.length - 1].getComponentType(); } if (index >= 0 && index < paramTypes.length) { return paramTypes[index]; } return null; } public static ITypeBinding guessBindingForTypeReference(ASTNode node) { StructuralPropertyDescriptor locationInParent = node.getLocationInParent(); if (locationInParent == QualifiedName.QUALIFIER_PROPERTY) { return null; // can't guess type for X.A } if (locationInParent == SimpleType.NAME_PROPERTY) { node = node.getParent(); } ITypeBinding binding = Bindings.normalizeTypeBinding(getPossibleTypeBinding(node)); if (binding != null) { if (binding.isWildcardType()) { return normalizeWildcardType(binding, true, node.getAST()); } } return binding; } private static ITypeBinding getPossibleTypeBinding(ASTNode node) { ASTNode parent = node.getParent(); switch (parent.getNodeType()) { case ASTNode.ARRAY_TYPE: { int dim = 1; while (parent.getParent() instanceof ArrayType) { parent = parent.getParent(); dim++; } ITypeBinding parentBinding = getPossibleTypeBinding(parent); if (parentBinding != null && parentBinding.getDimensions() == dim) { return parentBinding.getElementType(); } return null; } case ASTNode.PARAMETERIZED_TYPE: { ITypeBinding parentBinding = getPossibleTypeBinding(parent); if (parentBinding == null || !parentBinding.isParameterizedType()) { return null; } if (node.getLocationInParent() == ParameterizedType.TYPE_PROPERTY) { return parentBinding; } ITypeBinding[] typeArguments = parentBinding.getTypeArguments(); List<Type> argumentNodes = ((ParameterizedType)parent).typeArguments(); int index = argumentNodes.indexOf(node); if (index != -1 && typeArguments.length == argumentNodes.size()) { return typeArguments[index]; } return null; } case ASTNode.WILDCARD_TYPE: { ITypeBinding parentBinding = getPossibleTypeBinding(parent); if (parentBinding == null || !parentBinding.isWildcardType()) { return null; } WildcardType wildcardType = (WildcardType)parent; if (parentBinding.isUpperbound() == wildcardType.isUpperBound()) { return parentBinding.getBound(); } return null; } case ASTNode.QUALIFIED_TYPE: { ITypeBinding parentBinding = getPossibleTypeBinding(parent); if (parentBinding == null || !parentBinding.isMember()) { return null; } if (node.getLocationInParent() == QualifiedType.QUALIFIER_PROPERTY) { return parentBinding.getDeclaringClass(); } return parentBinding; } case ASTNode.VARIABLE_DECLARATION_STATEMENT: return guessVariableType(((VariableDeclarationStatement)parent).fragments()); case ASTNode.FIELD_DECLARATION: return guessVariableType(((FieldDeclaration)parent).fragments()); case ASTNode.VARIABLE_DECLARATION_EXPRESSION: return guessVariableType(((VariableDeclarationExpression)parent).fragments()); case ASTNode.SINGLE_VARIABLE_DECLARATION: SingleVariableDeclaration varDecl = (SingleVariableDeclaration)parent; if (varDecl.getInitializer() != null) { return Bindings.normalizeTypeBinding(varDecl.getInitializer().resolveTypeBinding()); } break; case ASTNode.ARRAY_CREATION: ArrayCreation creation = (ArrayCreation)parent; if (creation.getInitializer() != null) { return creation.getInitializer().resolveTypeBinding(); } return getPossibleReferenceBinding(parent); case ASTNode.TYPE_LITERAL: return ((TypeLiteral)parent).getType().resolveBinding(); case ASTNode.CLASS_INSTANCE_CREATION: return getPossibleReferenceBinding(parent); case ASTNode.CAST_EXPRESSION: return getPossibleReferenceBinding(parent); case ASTNode.TAG_ELEMENT: TagElement tagElement = (TagElement)parent; if (TagElement.TAG_THROWS.equals(tagElement.getTagName()) || TagElement.TAG_EXCEPTION.equals(tagElement.getTagName())) { ASTNode methNode = tagElement.getParent().getParent(); if (methNode instanceof MethodDeclaration) { List<Name> thrownExceptions = ((MethodDeclaration)methNode).thrownExceptions(); if (thrownExceptions.size() == 1) { return thrownExceptions.get(0).resolveTypeBinding(); } } } break; } return null; } private static ITypeBinding guessVariableType(List<VariableDeclarationFragment> fragments) { for (Iterator<VariableDeclarationFragment> iter = fragments.iterator(); iter.hasNext(); ) { VariableDeclarationFragment frag = iter.next(); if (frag.getInitializer() != null) { return Bindings.normalizeTypeBinding(frag.getInitializer().resolveTypeBinding()); } } return null; } /** * Finds all type bindings that contain a method of a given signature * * @param searchRoot * the ast node to start the search from * @param selector * the method name * @param arguments * the method arguments * @param context * the context in which the method would be called * @return returns all types known in the AST that have a method with a given name */ public static ITypeBinding[] getQualifierGuess(ASTNode searchRoot, final String selector, List<Expression> arguments, final IBinding context) { final int nArgs = arguments.size(); final ArrayList<ITypeBinding> result = new ArrayList<ITypeBinding>(); // test if selector is a object method ITypeBinding binding = searchRoot.getAST().resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$ IMethodBinding[] objectMethods = binding.getDeclaredMethods(); for (int i = 0; i < objectMethods.length; i++) { IMethodBinding meth = objectMethods[i]; if (meth.getName().equals(selector) && meth.getParameterTypes().length == nArgs) { return new ITypeBinding[]{binding}; } } visitAllBindings(searchRoot, new TypeBindingVisitor() { private HashSet<String> fVisitedBindings = new HashSet<String>(100); public boolean visit(ITypeBinding node) { node = Bindings.normalizeTypeBinding(node); if (node == null) { return true; } if (!fVisitedBindings.add(node.getKey())) { return true; } if (node.isGenericType()) { return true; // only look at parameterized types } if (context != null && !isUseableTypeInContext(node, context, false)) { return true; } IMethodBinding[] methods = node.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { IMethodBinding meth = methods[i]; if (meth.getName().equals(selector) && meth.getParameterTypes().length == nArgs) { result.add(node); } } return true; } }); return result.toArray(new ITypeBinding[result.size()]); } public static void visitAllBindings(ASTNode astRoot, TypeBindingVisitor visitor) { try { astRoot.accept(new AllBindingsVisitor(visitor)); } catch (AllBindingsVisitor.VisitCancelledException e) { } } private static class AllBindingsVisitor extends GenericVisitor { private final TypeBindingVisitor fVisitor; private static class VisitCancelledException extends RuntimeException { private static final long serialVersionUID = 1L; } public AllBindingsVisitor(TypeBindingVisitor visitor) { super(true); fVisitor = visitor; } @Override public boolean visit(SimpleName node) { ITypeBinding binding = node.resolveTypeBinding(); if (binding != null) { boolean res = fVisitor.visit(binding); if (res) { res = Bindings.visitHierarchy(binding, fVisitor); } if (!res) { throw new VisitCancelledException(); } } return false; } } public static IBinding getParentMethodOrTypeBinding(ASTNode node) { do { if (node instanceof MethodDeclaration) { return ((MethodDeclaration)node).resolveBinding(); } else if (node instanceof AbstractTypeDeclaration) { return ((AbstractTypeDeclaration)node).resolveBinding(); } else if (node instanceof AnonymousClassDeclaration) { return ((AnonymousClassDeclaration)node).resolveBinding(); } node = node.getParent(); } while (node != null); return null; } public static BodyDeclaration findParentBodyDeclaration(ASTNode node) { while ((node != null) && (!(node instanceof BodyDeclaration))) { node = node.getParent(); } return (BodyDeclaration)node; } public static BodyDeclaration findParentBodyDeclaration(ASTNode node, boolean treatModifiersOutside) { StructuralPropertyDescriptor lastLocation = null; while (node != null) { if (node instanceof BodyDeclaration) { BodyDeclaration decl = (BodyDeclaration)node; if (!treatModifiersOutside || lastLocation != decl.getModifiersProperty()) { return decl; } treatModifiersOutside = false; } lastLocation = node.getLocationInParent(); node = node.getParent(); } return (BodyDeclaration)node; } public static CompilationUnit findParentCompilationUnit(ASTNode node) { return (CompilationUnit)findAncestor(node, ASTNode.COMPILATION_UNIT); } /** * Finds the parent type of a node. * * @param node * the node inside the type to find * @param treatModifiersOutside * if set, modifiers are not part of their type, but of the type's parent * @return returns either a AbstractTypeDeclaration or an AnonymousTypeDeclaration */ public static ASTNode findParentType(ASTNode node, boolean treatModifiersOutside) { StructuralPropertyDescriptor lastLocation = null; while (node != null) { if (node instanceof AbstractTypeDeclaration) { AbstractTypeDeclaration decl = (AbstractTypeDeclaration)node; if (!treatModifiersOutside || lastLocation != decl.getModifiersProperty()) { return decl; } } else if (node instanceof AnonymousClassDeclaration) { return node; } lastLocation = node.getLocationInParent(); node = node.getParent(); } return null; } public static ASTNode findParentType(ASTNode node) { return findParentType(node, false); } /** * Returns the method binding of the node's parent method declaration or <code>null</code> if * the node is not inside a method. * * @param node * the ast node * @return the method binding of the node's parent method declaration or <code>null</code> if * the node */ public static MethodDeclaration findParentMethodDeclaration(ASTNode node) { while (node != null) { if (node.getNodeType() == ASTNode.METHOD_DECLARATION) { return (MethodDeclaration)node; } if (node instanceof AbstractTypeDeclaration || node instanceof AnonymousClassDeclaration) { return null; } node = node.getParent(); } return null; } public static ASTNode findAncestor(ASTNode node, int nodeType) { while ((node != null) && (node.getNodeType() != nodeType)) { node = node.getParent(); } return node; } public static Statement findParentStatement(ASTNode node) { while ((node != null) && (!(node instanceof Statement))) { node = node.getParent(); if (node instanceof BodyDeclaration) { return null; } } return (Statement)node; } public static TryStatement findParentTryStatement(ASTNode node) { while ((node != null) && (!(node instanceof TryStatement))) { node = node.getParent(); if (node instanceof BodyDeclaration) { return null; } } return (TryStatement)node; } public static boolean isInsideConstructorInvocation(MethodDeclaration methodDeclaration, ASTNode node) { if (methodDeclaration.isConstructor()) { Statement statement = ASTResolving.findParentStatement(node); if (statement instanceof ConstructorInvocation || statement instanceof SuperConstructorInvocation) { return true; // argument in a this or super call } } return false; } public static boolean isInsideModifiers(ASTNode node) { while (node != null && !(node instanceof BodyDeclaration)) { if (node instanceof Annotation) { return true; } node = node.getParent(); } return false; } public static boolean isInStaticContext(ASTNode selectedNode) { BodyDeclaration decl = ASTResolving.findParentBodyDeclaration(selectedNode); if (decl instanceof MethodDeclaration) { if (isInsideConstructorInvocation((MethodDeclaration)decl, selectedNode)) { return true; } return Modifier.isStatic(decl.getModifiers()); } else if (decl instanceof Initializer) { return Modifier.isStatic(((Initializer)decl).getModifiers()); } else if (decl instanceof FieldDeclaration) { return Modifier.isStatic(((FieldDeclaration)decl).getModifiers()); } return false; } public static boolean isWriteAccess(Name selectedNode) { ASTNode curr = selectedNode; ASTNode parent = curr.getParent(); while (parent != null) { switch (parent.getNodeType()) { case ASTNode.QUALIFIED_NAME: if (((QualifiedName)parent).getQualifier() == curr) { return false; } break; case ASTNode.FIELD_ACCESS: if (((FieldAccess)parent).getExpression() == curr) { return false; } break; case ASTNode.SUPER_FIELD_ACCESS: break; case ASTNode.ASSIGNMENT: return ((Assignment)parent).getLeftHandSide() == curr; case ASTNode.VARIABLE_DECLARATION_FRAGMENT: case ASTNode.SINGLE_VARIABLE_DECLARATION: return ((VariableDeclaration)parent).getName() == curr; case ASTNode.POSTFIX_EXPRESSION: return true; case ASTNode.PREFIX_EXPRESSION: PrefixExpression.Operator op = ((PrefixExpression)parent).getOperator(); return op == PrefixExpression.Operator.DECREMENT || op == PrefixExpression.Operator.INCREMENT; default: return false; } curr = parent; parent = curr.getParent(); } return false; } public static int getPossibleTypeKinds(ASTNode node, boolean is50OrHigher) { int kinds = internalGetPossibleTypeKinds(node); if (!is50OrHigher) { kinds &= (SimilarElementsRequestor.INTERFACES | SimilarElementsRequestor.CLASSES); } return kinds; } private static int internalGetPossibleTypeKinds(ASTNode node) { int kind = SimilarElementsRequestor.ALL_TYPES; int mask = SimilarElementsRequestor.ALL_TYPES | SimilarElementsRequestor.VOIDTYPE; ASTNode parent = node.getParent(); while (parent instanceof QualifiedName) { if (node.getLocationInParent() == QualifiedName.QUALIFIER_PROPERTY) { return SimilarElementsRequestor.REF_TYPES; } node = parent; parent = parent.getParent(); mask = SimilarElementsRequestor.REF_TYPES; } while (parent instanceof Type) { if (parent instanceof QualifiedType) { if (node.getLocationInParent() == QualifiedType.QUALIFIER_PROPERTY) { return mask & (SimilarElementsRequestor.REF_TYPES); } mask &= SimilarElementsRequestor.REF_TYPES; } else if (parent instanceof ParameterizedType) { if (node.getLocationInParent() == ParameterizedType.TYPE_ARGUMENTS_PROPERTY) { return mask & SimilarElementsRequestor.REF_TYPES_AND_VAR; } mask &= SimilarElementsRequestor.CLASSES | SimilarElementsRequestor.INTERFACES; } else if (parent instanceof WildcardType) { if (node.getLocationInParent() == WildcardType.BOUND_PROPERTY) { return mask & SimilarElementsRequestor.REF_TYPES_AND_VAR; } } node = parent; parent = parent.getParent(); } switch (parent.getNodeType()) { case ASTNode.TYPE_DECLARATION: if (node.getLocationInParent() == TypeDeclaration.SUPER_INTERFACE_TYPES_PROPERTY) { kind = SimilarElementsRequestor.INTERFACES; } else if (node.getLocationInParent() == TypeDeclaration.SUPERCLASS_TYPE_PROPERTY) { kind = SimilarElementsRequestor.CLASSES; } break; case ASTNode.ENUM_DECLARATION: kind = SimilarElementsRequestor.INTERFACES; break; case ASTNode.METHOD_DECLARATION: if (node.getLocationInParent() == MethodDeclaration.THROWN_EXCEPTIONS_PROPERTY) { kind = SimilarElementsRequestor.CLASSES; } else if (node.getLocationInParent() == MethodDeclaration.RETURN_TYPE2_PROPERTY) { kind = SimilarElementsRequestor.ALL_TYPES | SimilarElementsRequestor.VOIDTYPE; } break; case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: kind = SimilarElementsRequestor.PRIMITIVETYPES | SimilarElementsRequestor.ANNOTATIONS | SimilarElementsRequestor.ENUMS; break; case ASTNode.INSTANCEOF_EXPRESSION: kind = SimilarElementsRequestor.REF_TYPES; break; case ASTNode.THROW_STATEMENT: kind = SimilarElementsRequestor.CLASSES; break; case ASTNode.CLASS_INSTANCE_CREATION: if (((ClassInstanceCreation)parent).getAnonymousClassDeclaration() == null) { kind = SimilarElementsRequestor.CLASSES; } else { kind = SimilarElementsRequestor.CLASSES | SimilarElementsRequestor.INTERFACES; } break; case ASTNode.SINGLE_VARIABLE_DECLARATION: int superParent = parent.getParent().getNodeType(); if (superParent == ASTNode.CATCH_CLAUSE) { kind = SimilarElementsRequestor.CLASSES; } break; case ASTNode.TAG_ELEMENT: kind = SimilarElementsRequestor.REF_TYPES; break; case ASTNode.MARKER_ANNOTATION: case ASTNode.SINGLE_MEMBER_ANNOTATION: case ASTNode.NORMAL_ANNOTATION: kind = SimilarElementsRequestor.ANNOTATIONS; break; case ASTNode.TYPE_PARAMETER: if (((TypeParameter)parent).typeBounds().indexOf(node) > 0) { kind = SimilarElementsRequestor.INTERFACES; } else { kind = SimilarElementsRequestor.REF_TYPES_AND_VAR; } break; case ASTNode.TYPE_LITERAL: kind = SimilarElementsRequestor.REF_TYPES; break; default: } return kind & mask; } public static String getFullName(Name name) { return name.getFullyQualifiedName(); } // public static ICompilationUnit findCompilationUnitForBinding(ICompilationUnit cu, CompilationUnit astRoot, // ITypeBinding binding) throws JavaModelException // { // if (binding == null || !binding.isFromSource() || binding.isTypeVariable() || binding.isWildcardType()) // { // return null; // } // ASTNode node = astRoot.findDeclaringNode(binding.getTypeDeclaration()); // if (node == null) // { // ICompilationUnit targetCU = Bindings.findCompilationUnit(binding, cu.getJavaProject()); // if (targetCU != null) // { // return targetCU; // } // return null; // } // else if (node instanceof AbstractTypeDeclaration || node instanceof AnonymousClassDeclaration) // { // return cu; // } // return null; // } private static final Code[] CODE_ORDER = {PrimitiveType.CHAR, PrimitiveType.SHORT, PrimitiveType.INT, PrimitiveType.LONG, PrimitiveType.FLOAT, PrimitiveType.DOUBLE}; public static ITypeBinding[] getNarrowingTypes(AST ast, ITypeBinding type) { ArrayList<ITypeBinding> res = new ArrayList<ITypeBinding>(); res.add(type); if (type.isPrimitive()) { Code code = PrimitiveType.toCode(type.getName()); for (int i = 0; i < CODE_ORDER.length && code != CODE_ORDER[i]; i++) { String typeName = CODE_ORDER[i].toString(); res.add(ast.resolveWellKnownType(typeName)); } } return res.toArray(new ITypeBinding[res.size()]); } public static ITypeBinding[] getRelaxingTypes(AST ast, ITypeBinding type) { ArrayList<ITypeBinding> res = new ArrayList<ITypeBinding>(); res.add(type); if (type.isArray()) { res.add(ast.resolveWellKnownType("java.lang.Object")); //$NON-NLS-1$ // The following two types are not available in some j2me implementations, see https://bugs.eclipse.org/bugs/show_bug // .cgi?id=288060 : ITypeBinding serializable = ast.resolveWellKnownType("java.io.Serializable"); //$NON-NLS-1$ if (serializable != null) res.add(serializable); ITypeBinding cloneable = ast.resolveWellKnownType("java.lang.Cloneable"); //$NON-NLS-1$ if (cloneable != null) res.add(cloneable); } else if (type.isPrimitive()) { Code code = PrimitiveType.toCode(type.getName()); boolean found = false; for (int i = 0; i < CODE_ORDER.length; i++) { if (found) { String typeName = CODE_ORDER[i].toString(); res.add(ast.resolveWellKnownType(typeName)); } if (code == CODE_ORDER[i]) { found = true; } } } else { collectRelaxingTypes(res, type); } return res.toArray(new ITypeBinding[res.size()]); } private static void collectRelaxingTypes(Collection<ITypeBinding> res, ITypeBinding type) { ITypeBinding[] interfaces = type.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { ITypeBinding curr = interfaces[i]; if (!res.contains(curr)) { res.add(curr); } collectRelaxingTypes(res, curr); } ITypeBinding binding = type.getSuperclass(); if (binding != null) { if (!res.contains(binding)) { res.add(binding); } collectRelaxingTypes(res, binding); } } public static String[] getUsedVariableNames(ASTNode node) { CompilationUnit root = (CompilationUnit)node.getRoot(); Collection<String> res = (new ScopeAnalyzer(root)).getUsedVariableNames(node.getStartPosition(), node.getLength()); return res.toArray(new String[res.size()]); } private static boolean isVariableDefinedInContext(IBinding binding, ITypeBinding typeVariable) { if (binding.getKind() == IBinding.VARIABLE) { IVariableBinding var = (IVariableBinding)binding; binding = var.getDeclaringMethod(); if (binding == null) { binding = var.getDeclaringClass(); } } if (binding instanceof IMethodBinding) { if (binding == typeVariable.getDeclaringMethod()) { return true; } binding = ((IMethodBinding)binding).getDeclaringClass(); } while (binding instanceof ITypeBinding) { if (binding == typeVariable.getDeclaringClass()) { return true; } if (Modifier.isStatic(binding.getModifiers())) { break; } binding = ((ITypeBinding)binding).getDeclaringClass(); } return false; } public static boolean isUseableTypeInContext(ITypeBinding[] binding, IBinding context, boolean noWildcards) { for (int i = 0; i < binding.length; i++) { if (!isUseableTypeInContext(binding[i], context, noWildcards)) { return false; } } return true; } public static boolean isUseableTypeInContext(ITypeBinding type, IBinding context, boolean noWildcards) { if (type.isArray()) { type = type.getElementType(); } if (type.isAnonymous()) { return false; } if (type.isRawType() || type.isPrimitive()) { return true; } if (type.isTypeVariable()) { return isVariableDefinedInContext(context, type); } if (type.isGenericType()) { ITypeBinding[] typeParameters = type.getTypeParameters(); for (int i = 0; i < typeParameters.length; i++) { if (!isUseableTypeInContext(typeParameters[i], context, noWildcards)) { return false; } } return true; } if (type.isParameterizedType()) { ITypeBinding[] typeArguments = type.getTypeArguments(); for (int i = 0; i < typeArguments.length; i++) { if (!isUseableTypeInContext(typeArguments[i], context, noWildcards)) { return false; } } return true; } if (type.isCapture()) { type = type.getWildcard(); } if (type.isWildcardType()) { if (noWildcards) { return false; } if (type.getBound() != null) { return isUseableTypeInContext(type.getBound(), context, noWildcards); } } return true; } /** * Use this method before creating a type for a wildcard. Either to assign a wildcard to a new type or for a type to be assigned. * * @param wildcardType * the wildcard type to normalize * @param isBindingToAssign * If true, then a new receiver type is searched (X x= s), else the type of a sender (R r= x) * @param ast * th current AST * @return Returns the normalized binding or null when only the 'null' binding */ public static ITypeBinding normalizeWildcardType(ITypeBinding wildcardType, boolean isBindingToAssign, AST ast) { ITypeBinding bound = wildcardType.getBound(); if (isBindingToAssign) { if (bound == null || !wildcardType.isUpperbound()) { return ast.resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$ } } else { if (bound == null || wildcardType.isUpperbound()) { return null; } } return bound; } // pretty signatures public static String getTypeSignature(ITypeBinding type) { return BindingLabelProvider.getBindingLabel(type, BindingLabelProvider.DEFAULT_TEXTFLAGS); } public static String getMethodSignature(IMethodBinding binding) { return BindingLabelProvider.getBindingLabel(binding, BindingLabelProvider.DEFAULT_TEXTFLAGS); } public static String getMethodSignature(String name, ITypeBinding[] params, boolean isVarArgs) { StringBuffer buf = new StringBuffer(); buf.append(name).append('('); for (int i = 0; i < params.length; i++) { if (i > 0) { buf.append(", "); } if (isVarArgs && i == params.length - 1) { buf.append(getTypeSignature(params[i].getElementType())); buf.append("..."); //$NON-NLS-1$ } else { buf.append(getTypeSignature(params[i])); } } buf.append(')'); return buf.toString(); } public static CompilationUnit createQuickFixAST(Document document) { ASTParser astParser = ASTParser.newParser(AST.JLS4); astParser.setSource(document.get().toCharArray()); astParser.setResolveBindings(true); astParser.setStatementsRecovery(true); astParser.setBindingsRecovery(true); astParser.setNameEnvironment(WorkerMessageHandler.get().getNameEnvironment()); return (CompilationUnit)astParser.createAST(); } }