/******************************************************************************* * 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.text.correction.proposals; import org.eclipse.che.ide.ext.java.jdt.Images; 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.CompilationUnit; 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.Javadoc; import org.eclipse.che.ide.ext.java.jdt.core.dom.MethodDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.Name; import org.eclipse.che.ide.ext.java.jdt.core.dom.SimpleName; import org.eclipse.che.ide.ext.java.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.TagElement; import org.eclipse.che.ide.ext.java.jdt.core.dom.TextElement; import org.eclipse.che.ide.ext.java.jdt.core.dom.Type; 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.core.dom.rewrite.ListRewrite; 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.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.LinkedNodeFinder; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.ScopeAnalyzer; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.JavadocTagsSubProcessor; import org.eclipse.che.ide.ext.java.jdt.text.Document; import org.eclipse.che.ide.runtime.Assert; import org.eclipse.che.ide.runtime.CoreException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; public class ChangeMethodSignatureProposal extends LinkedCorrectionProposal { public static interface ChangeDescription { } public static class SwapDescription implements ChangeDescription { final int index; public SwapDescription(int index) { this.index = index; } } public static class RemoveDescription implements ChangeDescription { } static class ModifyDescription implements ChangeDescription { public final String name; public final ITypeBinding type; Type resultingParamType; SimpleName[] resultingParamName; SimpleName resultingTagArg; private ModifyDescription(ITypeBinding type, String name) { this.type = type; this.name = name; } } public static class EditDescription extends ModifyDescription { String orginalName; public EditDescription(ITypeBinding type, String name) { super(type, name); } } public static class InsertDescription extends ModifyDescription { public InsertDescription(ITypeBinding type, String name) { super(type, name); } } private ASTNode fInvocationNode; private IMethodBinding fSenderBinding; private ChangeDescription[] fParameterChanges; private ChangeDescription[] fExceptionChanges; public ChangeMethodSignatureProposal(String label, ASTNode invocationNode, IMethodBinding binding, ChangeDescription[] paramChanges, ChangeDescription[] exceptionChanges, int relevance, Document document, Images image) { super(label, null, relevance, document, image); Assert.isTrue(binding != null && Bindings.isDeclarationBinding(binding)); fInvocationNode = invocationNode; fSenderBinding = binding; fParameterChanges = paramChanges; fExceptionChanges = exceptionChanges; } @Override protected ASTRewrite getRewrite() throws CoreException { CompilationUnit astRoot = (CompilationUnit)fInvocationNode.getRoot(); ASTNode methodDecl = astRoot.findDeclaringNode(fSenderBinding); ASTNode newMethodDecl = null; if (methodDecl != null) { newMethodDecl = methodDecl; } else { astRoot = ASTResolving.createQuickFixAST(document); newMethodDecl = astRoot.findDeclaringNode(fSenderBinding.getKey()); } createImportRewrite(astRoot); if (newMethodDecl instanceof MethodDeclaration) { MethodDeclaration decl = (MethodDeclaration)newMethodDecl; ASTRewrite rewrite = ASTRewrite.create(astRoot.getAST()); if (fParameterChanges != null) { modifyParameters(rewrite, decl); } if (fExceptionChanges != null) { modifyExceptions(rewrite, decl); } return rewrite; } return null; } private void modifyParameters(ASTRewrite rewrite, MethodDeclaration methodDecl) { AST ast = methodDecl.getAST(); ArrayList<String> usedNames = new ArrayList<String>(); boolean hasCreatedVariables = false; IVariableBinding[] declaredFields = fSenderBinding.getDeclaringClass().getDeclaredFields(); for (int i = 0; i < declaredFields.length; i++) { // avoid to take parameter names that are equal to field names usedNames.add(declaredFields[i].getName()); } ImportRewrite imports = getImportRewrite(); ImportRewriteContext context = new ContextSensitiveImportRewriteContext(methodDecl, imports); ListRewrite listRewrite = rewrite.getListRewrite(methodDecl, MethodDeclaration.PARAMETERS_PROPERTY); List<SingleVariableDeclaration> parameters = methodDecl.parameters(); // old parameters int k = 0; // index over the oldParameters for (int i = 0; i < fParameterChanges.length; i++) { ChangeDescription curr = fParameterChanges[i]; if (curr == null) { SingleVariableDeclaration oldParam = parameters.get(k); usedNames.add(oldParam.getName().getIdentifier()); k++; } else if (curr instanceof InsertDescription) { InsertDescription desc = (InsertDescription)curr; SingleVariableDeclaration newNode = ast.newSingleVariableDeclaration(); newNode.setType(imports.addImport(desc.type, ast, context)); newNode.setName(ast.newSimpleName("x")); //$NON-NLS-1$ // remember to set name later desc.resultingParamName = new SimpleName[]{newNode.getName()}; desc.resultingParamType = newNode.getType(); hasCreatedVariables = true; listRewrite.insertAt(newNode, i, null); Javadoc javadoc = methodDecl.getJavadoc(); if (javadoc != null) { TagElement newTagElement = ast.newTagElement(); newTagElement.setTagName(TagElement.TAG_PARAM); SimpleName arg = ast.newSimpleName("x"); //$NON-NLS-1$ newTagElement.fragments().add(arg); insertTabStop(rewrite, newTagElement.fragments(), "param_tagcomment" + i); //$NON-NLS-1$ insertParamTag(rewrite.getListRewrite(javadoc, Javadoc.TAGS_PROPERTY), parameters, k, newTagElement); desc.resultingTagArg = arg; // set the name later } else { desc.resultingTagArg = null; } } else if (curr instanceof RemoveDescription) { SingleVariableDeclaration decl = parameters.get(k); listRewrite.remove(decl, null); k++; TagElement tagNode = findParamTag(methodDecl, decl); if (tagNode != null) { rewrite.remove(tagNode, null); } } else if (curr instanceof EditDescription) { EditDescription desc = (EditDescription)curr; ITypeBinding newTypeBinding = desc.type; SingleVariableDeclaration decl = parameters.get(k); if (k == parameters.size() - 1 && i == fParameterChanges.length - 1 && decl.isVarargs() && newTypeBinding.isArray()) { newTypeBinding = newTypeBinding.getElementType(); // stick with varargs if it was before } else { rewrite.set(decl, SingleVariableDeclaration.VARARGS_PROPERTY, Boolean.FALSE, null); } Type newType = imports.addImport(newTypeBinding, ast, context); rewrite.replace(decl.getType(), newType, null); rewrite.set(decl, SingleVariableDeclaration.EXTRA_DIMENSIONS_PROPERTY, new Integer(0), null); IBinding binding = decl.getName().resolveBinding(); if (binding != null) { SimpleName[] names = LinkedNodeFinder.findByBinding(decl.getRoot(), binding); SimpleName[] newNames = new SimpleName[names.length]; for (int j = 0; j < names.length; j++) { SimpleName newName = ast.newSimpleName("x"); //$NON-NLS-1$ // name will be set later newNames[j] = newName; rewrite.replace(names[j], newName, null); } desc.resultingParamName = newNames; } else { SimpleName newName = ast.newSimpleName("x"); //$NON-NLS-1$ // name will be set later rewrite.replace(decl.getName(), newName, null); // remember to set name later desc.resultingParamName = new SimpleName[]{newName}; } desc.resultingParamType = newType; desc.orginalName = decl.getName().getIdentifier(); hasCreatedVariables = true; k++; TagElement tagNode = findParamTag(methodDecl, decl); if (tagNode != null) { List<? extends ASTNode> fragments = tagNode.fragments(); if (!fragments.isEmpty()) { SimpleName arg = ast.newSimpleName("x"); //$NON-NLS-1$ rewrite.replace(fragments.get(0), arg, null); desc.resultingTagArg = arg; } } } else if (curr instanceof SwapDescription) { SingleVariableDeclaration decl1 = parameters.get(k); SingleVariableDeclaration decl2 = parameters.get(((SwapDescription)curr).index); rewrite.replace(decl1, rewrite.createCopyTarget(decl2), null); rewrite.replace(decl2, rewrite.createCopyTarget(decl1), null); usedNames.add(decl1.getName().getIdentifier()); k++; TagElement tagNode1 = findParamTag(methodDecl, decl1); TagElement tagNode2 = findParamTag(methodDecl, decl2); if (tagNode1 != null && tagNode2 != null) { rewrite.replace(tagNode1, rewrite.createCopyTarget(tagNode2), null); rewrite.replace(tagNode2, rewrite.createCopyTarget(tagNode1), null); } } } if (!hasCreatedVariables) { return; } if (methodDecl.getBody() != null) { // avoid take a name of a local variable inside CompilationUnit root = (CompilationUnit)methodDecl.getRoot(); IBinding[] bindings = (new ScopeAnalyzer(root)).getDeclarationsAfter(methodDecl.getBody().getStartPosition(), ScopeAnalyzer.VARIABLES); for (int i = 0; i < bindings.length; i++) { usedNames.add(bindings[i].getName()); } } fixupNames(rewrite, usedNames); } private void fixupNames(ASTRewrite rewrite, ArrayList<String> usedNames) { AST ast = rewrite.getAST(); // set names for new parameters for (int i = 0; i < fParameterChanges.length; i++) { ChangeDescription curr = fParameterChanges[i]; if (curr instanceof ModifyDescription) { ModifyDescription desc = (ModifyDescription)curr; String typeKey = getParamTypeGroupId(i); String nameKey = getParamNameGroupId(i); // collect name suggestions String favourite = null; String[] excludedNames = usedNames.toArray(new String[usedNames.size()]); String suggestedName = desc.name; if (suggestedName != null) { favourite = StubUtility.suggestArgumentName(suggestedName, excludedNames); // addLinkedPositionProposal(nameKey, favourite, null); } // if (desc instanceof EditDescription) // { // addLinkedPositionProposal(nameKey, ((EditDescription)desc).orginalName, null); // } Type type = desc.resultingParamType; String[] suggestedNames = StubUtility.getArgumentNameSuggestions(type, excludedNames); // for (int k = 0; k < suggestedNames.length; k++) // { // addLinkedPositionProposal(nameKey, suggestedNames[k], null); // } if (favourite == null) { favourite = suggestedNames[0]; } usedNames.add(favourite); // SimpleName[] names = desc.resultingParamName; // for (int j = 0; j < names.length; j++) // { // names[j].setIdentifier(favourite); // addLinkedPosition(rewrite.track(names[j]), false, nameKey); // } // addLinkedPosition(rewrite.track(desc.resultingParamType), true, typeKey); // collect type suggestions // ITypeBinding[] bindings = ASTResolving.getRelaxingTypes(ast, desc.type); // for (int k = 0; k < bindings.length; k++) // { // addLinkedPositionProposal(typeKey, bindings[k]); // } // SimpleName tagArg = desc.resultingTagArg; // if (tagArg != null) // { // tagArg.setIdentifier(favourite); // addLinkedPosition(rewrite.track(tagArg), false, nameKey); // } } } } private TagElement findParamTag(MethodDeclaration decl, SingleVariableDeclaration param) { Javadoc javadoc = decl.getJavadoc(); if (javadoc != null) { return JavadocTagsSubProcessor.findParamTag(javadoc, param.getName().getIdentifier()); } return null; } private TagElement insertParamTag(ListRewrite tagRewriter, List<SingleVariableDeclaration> parameters, int currentIndex, TagElement newTagElement) { HashSet<String> previousNames = new HashSet<String>(); for (int n = 0; n < currentIndex; n++) { SingleVariableDeclaration var = parameters.get(n); previousNames.add(var.getName().getIdentifier()); } JavadocTagsSubProcessor.insertTag(tagRewriter, newTagElement, previousNames); return newTagElement; } private void modifyExceptions(ASTRewrite rewrite, MethodDeclaration methodDecl) { AST ast = methodDecl.getAST(); ImportRewrite imports = getImportRewrite(); ImportRewriteContext context = new ContextSensitiveImportRewriteContext(methodDecl, imports); ListRewrite listRewrite = rewrite.getListRewrite(methodDecl, MethodDeclaration.THROWN_EXCEPTIONS_PROPERTY); List<Name> exceptions = methodDecl.thrownExceptions(); // old exceptions int k = 0; // index over the old exceptions for (int i = 0; i < fExceptionChanges.length; i++) { ChangeDescription curr = fExceptionChanges[i]; if (curr == null) { k++; } else if (curr instanceof InsertDescription) { InsertDescription desc = (InsertDescription)curr; String type = imports.addImport(desc.type, context); ASTNode newNode = ASTNodeFactory.newName(ast, type); listRewrite.insertAt(newNode, i, null); String key = getExceptionTypeGroupId(i); // addLinkedPosition(rewrite.track(newNode), false, key); Javadoc javadoc = methodDecl.getJavadoc(); if (javadoc != null && JavadocTagsSubProcessor.findThrowsTag(javadoc, type) == null) { TagElement newTagElement = ast.newTagElement(); newTagElement.setTagName(TagElement.TAG_THROWS); ASTNode newRef = ASTNodeFactory.newName(ast, type); newTagElement.fragments().add(newRef); insertTabStop(rewrite, newTagElement.fragments(), "throws_tagcomment" + i); //$NON-NLS-1$ insertThrowsTag(rewrite.getListRewrite(javadoc, Javadoc.TAGS_PROPERTY), exceptions, k, newTagElement); // addLinkedPosition(rewrite.track(newRef), false, key); } } else if (curr instanceof RemoveDescription) { Name node = exceptions.get(k); listRewrite.remove(node, null); k++; TagElement tagNode = findThrowsTag(methodDecl, node); if (tagNode != null) { rewrite.remove(tagNode, null); } } else if (curr instanceof EditDescription) { EditDescription desc = (EditDescription)curr; Name oldNode = exceptions.get(k); String type = imports.addImport(desc.type, context); ASTNode newNode = ASTNodeFactory.newName(ast, type); listRewrite.replace(oldNode, newNode, null); String key = getExceptionTypeGroupId(i); // addLinkedPosition(rewrite.track(newNode), false, key); k++; TagElement tagNode = findThrowsTag(methodDecl, oldNode); if (tagNode != null) { ASTNode newRef = ASTNodeFactory.newName(ast, type); rewrite.replace((ASTNode)tagNode.fragments().get(0), newRef, null); // addLinkedPosition(rewrite.track(newRef), false, key); } } else if (curr instanceof SwapDescription) { Name decl1 = exceptions.get(k); Name decl2 = exceptions.get(((SwapDescription)curr).index); rewrite.replace(decl1, rewrite.createCopyTarget(decl2), null); rewrite.replace(decl2, rewrite.createCopyTarget(decl1), null); k++; TagElement tagNode1 = findThrowsTag(methodDecl, decl1); TagElement tagNode2 = findThrowsTag(methodDecl, decl2); if (tagNode1 != null && tagNode2 != null) { rewrite.replace(tagNode1, rewrite.createCopyTarget(tagNode2), null); rewrite.replace(tagNode2, rewrite.createCopyTarget(tagNode1), null); } } } } private void insertTabStop(ASTRewrite rewriter, List<ASTNode> fragments, String linkedName) { TextElement textElement = rewriter.getAST().newTextElement(); textElement.setText(""); //$NON-NLS-1$ fragments.add(textElement); // addLinkedPosition(rewriter.track(textElement), false, linkedName); } private TagElement findThrowsTag(MethodDeclaration decl, Name exception) { Javadoc javadoc = decl.getJavadoc(); if (javadoc != null) { String name = ASTNodes.getSimpleNameIdentifier(exception); return JavadocTagsSubProcessor.findThrowsTag(javadoc, name); } return null; } private TagElement insertThrowsTag(ListRewrite tagRewriter, List<Name> exceptions, int currentIndex, TagElement newTagElement) { HashSet<String> previousNames = new HashSet<String>(); for (int n = 0; n < currentIndex; n++) { Name curr = exceptions.get(n); previousNames.add(ASTNodes.getSimpleNameIdentifier(curr)); } JavadocTagsSubProcessor.insertTag(tagRewriter, newTagElement, previousNames); return newTagElement; } public String getParamNameGroupId(int idx) { return "param_name_" + idx; //$NON-NLS-1$ } public String getParamTypeGroupId(int idx) { return "param_type_" + idx; //$NON-NLS-1$ } public String getExceptionTypeGroupId(int idx) { return "exc_type_" + idx; //$NON-NLS-1$ } }