/******************************************************************************* * 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 * Renaud Waldura <renaud+eclipse@waldura.com> - New class/interface with wizard *******************************************************************************/ package org.eclipse.che.ide.ext.java.jdt.internal.text.correction; import org.eclipse.che.ide.ext.java.jdt.Images; import org.eclipse.che.ide.ext.java.jdt.codeassistant.QualifiedTypeNameHistory; import org.eclipse.che.ide.ext.java.jdt.codeassistant.api.IProblemLocation; import org.eclipse.che.ide.ext.java.jdt.core.Signature; import org.eclipse.che.ide.ext.java.jdt.core.dom.AST; import org.eclipse.che.ide.ext.java.jdt.core.dom.ASTMatcher; import org.eclipse.che.ide.ext.java.jdt.core.dom.ASTNode; import org.eclipse.che.ide.ext.java.jdt.core.dom.Annotation; import org.eclipse.che.ide.ext.java.jdt.core.dom.ArrayType; 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.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.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.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.NormalAnnotation; import org.eclipse.che.ide.ext.java.jdt.core.dom.ParenthesizedExpression; import org.eclipse.che.ide.ext.java.jdt.core.dom.QualifiedName; 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.SingleMemberAnnotation; 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.SuperFieldAccess; 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.ThisExpression; import org.eclipse.che.ide.ext.java.jdt.core.dom.ThrowStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.Type; import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ImportRewrite; import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; import org.eclipse.che.ide.ext.java.jdt.internal.corext.codemanipulation.ASTResolving; import org.eclipse.che.ide.ext.java.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext; import org.eclipse.che.ide.ext.java.jdt.internal.corext.codemanipulation.SimilarElement; import org.eclipse.che.ide.ext.java.jdt.internal.corext.codemanipulation.SimilarElementsRequestor; import org.eclipse.che.ide.ext.java.jdt.internal.corext.codemanipulation.StubUtility; 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.ScopeAnalyzer; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.ASTRewriteCorrectionProposal; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.AddArgumentCorrectionProposal; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.AddImportCorrectionProposal; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.CUCorrectionProposal; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.CastCorrectionProposal; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.ChangeMethodSignatureProposal.ChangeDescription; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.ChangeMethodSignatureProposal.EditDescription; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.LinkedCorrectionProposal; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.NewMethodCorrectionProposal; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.NewVariableCorrectionProposal; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.RenameNodeCorrectionProposal; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.ReplaceCorrectionProposal; import org.eclipse.che.ide.ext.java.jdt.internal.ui.BindingLabelProvider; import org.eclipse.che.ide.ext.java.jdt.internal.ui.JavaElementLabels; import org.eclipse.che.ide.ext.java.jdt.quickassist.api.InvocationContext; import org.eclipse.che.ide.ext.java.jdt.text.Document; import org.eclipse.che.ide.runtime.CoreException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; public class UnresolvedElementsSubProcessor { private static final String ADD_IMPORT_ID = "org.eclipse.jdt.ui.correction.addImport"; //$NON-NLS-1$ public static void getVariableProposals(InvocationContext context, IProblemLocation problem, IVariableBinding resolvedField, Collection<ICommandAccess> proposals) throws CoreException { CompilationUnit astRoot = context.getASTRoot(); ASTNode selectedNode = problem.getCoveredNode(astRoot); if (selectedNode == null) { return; } // type that defines the variable ITypeBinding binding = null; ITypeBinding declaringTypeBinding = Bindings.getBindingOfParentTypeContext(selectedNode); if (declaringTypeBinding == null) { return; } // possible type kind of the node boolean suggestVariableProposals = true; int typeKind = 0; while (selectedNode instanceof ParenthesizedExpression) { selectedNode = ((ParenthesizedExpression)selectedNode).getExpression(); } Name node = null; switch (selectedNode.getNodeType()) { case ASTNode.SIMPLE_NAME: node = (SimpleName)selectedNode; ASTNode parent = node.getParent(); StructuralPropertyDescriptor locationInParent = node.getLocationInParent(); if (locationInParent == MethodInvocation.EXPRESSION_PROPERTY) { typeKind = SimilarElementsRequestor.CLASSES; } else if (locationInParent == FieldAccess.NAME_PROPERTY) { Expression expression = ((FieldAccess)parent).getExpression(); if (expression != null) { binding = expression.resolveTypeBinding(); if (binding == null) { node = null; } } } else if (parent instanceof SimpleType) { suggestVariableProposals = false; typeKind = SimilarElementsRequestor.REF_TYPES_AND_VAR; } else if (parent instanceof QualifiedName) { Name qualifier = ((QualifiedName)parent).getQualifier(); if (qualifier != node) { binding = qualifier.resolveTypeBinding(); } else { typeKind = SimilarElementsRequestor.REF_TYPES; } ASTNode outerParent = parent.getParent(); while (outerParent instanceof QualifiedName) { outerParent = outerParent.getParent(); } if (outerParent instanceof SimpleType) { typeKind = SimilarElementsRequestor.REF_TYPES; suggestVariableProposals = false; } } else if (locationInParent == SwitchCase.EXPRESSION_PROPERTY) { ITypeBinding switchExp = ((SwitchStatement)node.getParent().getParent()).getExpression().resolveTypeBinding(); if (switchExp != null && switchExp.isEnum()) { binding = switchExp; } } else if (locationInParent == SuperFieldAccess.NAME_PROPERTY) { binding = declaringTypeBinding.getSuperclass(); } break; case ASTNode.QUALIFIED_NAME: QualifiedName qualifierName = (QualifiedName)selectedNode; ITypeBinding qualifierBinding = qualifierName.getQualifier().resolveTypeBinding(); if (qualifierBinding != null) { node = qualifierName.getName(); binding = qualifierBinding; } else { node = qualifierName.getQualifier(); typeKind = SimilarElementsRequestor.REF_TYPES; suggestVariableProposals = node.isSimpleName(); } if (selectedNode.getParent() instanceof SimpleType) { typeKind = SimilarElementsRequestor.REF_TYPES; suggestVariableProposals = false; } break; case ASTNode.FIELD_ACCESS: FieldAccess access = (FieldAccess)selectedNode; Expression expression = access.getExpression(); if (expression != null) { binding = expression.resolveTypeBinding(); if (binding != null) { node = access.getName(); } } break; case ASTNode.SUPER_FIELD_ACCESS: binding = declaringTypeBinding.getSuperclass(); node = ((SuperFieldAccess)selectedNode).getName(); break; default: } if (node == null) { return; } // add type proposals if (typeKind != 0) { // if (!JavaModelUtil.is50OrHigher(cu.getJavaProject())) // { // typeKind &= // ~(SimilarElementsRequestor.ANNOTATIONS | SimilarElementsRequestor.ENUMS | SimilarElementsRequestor.VARIABLES); // } int relevance = Character.isUpperCase(ASTNodes.getSimpleNameIdentifier(node).charAt(0)) ? 5 : -2; addSimilarTypeProposals(context.getDocument(), context.getASTRoot(), typeKind, node, relevance + 1, proposals); typeKind &= ~SimilarElementsRequestor.ANNOTATIONS; addNewTypeProposals(node, typeKind, relevance, proposals); // ReorgCorrectionsSubProcessor.addProjectSetupFixProposal(context, problem, node.getFullyQualifiedName(), // proposals); } if (!suggestVariableProposals) { return; } SimpleName simpleName = node.isSimpleName() ? (SimpleName)node : ((QualifiedName)node).getName(); boolean isWriteAccess = ASTResolving.isWriteAccess(node); // similar variables addSimilarVariableProposals(context.getDocument(), astRoot, binding, simpleName, isWriteAccess, proposals); if (resolvedField == null || binding == null || resolvedField.getDeclaringClass() != binding.getTypeDeclaration() && Modifier.isPrivate(resolvedField.getModifiers())) { // new fields addNewFieldProposals(context.getDocument(), astRoot, binding, declaringTypeBinding, simpleName, isWriteAccess, proposals); // new parameters and local variables if (binding == null) { addNewVariableProposals(context.getDocument(), node, simpleName, proposals); } } } private static void addNewVariableProposals(Document document, Name node, SimpleName simpleName, Collection<ICommandAccess> proposals) { String name = simpleName.getIdentifier(); BodyDeclaration bodyDeclaration = ASTResolving.findParentBodyDeclaration(node, true); int type = bodyDeclaration.getNodeType(); if (type == ASTNode.METHOD_DECLARATION) { int relevance = StubUtility.hasParameterName(name) ? 8 : 5; String label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_createparameter_description(name); Images image = Images.local_var; proposals.add(new NewVariableCorrectionProposal(label, NewVariableCorrectionProposal.PARAM, simpleName, null, relevance, document, image)); } if (type == ASTNode.INITIALIZER || type == ASTNode.METHOD_DECLARATION && !ASTResolving.isInsideConstructorInvocation((MethodDeclaration)bodyDeclaration, node)) { int relevance = StubUtility.hasLocalVariableName(name) ? 10 : 7; String label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_createlocal_description(name); Images image = Images.local_var; proposals.add(new NewVariableCorrectionProposal(label, NewVariableCorrectionProposal.LOCAL, simpleName, null, relevance, document, image)); } if (node.getParent().getNodeType() == ASTNode.ASSIGNMENT) { Assignment assignment = (Assignment)node.getParent(); if (assignment.getLeftHandSide() == node && assignment.getParent().getNodeType() == ASTNode.EXPRESSION_STATEMENT) { ASTNode statement = assignment.getParent(); ASTRewrite rewrite = ASTRewrite.create(statement.getAST()); if (ASTNodes.isControlStatementBody(assignment.getParent().getLocationInParent())) { rewrite.replace(statement, rewrite.getAST().newBlock(), null); } else { rewrite.remove(statement, null); } String label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_removestatement_description(); Images image = Images.delete_obj; ASTRewriteCorrectionProposal proposal = new ASTRewriteCorrectionProposal(label, rewrite, 4, document, image); proposals.add(proposal); } } } private static void addNewFieldProposals(Document document, CompilationUnit astRoot, ITypeBinding binding, ITypeBinding declaringTypeBinding, SimpleName simpleName, boolean isWriteAccess, Collection<ICommandAccess> proposals) { // new variables ITypeBinding senderDeclBinding; if (binding != null) { senderDeclBinding = binding.getTypeDeclaration(); //TODO load class file // targetCU= ASTResolving.findCompilationUnitForBinding(cu, astRoot, senderDeclBinding); } else { // binding is null for accesses without qualifier senderDeclBinding = declaringTypeBinding; // targetCU = cu } if (!senderDeclBinding.isFromSource() && binding != null) { return; } boolean mustBeConst = ASTResolving.isInsideModifiers(simpleName); addNewFieldForType(document, binding, senderDeclBinding, simpleName, isWriteAccess, mustBeConst, proposals); if (binding == null && senderDeclBinding.isNested()) { ASTNode anonymDecl = astRoot.findDeclaringNode(senderDeclBinding); if (anonymDecl != null) { ITypeBinding bind = Bindings.getBindingOfParentType(anonymDecl.getParent()); if (!bind.isAnonymous()) { addNewFieldForType(document, bind, bind, simpleName, isWriteAccess, mustBeConst, proposals); } } } } private static void addNewFieldForType(Document document, ITypeBinding binding, ITypeBinding senderDeclBinding, SimpleName simpleName, boolean isWriteAccess, boolean mustBeConst, Collection<ICommandAccess> proposals) { String name = simpleName.getIdentifier(); String nameLabel = name; String label; Images image; if (senderDeclBinding.isEnum() && !isWriteAccess) { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_createenum_description(nameLabel, ASTResolving.getTypeSignature( senderDeclBinding)); image = Images.field_public; proposals.add(new NewVariableCorrectionProposal(label, NewVariableCorrectionProposal.ENUM_CONST, simpleName, senderDeclBinding, 10, document, image)); } else { if (!mustBeConst) { if (binding == null) { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_createfield_description(nameLabel); image = Images.privateField; } else { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_createfield_other_description(nameLabel, ASTResolving .getTypeSignature( senderDeclBinding)); image = Images.field_public; } int fieldRelevance = StubUtility.hasFieldName(name) ? 9 : 6; proposals.add(new NewVariableCorrectionProposal(label, NewVariableCorrectionProposal.FIELD, simpleName, senderDeclBinding, fieldRelevance, document, image)); } if (!isWriteAccess && !senderDeclBinding.isAnonymous()) { if (binding == null) { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_createconst_description(nameLabel); image = Images.privateField; } else { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_createconst_other_description(nameLabel, ASTResolving .getTypeSignature( senderDeclBinding)); image = Images.field_public; } int constRelevance = StubUtility.hasConstantName(name) ? 9 : 4; proposals.add(new NewVariableCorrectionProposal(label, NewVariableCorrectionProposal.CONST_FIELD, simpleName, senderDeclBinding, constRelevance, document, image)); } } } private static void addSimilarVariableProposals(Document document, CompilationUnit astRoot, ITypeBinding binding, SimpleName node, boolean isWriteAccess, Collection<ICommandAccess> proposals) { int kind = ScopeAnalyzer.VARIABLES | ScopeAnalyzer.CHECK_VISIBILITY; if (!isWriteAccess) { kind |= ScopeAnalyzer.METHODS; // also try to find similar methods } IBinding[] varsAndMethodsInScope = (new ScopeAnalyzer(astRoot)).getDeclarationsInScope(node, kind); if (varsAndMethodsInScope.length > 0) { // avoid corrections like int i= i; String otherNameInAssign = null; // help with x.getString() -> y.getString() String methodSenderName = null; String fieldSenderName = null; ASTNode parent = node.getParent(); switch (parent.getNodeType()) { case ASTNode.VARIABLE_DECLARATION_FRAGMENT: // node must be initializer otherNameInAssign = ((VariableDeclarationFragment)parent).getName().getIdentifier(); break; case ASTNode.ASSIGNMENT: Assignment assignment = (Assignment)parent; if (isWriteAccess && assignment.getRightHandSide() instanceof SimpleName) { otherNameInAssign = ((SimpleName)assignment.getRightHandSide()).getIdentifier(); } else if (!isWriteAccess && assignment.getLeftHandSide() instanceof SimpleName) { otherNameInAssign = ((SimpleName)assignment.getLeftHandSide()).getIdentifier(); } break; case ASTNode.METHOD_INVOCATION: MethodInvocation inv = (MethodInvocation)parent; if (inv.getExpression() == node) { methodSenderName = inv.getName().getIdentifier(); } break; case ASTNode.QUALIFIED_NAME: QualifiedName qualName = (QualifiedName)parent; if (qualName.getQualifier() == node) { fieldSenderName = qualName.getName().getIdentifier(); } break; } ITypeBinding guessedType = ASTResolving.guessBindingForReference(node); ITypeBinding objectBinding = astRoot.getAST().resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$ String identifier = node.getIdentifier(); boolean isInStaticContext = ASTResolving.isInStaticContext(node); ArrayList<CUCorrectionProposal> newProposals = new ArrayList<CUCorrectionProposal>(51); loop: for (int i = 0; i < varsAndMethodsInScope.length && newProposals.size() <= 50; i++) { IBinding varOrMeth = varsAndMethodsInScope[i]; if (varOrMeth instanceof IVariableBinding) { IVariableBinding curr = (IVariableBinding)varOrMeth; String currName = curr.getName(); if (currName.equals(otherNameInAssign)) { continue loop; } boolean isFinal = Modifier.isFinal(curr.getModifiers()); if (isFinal && curr.isField() && isWriteAccess) { continue loop; } if (isInStaticContext && !Modifier.isStatic(curr.getModifiers()) && curr.isField()) { continue loop; } int relevance = 0; if (NameMatcher.isSimilarName(currName, identifier)) { relevance += 3; // variable with a similar name than the unresolved variable } if (currName.equalsIgnoreCase(identifier)) { relevance += 5; } ITypeBinding varType = curr.getType(); if (varType != null) { if (guessedType != null && guessedType != objectBinding) { // too many result with object // variable type is compatible with the guessed type if (!isWriteAccess && canAssign(varType, guessedType) || isWriteAccess && canAssign(guessedType, varType)) { relevance += 2; // unresolved variable can be assign to this variable } } if (methodSenderName != null && hasMethodWithName(varType, methodSenderName)) { relevance += 2; } if (fieldSenderName != null && hasFieldWithName(varType, fieldSenderName)) { relevance += 2; } } if (relevance > 0) { String label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_changevariable_description(currName); newProposals.add(new RenameNodeCorrectionProposal(label, astRoot, node.getStartPosition(), node .getLength(), currName, relevance, document)); } } else if (varOrMeth instanceof IMethodBinding) { IMethodBinding curr = (IMethodBinding)varOrMeth; if (!curr.isConstructor() && guessedType != null && canAssign(curr.getReturnType(), guessedType)) { if (NameMatcher.isSimilarName(curr.getName(), identifier)) { AST ast = astRoot.getAST(); ASTRewrite rewrite = ASTRewrite.create(ast); String label = CorrectionMessages.INSTANCE .UnresolvedElementsSubProcessor_changetomethod_description(ASTResolving .getMethodSignature( curr)); Images image = Images.correction_change; LinkedCorrectionProposal proposal = new LinkedCorrectionProposal(label, rewrite, 8, document, image); newProposals.add(proposal); MethodInvocation newInv = ast.newMethodInvocation(); newInv.setName(ast.newSimpleName(curr.getName())); ITypeBinding[] parameterTypes = curr.getParameterTypes(); for (int k = 0; k < parameterTypes.length; k++) { ASTNode arg = ASTNodeFactory.newDefaultExpression(ast, parameterTypes[k]); newInv.arguments().add(arg); // proposal.addLinkedPosition(rewrite.track(arg), false, null); } rewrite.replace(node, newInv, null); } } } } if (newProposals.size() <= 50) proposals.addAll(newProposals); } if (binding != null && binding.isArray()) { String idLength = "length"; //$NON-NLS-1$ String label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_changevariable_description(idLength); proposals.add(new RenameNodeCorrectionProposal(label, astRoot, node.getStartPosition(), node.getLength(), idLength, 8, document)); } } private static boolean canAssign(ITypeBinding returnType, ITypeBinding guessedType) { return returnType.isAssignmentCompatible(guessedType); } private static boolean hasMethodWithName(ITypeBinding typeBinding, String name) { IVariableBinding[] fields = typeBinding.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { if (fields[i].getName().equals(name)) { return true; } } ITypeBinding superclass = typeBinding.getSuperclass(); if (superclass != null) { return hasMethodWithName(superclass, name); } return false; } private static boolean hasFieldWithName(ITypeBinding typeBinding, String name) { IMethodBinding[] methods = typeBinding.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equals(name)) { return true; } } ITypeBinding superclass = typeBinding.getSuperclass(); if (superclass != null) { return hasMethodWithName(superclass, name); } return false; } private static int evauateTypeKind(ASTNode node) { int kind = ASTResolving.getPossibleTypeKinds(node, true); return kind; } public static void getTypeProposals(InvocationContext context, IProblemLocation problem, Collection<ICommandAccess> proposals) throws CoreException { ASTNode selectedNode = problem.getCoveringNode(context.getASTRoot()); if (selectedNode == null) { return; } int kind = evauateTypeKind(selectedNode); while (selectedNode.getLocationInParent() == QualifiedName.NAME_PROPERTY) { selectedNode = selectedNode.getParent(); } Name node = null; if (selectedNode instanceof SimpleType) { node = ((SimpleType)selectedNode).getName(); } else if (selectedNode instanceof ArrayType) { Type elementType = ((ArrayType)selectedNode).getElementType(); if (elementType.isSimpleType()) { node = ((SimpleType)elementType).getName(); } else { return; } } else if (selectedNode instanceof Name) { node = (Name)selectedNode; } else { return; } // change to similar type proposals addSimilarTypeProposals(context.getDocument(), context.getASTRoot(), kind, node, 3, proposals); while (node.getParent() instanceof QualifiedName) { node = (Name)node.getParent(); } if (selectedNode != node) { kind = evauateTypeKind(node); } if ((kind & (SimilarElementsRequestor.CLASSES | SimilarElementsRequestor.INTERFACES)) != 0) { kind &= ~SimilarElementsRequestor.ANNOTATIONS; // only propose annotations when there are no other suggestions } addNewTypeProposals(node, kind, 0, proposals); // // ReorgCorrectionsSubProcessor // .addProjectSetupFixProposal(context, problem, node.getFullyQualifiedName(), proposals); } private static void addSimilarTypeProposals(Document document, CompilationUnit compilationUnit, int kind, Name node, int relevance, Collection<ICommandAccess> proposals) throws CoreException { SimilarElement[] elements = SimilarElementsRequestor.findSimilarElement(document, compilationUnit, node, kind); // try to resolve type in context -> highest severity String resolvedTypeName = null; ITypeBinding binding = ASTResolving.guessBindingForTypeReference(node); if (binding != null) { ITypeBinding simpleBinding = binding; if (simpleBinding.isArray()) { simpleBinding = simpleBinding.getElementType(); } simpleBinding = simpleBinding.getTypeDeclaration(); if (!simpleBinding.isRecovered()) { resolvedTypeName = simpleBinding.getQualifiedName(); CUCorrectionProposal proposal = createTypeRefChangeProposal(document, resolvedTypeName, node, relevance + 2, elements.length); proposals.add(proposal); if (proposal instanceof AddImportCorrectionProposal) proposal.setRelevance(relevance + elements.length + 2); if (binding.isParameterizedType() && node.getParent() instanceof SimpleType && !(node.getParent().getParent() instanceof Type)) { proposals.add(createTypeRefChangeFullProposal(document, binding, node, relevance + 2)); } } } else { ASTNode normalizedNode = ASTNodes.getNormalizedNode(node); if (!(normalizedNode.getParent() instanceof Type) && node.getParent() != normalizedNode) { ITypeBinding normBinding = ASTResolving.guessBindingForTypeReference(normalizedNode); if (normBinding != null && !normBinding.isRecovered()) { proposals.add(createTypeRefChangeFullProposal(document, normBinding, normalizedNode, relevance + 2)); } } } // add all similar elements for (int i = 0; i < elements.length; i++) { SimilarElement elem = elements[i]; if ((elem.getKind() & SimilarElementsRequestor.ALL_TYPES) != 0) { String fullName = elem.getName(); if (!fullName.equals(resolvedTypeName)) { proposals.add(createTypeRefChangeProposal(document, fullName, node, relevance, elements.length)); } } } } private static CUCorrectionProposal createTypeRefChangeProposal(Document document, String fullName, Name node, int relevance, int maxProposals) { ImportRewrite importRewrite = null; String simpleName = fullName; String packName = Signature.getQualifier(fullName); if (packName.length() > 0) { // no imports for primitive types, type variables importRewrite = StubUtility.createImportRewrite(document, (CompilationUnit)node.getRoot(), true); ImportRewriteContext context = new ContextSensitiveImportRewriteContext(ASTResolving.findParentBodyDeclaration(node), importRewrite); simpleName = importRewrite.addImport(fullName, context); } if (!isLikelyTypeName(simpleName)) { relevance -= 2; } ASTRewriteCorrectionProposal proposal; if (importRewrite != null && node.isSimpleName() && simpleName.equals(((SimpleName)node).getIdentifier())) { // import only // import only String label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_importtype_description(simpleName, packName); Images image = Images.imp_obj; int boost = QualifiedTypeNameHistory.getBoost(fullName, 0, maxProposals); proposal = new AddImportCorrectionProposal(label, relevance + 100 + boost, document, image, packName, simpleName, (SimpleName)node); proposal.setCommandId(ADD_IMPORT_ID); } else { String label; if (packName.length() == 0) { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_changetype_nopack_description(simpleName); } else { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_changetype_description(simpleName, packName); } ASTRewrite rewrite = ASTRewrite.create(node.getAST()); rewrite.replace(node, rewrite.createStringPlaceholder(simpleName, ASTNode.SIMPLE_TYPE), null); Images image = Images.correction_change; proposal = new ASTRewriteCorrectionProposal(label, rewrite, relevance, document, image); } if (importRewrite != null) { proposal.setImportRewrite(importRewrite); } return proposal; } static CUCorrectionProposal createTypeRefChangeFullProposal(Document document, ITypeBinding binding, ASTNode node, int relevance) { ASTRewrite rewrite = ASTRewrite.create(node.getAST()); String label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_change_full_type_description(BindingLabelProvider .getBindingLabel(binding, JavaElementLabels.ALL_DEFAULT)); Images image = Images.correction_change; ASTRewriteCorrectionProposal proposal = new ASTRewriteCorrectionProposal(label, rewrite, relevance + 3, document, image); ImportRewrite imports = proposal.createImportRewrite((CompilationUnit)node.getRoot()); Type type = imports.addImport(binding, node.getAST()); rewrite.replace(node, type, null); return proposal; } private static boolean isLikelyTypeName(String name) { return name.length() > 0 && Character.isUpperCase(name.charAt(0)); } private static boolean isLikelyPackageName(String name) { if (name.length() != 0) { int i = 0; do { if (Character.isUpperCase(name.charAt(i))) { return false; } i = name.indexOf('.', i) + 1; } while (i != 0 && i < name.length()); } return true; } private static boolean isLikelyTypeParameterName(String name) { return name.length() == 1 && Character.isUpperCase(name.charAt(0)); } private static boolean isLikelyMethodTypeParameterName(String name) { if (name.length() == 1) { switch (name.charAt(0)) { case 'S': case 'T': case 'U': return true; } } return false; } public static void addNewTypeProposals(Name refNode, int kind, int relevance, Collection<ICommandAccess> proposals) { System.out.println("UnresolvedElementsSubProcessor.addNewTypeProposals()"); // TODO // Name node = refNode; // do // { // String typeName = ASTNodes.getSimpleNameIdentifier(node); // Name qualifier = null; // // only propose to create types for qualifiers when the name starts with upper case // boolean isPossibleName = isLikelyTypeName(typeName) || node == refNode; // if (isPossibleName) // { // IPackageFragment enclosingPackage = null; // IType enclosingType = null; // if (node.isSimpleName()) // { // enclosingPackage = (IPackageFragment)cu.getParent(); // // don't suggest member type, user can select it in wizard // } // else // { // Name qualifierName = ((QualifiedName)node).getQualifier(); // IBinding binding = qualifierName.resolveBinding(); // if (binding != null && binding.isRecovered()) // { // binding = null; // } // if (binding instanceof ITypeBinding) // { // enclosingType = (IType)binding.getJavaElement(); // } // else if (binding instanceof IPackageBinding) // { // qualifier = qualifierName; // enclosingPackage = (IPackageFragment)binding.getJavaElement(); // } // else // { // IJavaElement[] res = cu.codeSelect(qualifierName.getStartPosition(), qualifierName.getLength()); // if (res != null && res.length > 0 && res[0] instanceof IType) // { // enclosingType = (IType)res[0]; // } // else // { // qualifier = qualifierName; // enclosingPackage = // JavaModelUtil.getPackageFragmentRoot(cu).getPackageFragment( // ASTResolving.getFullName(qualifierName)); // } // } // } // int rel = relevance; // if (enclosingPackage != null && isLikelyPackageName(enclosingPackage.getElementName())) // { // rel += 3; // } // // if (enclosingPackage != null // && !enclosingPackage.getCompilationUnit(typeName + JavaModelUtil.DEFAULT_CU_SUFFIX).exists() // || enclosingType != null && !enclosingType.isReadOnly() && !enclosingType.getType(typeName).exists()) // { // new member type // IJavaElement enclosing = enclosingPackage != null ? (IJavaElement)enclosingPackage : enclosingType; // // if ((kind & SimilarElementsRequestor.CLASSES) != 0) // { // proposals.add(new NewCUUsingWizardProposal(cu, node, NewCUUsingWizardProposal.K_CLASS, enclosing, // rel + 3)); // } // if ((kind & SimilarElementsRequestor.INTERFACES) != 0) // { // proposals.add(new NewCUUsingWizardProposal(cu, node, NewCUUsingWizardProposal.K_INTERFACE, enclosing, // rel + 2)); // } // if ((kind & SimilarElementsRequestor.ENUMS) != 0) // { // proposals // .add(new NewCUUsingWizardProposal(cu, node, NewCUUsingWizardProposal.K_ENUM, enclosing, rel)); // } // if ((kind & SimilarElementsRequestor.ANNOTATIONS) != 0) // { // proposals.add(new NewCUUsingWizardProposal(cu, node, NewCUUsingWizardProposal.K_ANNOTATION, // enclosing, rel + 1)); // } // } // } // node = qualifier; // } // while (node != null); // // // type parameter proposals // if (refNode.isSimpleName() && (kind & SimilarElementsRequestor.VARIABLES) != 0) // { // CompilationUnit root = (CompilationUnit)refNode.getRoot(); // String name = ((SimpleName)refNode).getIdentifier(); // BodyDeclaration declaration = ASTResolving.findParentBodyDeclaration(refNode); // int baseRel = relevance; // if (isLikelyTypeParameterName(name)) // { // baseRel += 8; // } // while (declaration != null) // { // IBinding binding = null; // int rel = baseRel; // if (declaration instanceof MethodDeclaration) // { // binding = ((MethodDeclaration)declaration).resolveBinding(); // if (isLikelyMethodTypeParameterName(name)) // rel += 2; // } // else if (declaration instanceof TypeDeclaration) // { // binding = ((TypeDeclaration)declaration).resolveBinding(); // rel++; // } // if (binding != null) // { // AddTypeParameterProposal proposal = new AddTypeParameterProposal(cu, binding, root, name, null, rel); // proposals.add(proposal); // } // if (!Modifier.isStatic(declaration.getModifiers())) // { // declaration = ASTResolving.findParentBodyDeclaration(declaration.getParent()); // } // else // { // declaration = null; // } // } // } } public static void getMethodProposals(InvocationContext context, IProblemLocation problem, boolean isOnlyParameterMismatch, Collection<ICommandAccess> proposals) throws CoreException { CompilationUnit astRoot = context.getASTRoot(); ASTNode selectedNode = problem.getCoveringNode(astRoot); if (!(selectedNode instanceof SimpleName)) { return; } SimpleName nameNode = (SimpleName)selectedNode; List<Expression> arguments; Expression sender; boolean isSuperInvocation; ASTNode invocationNode = nameNode.getParent(); if (invocationNode instanceof MethodInvocation) { MethodInvocation methodImpl = (MethodInvocation)invocationNode; arguments = methodImpl.arguments(); sender = methodImpl.getExpression(); isSuperInvocation = false; } else if (invocationNode instanceof SuperMethodInvocation) { SuperMethodInvocation methodImpl = (SuperMethodInvocation)invocationNode; arguments = methodImpl.arguments(); sender = methodImpl.getQualifier(); isSuperInvocation = true; } else { return; } String methodName = nameNode.getIdentifier(); int nArguments = arguments.size(); // corrections IBinding[] bindings = (new ScopeAnalyzer(astRoot)).getDeclarationsInScope(nameNode, ScopeAnalyzer.METHODS); HashSet<String> suggestedRenames = new HashSet<String>(); for (int i = 0; i < bindings.length; i++) { IMethodBinding binding = (IMethodBinding)bindings[i]; String curr = binding.getName(); if (!curr.equals(methodName) && binding.getParameterTypes().length == nArguments && NameMatcher.isSimilarName(methodName, curr) && suggestedRenames.add(curr)) { String label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_changemethod_description(curr); proposals.add(new RenameNodeCorrectionProposal(label, context.getASTRoot(), problem.getOffset(), problem .getLength(), curr, 6, context.getDocument())); } } suggestedRenames = null; if (isOnlyParameterMismatch) { ArrayList<IMethodBinding> parameterMismatchs = new ArrayList<IMethodBinding>(); for (int i = 0; i < bindings.length; i++) { IMethodBinding binding = (IMethodBinding)bindings[i]; if (binding.getName().equals(methodName)) { parameterMismatchs.add(binding); } } addParameterMissmatchProposals(context, problem, parameterMismatchs, invocationNode, arguments, proposals); } // new method addNewMethodProposals(context.getDocument(), astRoot, sender, arguments, isSuperInvocation, invocationNode, methodName, proposals); if (!isOnlyParameterMismatch && !isSuperInvocation && sender != null) { addMissingCastParentsProposal(context.getDocument(), (MethodInvocation)invocationNode, proposals); } if (!isSuperInvocation && sender == null && invocationNode.getParent() instanceof ThrowStatement) { String str = "new "; //$NON-NLS-1$ // do it the manual way, copting all the arguments is nasty String label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_addnewkeyword_description(); int relevance = Character.isUpperCase(methodName.charAt(0)) ? 7 : 4; ReplaceCorrectionProposal proposal = new ReplaceCorrectionProposal(label, invocationNode.getStartPosition(), 0, str, relevance, context.getDocument()); proposals.add(proposal); } } private static void addNewMethodProposals(Document document, CompilationUnit astRoot, Expression sender, List<Expression> arguments, boolean isSuperInvocation, ASTNode invocationNode, String methodName, Collection<ICommandAccess> proposals) { ITypeBinding nodeParentType = Bindings.getBindingOfParentType(invocationNode); ITypeBinding binding = null; if (sender != null) { binding = sender.resolveTypeBinding(); } else { binding = nodeParentType; if (isSuperInvocation && binding != null) { binding = binding.getSuperclass(); } } if (binding != null && binding.isFromSource()) { ITypeBinding senderDeclBinding = binding.getTypeDeclaration(); //TODO load class file // ICompilationUnit targetCU = ASTResolving.findCompilationUnitForBinding(cu, astRoot, senderDeclBinding); // if (targetCU != null) // { String label; Images image; ITypeBinding[] parameterTypes = getParameterTypes(arguments); if (parameterTypes != null) { String sig = ASTResolving.getMethodSignature(methodName, parameterTypes, false); if (ASTResolving.isUseableTypeInContext(parameterTypes, senderDeclBinding, false)) { if (nodeParentType == senderDeclBinding) { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_createmethod_description(sig); image = Images.privateMethod; } else { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_createmethod_other_description(sig, senderDeclBinding .getName()); image = Images.publicMethod; } proposals.add(new NewMethodCorrectionProposal(label, invocationNode, arguments, senderDeclBinding, 5, document, image)); } if (senderDeclBinding.isNested() && sender == null && Bindings.findMethodInHierarchy(senderDeclBinding, methodName, (ITypeBinding[])null) == null) { // no covering method ASTNode anonymDecl = astRoot.findDeclaringNode(senderDeclBinding); if (anonymDecl != null) { senderDeclBinding = Bindings.getBindingOfParentType(anonymDecl.getParent()); if (!senderDeclBinding.isAnonymous() && ASTResolving.isUseableTypeInContext(parameterTypes, senderDeclBinding, false)) { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_createmethod_other_description(sig, ASTResolving .getTypeSignature( senderDeclBinding)); image = Images.protectedMethod; proposals.add(new NewMethodCorrectionProposal(label, invocationNode, arguments, senderDeclBinding, 5, document, image)); } } } } // } } } private static void addMissingCastParentsProposal(Document document, MethodInvocation invocationNode, Collection<ICommandAccess> proposals) { Expression sender = invocationNode.getExpression(); if (sender instanceof ThisExpression) { return; } ITypeBinding senderBinding = sender.resolveTypeBinding(); if (senderBinding == null || Modifier.isFinal(senderBinding.getModifiers())) { return; } if (sender instanceof Name && ((Name)sender).resolveBinding() instanceof ITypeBinding) { return; // static access } ASTNode parent = invocationNode.getParent(); while (parent instanceof Expression && parent.getNodeType() != ASTNode.CAST_EXPRESSION) { parent = parent.getParent(); } boolean hasCastProposal = false; if (parent instanceof CastExpression) { // (TestCase) x.getName() -> ((TestCase) x).getName hasCastProposal = useExistingParentCastProposal(document, (CastExpression)parent, sender, invocationNode.getName(), getArgumentTypes(invocationNode.arguments()), proposals); } if (!hasCastProposal) { // x.getName() -> ((TestCase) x).getName Expression target = sender; while (target instanceof ParenthesizedExpression) { target = ((ParenthesizedExpression)target).getExpression(); } String label; if (target.getNodeType() != ASTNode.CAST_EXPRESSION) { String targetName = null; if (target.getLength() <= 18) { targetName = ASTNodes.asString(target); } if (targetName == null) { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_methodtargetcast_description(); } else { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_methodtargetcast2_description(targetName); } } else { String targetName = null; if (target.getLength() <= 18) { targetName = ASTNodes.asString(((CastExpression)target).getExpression()); } if (targetName == null) { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_changemethodtargetcast_description(); } else { label = CorrectionMessages.INSTANCE .UnresolvedElementsSubProcessor_changemethodtargetcast2_description(targetName); } } proposals.add(new CastCorrectionProposal(label, target, (ITypeBinding)null, 3, document)); } } private static boolean useExistingParentCastProposal(Document document, CastExpression expression, Expression accessExpression, SimpleName accessSelector, ITypeBinding[] paramTypes, Collection<ICommandAccess> proposals) { ITypeBinding castType = expression.getType().resolveBinding(); if (castType == null) { return false; } if (paramTypes != null) { if (Bindings.findMethodInHierarchy(castType, accessSelector.getIdentifier(), paramTypes) == null) { return false; } } else if (Bindings.findFieldInHierarchy(castType, accessSelector.getIdentifier()) == null) { return false; } ITypeBinding bindingToCast = accessExpression.resolveTypeBinding(); if (bindingToCast != null && !bindingToCast.isCastCompatible(castType)) { return false; } IMethodBinding res = Bindings.findMethodInHierarchy(castType, accessSelector.getIdentifier(), paramTypes); if (res != null) { AST ast = expression.getAST(); ASTRewrite rewrite = ASTRewrite.create(ast); CastExpression newCast = ast.newCastExpression(); newCast.setType((Type)ASTNode.copySubtree(ast, expression.getType())); newCast.setExpression((Expression)rewrite.createCopyTarget(accessExpression)); ParenthesizedExpression parents = ast.newParenthesizedExpression(); parents.setExpression(newCast); ASTNode node = rewrite.createCopyTarget(expression.getExpression()); rewrite.replace(expression, node, null); rewrite.replace(accessExpression, parents, null); String label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_missingcastbrackets_description(); Images image = Images.correction_cast; ASTRewriteCorrectionProposal proposal = new ASTRewriteCorrectionProposal(label, rewrite, 8, document, image); proposals.add(proposal); return true; } return false; } private static void addParameterMissmatchProposals(InvocationContext context, IProblemLocation problem, List<IMethodBinding> similarElements, ASTNode invocationNode, List<Expression> arguments, Collection<ICommandAccess> proposals) throws CoreException { int nSimilarElements = similarElements.size(); ITypeBinding[] argTypes = getArgumentTypes(arguments); if (argTypes == null || nSimilarElements == 0) { return; } for (int i = 0; i < nSimilarElements; i++) { IMethodBinding elem = similarElements.get(i); int diff = elem.getParameterTypes().length - argTypes.length; if (diff == 0) { int nProposals = proposals.size(); doEqualNumberOfParameters(context, invocationNode, problem, arguments, argTypes, elem, proposals); if (nProposals != proposals.size()) { return; // only suggest for one method (avoid duplicated proposals) } } else if (diff > 0) { doMoreParameters(context, invocationNode, argTypes, elem, proposals); } else { doMoreArguments(context, invocationNode, arguments, argTypes, elem, proposals); } } } private static void doMoreParameters(InvocationContext context, ASTNode invocationNode, ITypeBinding[] argTypes, IMethodBinding methodBinding, Collection<ICommandAccess> proposals) throws CoreException { ITypeBinding[] paramTypes = methodBinding.getParameterTypes(); int k = 0, nSkipped = 0; int diff = paramTypes.length - argTypes.length; int[] indexSkipped = new int[diff]; for (int i = 0; i < paramTypes.length; i++) { if (k < argTypes.length && canAssign(argTypes[k], paramTypes[i])) { k++; // match } else { if (nSkipped >= diff) { return; // too different } indexSkipped[nSkipped++] = i; } } ITypeBinding declaringType = methodBinding.getDeclaringClass(); CompilationUnit astRoot = context.getASTRoot(); // add arguments { String arg = ASTResolving.getMethodSignature(methodBinding); String label; if (diff == 1) { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_addargument_description(arg); } else { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_addarguments_description(arg); } AddArgumentCorrectionProposal proposal = new AddArgumentCorrectionProposal(label, invocationNode, indexSkipped, paramTypes, 8, context.getDocument()); proposal.setImage(Images.add_obj); proposals.add(proposal); } // remove parameters if (!declaringType.isFromSource()) { return; } //TODO load class file // if (targetCU != null) // { // IMethodBinding methodDecl = methodBinding.getMethodDeclaration(); // ITypeBinding[] declParameterTypes = methodDecl.getParameterTypes(); // // ChangeDescription[] changeDesc = new ChangeDescription[declParameterTypes.length]; // ITypeBinding[] changedTypes = new ITypeBinding[diff]; // for (int i = diff - 1; i >= 0; i--) // { // int idx = indexSkipped[i]; // changeDesc[idx] = new RemoveDescription(); // changedTypes[i] = declParameterTypes[idx]; // } // String signature = ASTResolving.getMethodSignature(methodDecl); // String typeNames = getTypeNames(changedTypes); // String label; // if (methodDecl.isConstructor()) // { // if (diff == 1) // { // label = // CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_removeparam_constr_description(signature, // typeNames); // } // else // { // label = // CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_removeparams_constr_description(signature, // typeNames); // } // } // else // { // if (diff == 1) // { // label = // CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_removeparam_description(signature, typeNames); // } // else // { // label = // CorrectionMessages.INSTANCE // .UnresolvedElementsSubProcessor_removeparams_description(signature, typeNames); // } // } // // Image image = new Image(JavaClientBundle.INSTANCE.remove_correction()); // ChangeMethodSignatureProposal proposal = // new ChangeMethodSignatureProposal(label, invocationNode, methodDecl, changeDesc, null, 5, // context.getDocument(), image); // proposals.add(proposal); // } } private static String getTypeNames(ITypeBinding[] types) { StringBuffer buf = new StringBuffer(); for (int i = 0; i < types.length; i++) { if (i > 0) { buf.append(", "); //$NON-NLS-1$ } buf.append(ASTResolving.getTypeSignature(types[i])); } return buf.toString(); } private static String getArgumentName(List<Expression> arguments, int index) { String def = String.valueOf(index + 1); ASTNode expr = arguments.get(index); if (expr.getLength() > 18) { return def; } ASTMatcher matcher = new ASTMatcher(); for (int i = 0; i < arguments.size(); i++) { if (i != index && matcher.safeSubtreeMatch(expr, arguments.get(i))) { return def; } } return '\'' + ASTNodes.asString(expr) + '\''; } private static void doMoreArguments(InvocationContext context, ASTNode invocationNode, List<Expression> arguments, ITypeBinding[] argTypes, IMethodBinding methodRef, Collection<ICommandAccess> proposals) throws CoreException { ITypeBinding[] paramTypes = methodRef.getParameterTypes(); int k = 0, nSkipped = 0; int diff = argTypes.length - paramTypes.length; int[] indexSkipped = new int[diff]; for (int i = 0; i < argTypes.length; i++) { if (k < paramTypes.length && canAssign(argTypes[i], paramTypes[k])) { k++; // match } else { if (nSkipped >= diff) { return; // too different } indexSkipped[nSkipped++] = i; } } CompilationUnit astRoot = context.getASTRoot(); // remove arguments { ASTRewrite rewrite = ASTRewrite.create(astRoot.getAST()); for (int i = diff - 1; i >= 0; i--) { rewrite.remove(arguments.get(indexSkipped[i]), null); } String[] arg = new String[]{}; String label; if (diff == 1) { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_removeargument_description(ASTResolving .getMethodSignature( methodRef)); } else { label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_removearguments_description(ASTResolving .getMethodSignature( methodRef)); } Images image = Images.remove_correction; ASTRewriteCorrectionProposal proposal = new ASTRewriteCorrectionProposal(label, rewrite, 8, context.getDocument(), image); proposals.add(proposal); } IMethodBinding methodDecl = methodRef.getMethodDeclaration(); ITypeBinding declaringType = methodDecl.getDeclaringClass(); // add parameters if (!declaringType.isFromSource()) { return; } //TODO load class file // ICompilationUnit targetCU = ASTResolving.findCompilationUnitForBinding(cu, astRoot, declaringType); // if (targetCU != null) // { // // if (isImplicitConstructor(methodDecl)) // { // return; // } // // ChangeDescription[] changeDesc = new ChangeDescription[argTypes.length]; // ITypeBinding[] changeTypes = new ITypeBinding[diff]; // for (int i = diff - 1; i >= 0; i--) // { // int idx = indexSkipped[i]; // Expression arg = arguments.get(idx); // String name = getExpressionBaseName(arg); // ITypeBinding newType = Bindings.normalizeTypeBinding(argTypes[idx]); // if (newType == null) // { // newType = astRoot.getAST().resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$ // } // if (newType.isWildcardType()) // { // newType = ASTResolving.normalizeWildcardType(newType, true, astRoot.getAST()); // } // if (!ASTResolving.isUseableTypeInContext(newType, methodDecl, false)) // { // return; // } // changeDesc[idx] = new InsertDescription(newType, name); // changeTypes[i] = newType; // } // String[] arg = new String[]{ASTResolving.getMethodSignature(methodDecl), getTypeNames(changeTypes)}; // String label; // if (methodDecl.isConstructor()) // { // if (diff == 1) // { // label = // Messages.format( // CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_addparam_constr_description, arg); // } // else // { // label = // Messages.format( // CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_addparams_constr_description, arg); // } // } // else // { // if (diff == 1) // { // label = // Messages.format(CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_addparam_description, arg); // } // else // { // label = // Messages // .format(CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_addparams_description, arg); // } // } // Image image = JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_ADD); // ChangeMethodSignatureProposal proposal = // new ChangeMethodSignatureProposal(label, targetCU, invocationNode, methodDecl, changeDesc, null, 5, image); // proposals.add(proposal); // } } private static boolean isImplicitConstructor(IMethodBinding meth) { return meth.isDefaultConstructor(); } private static ITypeBinding[] getParameterTypes(List<Expression> args) { ITypeBinding[] params = new ITypeBinding[args.size()]; for (int i = 0; i < args.size(); i++) { Expression expr = args.get(i); ITypeBinding curr = Bindings.normalizeTypeBinding(expr.resolveTypeBinding()); if (curr != null && curr.isWildcardType()) { curr = ASTResolving.normalizeWildcardType(curr, true, expr.getAST()); } if (curr == null) { curr = expr.getAST().resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$ } params[i] = curr; } return params; } private static void doEqualNumberOfParameters(InvocationContext context, ASTNode invocationNode, IProblemLocation problem, List<Expression> arguments, ITypeBinding[] argTypes, IMethodBinding methodBinding, Collection<ICommandAccess> proposals) throws CoreException { ITypeBinding[] paramTypes = methodBinding.getParameterTypes(); int[] indexOfDiff = new int[paramTypes.length]; int nDiffs = 0; for (int n = 0; n < argTypes.length; n++) { if (!canAssign(argTypes[n], paramTypes[n])) { indexOfDiff[nDiffs++] = n; } } ITypeBinding declaringTypeDecl = methodBinding.getDeclaringClass().getTypeDeclaration(); CompilationUnit astRoot = context.getASTRoot(); ASTNode nameNode = problem.getCoveringNode(astRoot); if (nameNode == null) { return; } if (nDiffs == 0) { if (nameNode.getParent() instanceof MethodInvocation) { MethodInvocation inv = (MethodInvocation)nameNode.getParent(); if (inv.getExpression() == null) { addQualifierToOuterProposal(context, inv, methodBinding, proposals); } } return; } if (nDiffs == 1) { // one argument mismatching: try to fix int idx = indexOfDiff[0]; Expression nodeToCast = arguments.get(idx); ITypeBinding castType = paramTypes[idx]; castType = Bindings.normalizeTypeBinding(castType); if (castType.isWildcardType()) { castType = ASTResolving.normalizeWildcardType(castType, false, nodeToCast.getAST()); } if (castType != null) { ITypeBinding binding = nodeToCast.resolveTypeBinding(); ITypeBinding castFixType = null; if (binding == null || castType.isCastCompatible(binding)) { castFixType = castType; } else // if (JavaModelUtil.is50OrHigher(cu.getJavaProject())) { ITypeBinding boxUnboxedTypeBinding = TypeMismatchSubProcessor.boxUnboxPrimitives(castType, binding, nodeToCast.getAST()); if (boxUnboxedTypeBinding != castType && boxUnboxedTypeBinding.isCastCompatible(binding)) { castFixType = boxUnboxedTypeBinding; } } if (castFixType != null) { ASTRewriteCorrectionProposal proposal = TypeMismatchSubProcessor.createCastProposal(context, castFixType, nodeToCast, 6); String castTypeName = BindingLabelProvider.getBindingLabel(castFixType, JavaElementLabels.ALL_DEFAULT); String[] arg = new String[]{,}; proposal.setDisplayName(CorrectionMessages.INSTANCE .UnresolvedElementsSubProcessor_addargumentcast_description( getArgumentName(arguments, idx), castTypeName)); proposals.add(proposal); } TypeMismatchSubProcessor.addChangeSenderTypeProposals(context, nodeToCast, castType, false, 5, proposals); } } if (nDiffs == 2) { // try to swap int idx1 = indexOfDiff[0]; int idx2 = indexOfDiff[1]; boolean canSwap = canAssign(argTypes[idx1], paramTypes[idx2]) && canAssign(argTypes[idx2], paramTypes[idx1]); if (canSwap) { Expression arg1 = arguments.get(idx1); Expression arg2 = arguments.get(idx2); ASTRewrite rewrite = ASTRewrite.create(astRoot.getAST()); rewrite.replace(arg1, rewrite.createCopyTarget(arg2), null); rewrite.replace(arg2, rewrite.createCopyTarget(arg1), null); { String[] arg = new String[]{}; String label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_swaparguments_description( getArgumentName(arguments, idx1), getArgumentName(arguments, idx2)); Images image = Images.correction_change; ASTRewriteCorrectionProposal proposal = new ASTRewriteCorrectionProposal(label, rewrite, 8, context.getDocument(), image); proposals.add(proposal); } if (declaringTypeDecl.isFromSource()) { //TODO load class file // ICompilationUnit targetCU = ASTResolving.findCompilationUnitForBinding(cu, astRoot, declaringTypeDecl); // if (targetCU != null) // { // ChangeDescription[] changeDesc = new ChangeDescription[paramTypes.length]; // for (int i = 0; i < nDiffs; i++) // { // changeDesc[idx1] = new SwapDescription(idx2); // } // IMethodBinding methodDecl = methodBinding.getMethodDeclaration(); // ITypeBinding[] declParamTypes = methodDecl.getParameterTypes(); // // ITypeBinding[] swappedTypes = new ITypeBinding[]{declParamTypes[idx1], declParamTypes[idx2]}; // String[] args = new String[]{ASTResolving.getMethodSignature(methodDecl), getTypeNames(swappedTypes)}; // String label; // if (methodDecl.isConstructor()) // { // label = // Messages.format( // CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_swapparams_constr_description, // args); // } // else // { // label = // Messages.format( // CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_swapparams_description, args); // } // Image image = new Image(JavaClientBundle.INSTANCE.correction_change()); // ChangeMethodSignatureProposal proposal = // new ChangeMethodSignatureProposal(label, targetCU, invocationNode, methodDecl, changeDesc, null, // 5, image); // proposals.add(proposal); // } } return; } } if (declaringTypeDecl.isFromSource()) { //TODO load class file // ICompilationUnit targetCU = ASTResolving.findCompilationUnitForBinding(cu, astRoot, declaringTypeDecl); // if (targetCU != null) // { // ChangeDescription[] changeDesc = // createSignatureChangeDescription(indexOfDiff, nDiffs, paramTypes, arguments, argTypes); // if (changeDesc != null) // { // // IMethodBinding methodDecl = methodBinding.getMethodDeclaration(); // ITypeBinding[] declParamTypes = methodDecl.getParameterTypes(); // // ITypeBinding[] newParamTypes = new ITypeBinding[changeDesc.length]; // for (int i = 0; i < newParamTypes.length; i++) // { // newParamTypes[i] = changeDesc[i] == null ? declParamTypes[i] : ((EditDescription)changeDesc[i]).type; // } // boolean isVarArgs = // methodDecl.isVarargs() && newParamTypes.length > 0 // && newParamTypes[newParamTypes.length - 1].isArray(); // String[] args = // new String[]{ASTResolving.getMethodSignature(methodDecl), // ASTResolving.getMethodSignature(methodDecl.getName(), newParamTypes, isVarArgs)}; // String label; // if (methodDecl.isConstructor()) // { // label = // Messages // .format( // CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_changeparamsignature_constr_description, // args); // } // else // { // label = // Messages.format( // CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_changeparamsignature_description, // args); // } // Image image = new Image(JavaClientBundle.INSTANCE.correction_change()); // ChangeMethodSignatureProposal proposal = // new ChangeMethodSignatureProposal(label, targetCU, invocationNode, methodDecl, changeDesc, null, 7, // image); // proposals.add(proposal); // } // } } } private static ChangeDescription[] createSignatureChangeDescription(int[] indexOfDiff, int nDiffs, ITypeBinding[] paramTypes, List<Expression> arguments, ITypeBinding[] argTypes) { ChangeDescription[] changeDesc = new ChangeDescription[paramTypes.length]; for (int i = 0; i < nDiffs; i++) { int diffIndex = indexOfDiff[i]; Expression arg = arguments.get(diffIndex); String name = getExpressionBaseName(arg); ITypeBinding argType = argTypes[diffIndex]; if (argType.isWildcardType()) { argType = ASTResolving.normalizeWildcardType(argType, true, arg.getAST()); if (argType == null) { return null; } } changeDesc[diffIndex] = new EditDescription(argType, name); } return changeDesc; } private static String getExpressionBaseName(Expression expr) { IBinding argBinding = Bindings.resolveExpressionBinding(expr, true); if (argBinding instanceof IVariableBinding) { // IJavaProject project = null; ASTNode root = expr.getRoot(); // if (root instanceof CompilationUnit) // { // ITypeRoot typeRoot = ((CompilationUnit)root).getTypeRoot(); // if (typeRoot != null) // project = typeRoot.getJavaProject(); // } return StubUtility.getBaseName((IVariableBinding)argBinding); } if (expr instanceof SimpleName) return ((SimpleName)expr).getIdentifier(); return null; } private static ITypeBinding[] getArgumentTypes(List<Expression> arguments) { ITypeBinding[] res = new ITypeBinding[arguments.size()]; for (int i = 0; i < res.length; i++) { Expression expression = arguments.get(i); ITypeBinding curr = expression.resolveTypeBinding(); if (curr == null) { return null; } if (!curr.isNullType()) { // don't normalize null type curr = Bindings.normalizeTypeBinding(curr); if (curr == null) { curr = expression.getAST().resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$ } } res[i] = curr; } return res; } private static void addQualifierToOuterProposal(InvocationContext context, MethodInvocation invocationNode, IMethodBinding binding, Collection<ICommandAccess> proposals) { ITypeBinding declaringType = binding.getDeclaringClass(); ITypeBinding parentType = Bindings.getBindingOfParentType(invocationNode); ITypeBinding currType = parentType; boolean isInstanceMethod = !Modifier.isStatic(binding.getModifiers()); while (currType != null && !Bindings.isSuperType(declaringType, currType)) { if (isInstanceMethod && Modifier.isStatic(currType.getModifiers())) { return; } currType = currType.getDeclaringClass(); } if (currType == null || currType == parentType) { return; } ASTRewrite rewrite = ASTRewrite.create(invocationNode.getAST()); String label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_changetoouter_description(ASTResolving .getTypeSignature(currType)); Images image = Images.correction_change; ASTRewriteCorrectionProposal proposal = new ASTRewriteCorrectionProposal(label, rewrite, 8, context.getDocument(), image); ImportRewrite imports = proposal.createImportRewrite(context.getASTRoot()); ImportRewriteContext importRewriteContext = new ContextSensitiveImportRewriteContext(invocationNode, imports); AST ast = invocationNode.getAST(); String qualifier = imports.addImport(currType, importRewriteContext); Name name = ASTNodeFactory.newName(ast, qualifier); Expression newExpression; if (isInstanceMethod) { ThisExpression expr = ast.newThisExpression(); expr.setQualifier(name); newExpression = expr; } else { newExpression = name; } rewrite.set(invocationNode, MethodInvocation.EXPRESSION_PROPERTY, newExpression, null); proposals.add(proposal); } public static void getConstructorProposals(InvocationContext context, IProblemLocation problem, Collection<ICommandAccess> proposals) throws CoreException { CompilationUnit astRoot = context.getASTRoot(); ASTNode selectedNode = problem.getCoveringNode(astRoot); if (selectedNode == null) { return; } ITypeBinding targetBinding = null; List<Expression> arguments = null; IMethodBinding recursiveConstructor = null; int type = selectedNode.getNodeType(); if (type == ASTNode.CLASS_INSTANCE_CREATION) { ClassInstanceCreation creation = (ClassInstanceCreation)selectedNode; IBinding binding = creation.getType().resolveBinding(); if (binding instanceof ITypeBinding) { targetBinding = (ITypeBinding)binding; arguments = creation.arguments(); } } else if (type == ASTNode.SUPER_CONSTRUCTOR_INVOCATION) { ITypeBinding typeBinding = Bindings.getBindingOfParentType(selectedNode); if (typeBinding != null && !typeBinding.isAnonymous()) { targetBinding = typeBinding.getSuperclass(); arguments = ((SuperConstructorInvocation)selectedNode).arguments(); } } else if (type == ASTNode.CONSTRUCTOR_INVOCATION) { ITypeBinding typeBinding = Bindings.getBindingOfParentType(selectedNode); if (typeBinding != null && !typeBinding.isAnonymous()) { targetBinding = typeBinding; arguments = ((ConstructorInvocation)selectedNode).arguments(); recursiveConstructor = ASTResolving.findParentMethodDeclaration(selectedNode).resolveBinding(); } } if (targetBinding == null) { return; } IMethodBinding[] methods = targetBinding.getDeclaredMethods(); ArrayList<IMethodBinding> similarElements = new ArrayList<IMethodBinding>(); for (int i = 0; i < methods.length; i++) { IMethodBinding curr = methods[i]; if (curr.isConstructor() && recursiveConstructor != curr) { similarElements.add(curr); // similar elements can contain a implicit default constructor } } addParameterMissmatchProposals(context, problem, similarElements, selectedNode, arguments, proposals); if (targetBinding.isFromSource()) { ITypeBinding targetDecl = targetBinding.getTypeDeclaration(); //TODO load class file // ICompilationUnit targetCU = ASTResolving.findCompilationUnitForBinding(cu, astRoot, targetDecl); // if (targetCU != null) // { // String[] args = // new String[]{ASTResolving.getMethodSignature(ASTResolving.getTypeSignature(targetDecl), // getParameterTypes(arguments), false)}; // String label = // Messages.format(CorrectionMessages.UnresolvedElementsSubProcessor_createconstructor_description, args); // Image image = // JavaElementImageProvider.getDecoratedImage(JavaPluginImages.DESC_MISC_PUBLIC, // JavaElementImageDescriptor.CONSTRUCTOR, JavaElementImageProvider.SMALL_SIZE); // proposals.add(new NewMethodCorrectionProposal(label, targetCU, selectedNode, arguments, targetDecl, 5, // image)); // } } } public static void getAmbiguosTypeReferenceProposals(InvocationContext context, IProblemLocation problem, Collection<ICommandAccess> proposals) throws CoreException { //TODO System.out.println("UnresolvedElementsSubProcessor.getAmbiguosTypeReferenceProposals()"); // int offset = problem.getOffset(); // int len = problem.getLength(); // // IJavaElement[] elements = cu.codeSelect(offset, len); // for (int i = 0; i < elements.length; i++) // { // IJavaElement curr = elements[i]; // if (curr instanceof IType && !TypeFilter.isFiltered((IType)curr)) // { // String qualifiedTypeName = ((IType)curr).getFullyQualifiedName('.'); // // CompilationUnit root = context.getASTRoot(); // // String label = // Messages.format(CorrectionMessages.UnresolvedElementsSubProcessor_importexplicit_description, // BasicElementLabels.getJavaElementName(qualifiedTypeName)); // Image image = JavaPluginImages.get(JavaPluginImages.IMG_OBJS_IMPDECL); // ASTRewriteCorrectionProposal proposal = // new ASTRewriteCorrectionProposal(label, cu, ASTRewrite.create(root.getAST()), 5, image); // // ImportRewrite imports = proposal.createImportRewrite(root); // imports.addImport(qualifiedTypeName); // // proposals.add(proposal); // } // } } public static void getArrayAccessProposals(InvocationContext context, IProblemLocation problem, Collection<ICommandAccess> proposals) { CompilationUnit root = context.getASTRoot(); ASTNode selectedNode = problem.getCoveringNode(root); if (!(selectedNode instanceof MethodInvocation)) { return; } MethodInvocation decl = (MethodInvocation)selectedNode; SimpleName nameNode = decl.getName(); String methodName = nameNode.getIdentifier(); IBinding[] bindings = (new ScopeAnalyzer(root)).getDeclarationsInScope(nameNode, ScopeAnalyzer.METHODS); for (int i = 0; i < bindings.length; i++) { String currName = bindings[i].getName(); if (NameMatcher.isSimilarName(methodName, currName)) { String label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_arraychangetomethod_description(currName); proposals.add(new RenameNodeCorrectionProposal(label, context.getASTRoot(), nameNode.getStartPosition(), nameNode.getLength(), currName, 6, context.getDocument())); } } // always suggest 'length' String lengthId = "length"; //$NON-NLS-1$ String label = CorrectionMessages.INSTANCE.UnresolvedElementsSubProcessor_arraychangetolength_description(); int offset = nameNode.getStartPosition(); int length = decl.getStartPosition() + decl.getLength() - offset; proposals.add(new RenameNodeCorrectionProposal(label, context.getASTRoot(), offset, length, lengthId, 7, context .getDocument())); } public static void getAnnotationMemberProposals(InvocationContext context, IProblemLocation problem, Collection<ICommandAccess> proposals) throws CoreException { CompilationUnit astRoot = context.getASTRoot(); ASTNode selectedNode = problem.getCoveringNode(astRoot); Annotation annotation; String memberName; if (selectedNode.getLocationInParent() == MemberValuePair.NAME_PROPERTY) { if (selectedNode.getParent().getLocationInParent() != NormalAnnotation.VALUES_PROPERTY) { return; } annotation = (Annotation)selectedNode.getParent().getParent(); memberName = ((SimpleName)selectedNode).getIdentifier(); } else if (selectedNode.getLocationInParent() == SingleMemberAnnotation.VALUE_PROPERTY) { annotation = (Annotation)selectedNode.getParent(); memberName = "value"; //$NON-NLS-1$ } else { return; } ITypeBinding annotBinding = annotation.resolveTypeBinding(); if (annotBinding == null) { return; } if (annotation instanceof NormalAnnotation) { // similar names IMethodBinding[] otherMembers = annotBinding.getDeclaredMethods(); for (int i = 0; i < otherMembers.length; i++) { IMethodBinding binding = otherMembers[i]; String curr = binding.getName(); int relevance = NameMatcher.isSimilarName(memberName, curr) ? 6 : 3; String label = CorrectionMessages.INSTANCE .UnresolvedElementsSubProcessor_UnresolvedElementsSubProcessor_changetoattribute_description( curr); proposals.add(new RenameNodeCorrectionProposal(label, context.getASTRoot(), problem.getOffset(), problem .getLength(), curr, relevance, context.getDocument())); } } if (annotBinding.isFromSource()) { //TODO load class file // ICompilationUnit targetCU = ASTResolving.findCompilationUnitForBinding(cu, astRoot, annotBinding); // if (targetCU != null) // { // String label = // Messages // .format( // CorrectionMessages.UnresolvedElementsSubProcessor_UnresolvedElementsSubProcessor_createattribute_description, // BasicElementLabels.getJavaElementName(memberName)); // Image image = JavaPluginImages.get(JavaPluginImages.IMG_MISC_PUBLIC); // proposals.add(new NewAnnotationMemberProposal(label, targetCU, selectedNode, annotBinding, 5, image)); // } } } }