expand static import to class qualification (IDEA-68853)

This commit is contained in:
anna
2011-08-19 16:14:36 +02:00
parent 3f9b5874c2
commit 6aba03f21c
12 changed files with 265 additions and 1 deletions

View File

@@ -0,0 +1,163 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.intention.impl;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.CodeInsightUtilBase;
import com.intellij.codeInsight.highlighting.HighlightManager;
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.sun.org.apache.xalan.internal.xsltc.compiler.util.StringStack;
import gnu.trove.TIntArrayList;
import gnu.trove.TIntProcedure;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ExpandStaticImportAction extends PsiElementBaseIntentionAction {
private static final Logger LOG = Logger.getInstance("#" + ExpandStaticImportAction.class.getName());
private static final String REPLACE_THIS_OCCURRENCE = "Replace this occurrence and keep the method";
private static final String REPLACE_ALL_AND_DELETE_IMPORT = "Replace all and delete the import";
@NotNull
public String getFamilyName() {
return "Expand Static Import";
}
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
if (!PsiUtil.isLanguageLevel5OrHigher(element)) return false;
final PsiElement parent = element.getParent();
if (!(element instanceof PsiIdentifier) || !(parent instanceof PsiJavaCodeReferenceElement)) {
return false;
}
final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)parent;
final PsiElement resolveScope = referenceElement.advancedResolve(true).getCurrentFileResolveScope();
if (resolveScope instanceof PsiImportStaticStatement) {
final PsiClass targetClass = ((PsiImportStaticStatement)resolveScope).resolveTargetClass();
if (targetClass == null) return false;
setText("Expand static import to " + targetClass.getName() + "." + referenceElement.getReferenceName());
return true;
}
return false;
}
public void invoke(final Project project, final PsiFile file, final Editor editor, PsiElement element) {
if (!CodeInsightUtilBase.prepareFileForWrite(file)) return;
final PsiJavaCodeReferenceElement refExpr = (PsiJavaCodeReferenceElement)element.getParent();
final PsiImportStaticStatement staticImport = (PsiImportStaticStatement)refExpr.advancedResolve(true).getCurrentFileResolveScope();
final List<PsiJavaCodeReferenceElement> expressionToExpand = new ArrayList<PsiJavaCodeReferenceElement>();
file.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitReferenceElement(PsiJavaCodeReferenceElement expression) {
if (refExpr != expression) {
final PsiElement resolveScope = expression.advancedResolve(true).getCurrentFileResolveScope();
if (resolveScope == staticImport) {
expressionToExpand.add(expression);
}
}
super.visitElement(expression);
}
});
if (expressionToExpand.isEmpty()) {
expand(refExpr, staticImport);
staticImport.delete();
}
else {
if (ApplicationManager.getApplication().isUnitTestMode()) {
replaceAllAndDeleteImport(expressionToExpand, refExpr, staticImport);
}
else {
final BaseListPopupStep<String> step =
new BaseListPopupStep<String>("Multiple Similar Calls Found",
new String[]{REPLACE_THIS_OCCURRENCE, REPLACE_ALL_AND_DELETE_IMPORT}) {
@Override
public PopupStep onChosen(final String selectedValue, boolean finalChoice) {
new WriteCommandAction(project, ExpandStaticImportAction.this.getText()) {
@Override
protected void run(Result result) throws Throwable {
if (selectedValue == REPLACE_THIS_OCCURRENCE) {
expand(refExpr, staticImport);
}
else {
replaceAllAndDeleteImport(expressionToExpand, refExpr, staticImport);
}
}
}.execute();
return FINAL_CHOICE;
}
};
JBPopupFactory.getInstance().createListPopup(step).showInBestPositionFor(editor);
}
}
}
private static void replaceAllAndDeleteImport(List<PsiJavaCodeReferenceElement> expressionToExpand,
PsiJavaCodeReferenceElement refExpr,
PsiImportStaticStatement staticImport) {
expressionToExpand.add(refExpr);
Collections.sort(expressionToExpand, new Comparator<PsiJavaCodeReferenceElement>() {
@Override
public int compare(PsiJavaCodeReferenceElement o1, PsiJavaCodeReferenceElement o2) {
return o2.getTextOffset() - o1.getTextOffset();
}
});
for (PsiJavaCodeReferenceElement expression : expressionToExpand) {
expand(expression, staticImport);
}
staticImport.delete();
}
private static void expand(PsiJavaCodeReferenceElement refExpr, PsiImportStaticStatement staticImport) {
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(refExpr.getProject());
final PsiReferenceExpression referenceExpression = elementFactory.createReferenceExpression(staticImport.resolveTargetClass());
if (refExpr instanceof PsiReferenceExpression) {
((PsiReferenceExpression)refExpr).setQualifierExpression(referenceExpression);
}
else {
refExpr.replace(elementFactory.createReferenceFromText(referenceExpression.getText() + "." + refExpr.getText(), refExpr));
}
}
public void invoke(@NotNull final Project project, final Editor editor, PsiFile file) throws IncorrectOperationException {
PsiElement element = file.findElementAt(editor.getCaretModel().getOffset());
invoke(project, file, editor, element);
}
}

View File

@@ -0,0 +1,9 @@
// "Expand static import to Arrays.sort" "true"
import java.util.Arrays;
class Test {
public void sendMessage(String... destinationAddressNames) {
Arrays.sort(destinationAddressNames);
Arrays.asList(destinationAddressNames)
}
}

View File

@@ -0,0 +1,8 @@
// "Expand static import to Arrays.sort" "true"
import java.util.Arrays;
class Test {
public void sendMessage(String... destinationAddressNames) {
Arrays.sort(destinationAddressNames);
}
}

View File

@@ -0,0 +1,8 @@
// "Expand static import to Arrays.sort" "true"
import java.util.Arrays;
class Test {
public void sendMessage(String... destinationAddressNames) {
Arrays.sort(destinationAddressNames);
}
}

View File

@@ -0,0 +1,9 @@
// "Expand static import to Arrays.sort" "true"
import static java.util.Arrays.*;
class Test {
public void sendMessage(String... destinationAddressNames) {
s<caret>ort(destinationAddressNames);
asList(destinationAddressNames)
}
}

View File

@@ -0,0 +1,8 @@
// "Expand static import to Arrays.sort" "true"
import static java.util.Arrays.sort;
class Test {
public void sendMessage(String... destinationAddressNames) {
s<caret>ort(destinationAddressNames);
}
}

View File

@@ -0,0 +1,8 @@
// "Expand static import to Arrays.sort" "true"
import static java.util.Arrays.*;
class Test {
public void sendMessage(String... destinationAddressNames) {
s<caret>ort(destinationAddressNames);
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.intention;
import com.intellij.codeInsight.daemon.LightIntentionActionTestCase;
public class ExpandStaticImportActionTest extends LightIntentionActionTestCase {
public void test() throws Exception { doAllTests(); }
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/expandStaticImport";
}
}

View File

@@ -0,0 +1,7 @@
import java.util.Arrays;
class Foo {
public void foo(String... args) {
<spot>Arrays.sort</spot>(args);
}
}

View File

@@ -0,0 +1,7 @@
import static java.util.Arrays.sort;
class Foo {
public void foo(String... args) {
<spot>sort</spot>(args);
}
}

View File

@@ -0,0 +1,6 @@
<html>
<body>
<span style="font-family: verdana,serif; font-size: smaller;">This intention expands statically imported method call to explicit class qualification.
When multiple calls use corresponding static import, it is possible to replace them all or to change current one only.</span>
</body>
</html>

View File

@@ -438,7 +438,10 @@
<className>com.intellij.codeInsight.intention.impl.AddSingleMemberStaticImportAction</className>
<category>Imports</category>
</intentionAction>
<intentionAction>
<className>com.intellij.codeInsight.intention.impl.ExpandStaticImportAction</className>
<category>Imports</category>
</intentionAction>
<intentionAction>
<className>com.intellij.codeInspection.actions.UnimplementInterfaceAction</className>