From 941142b346b9e5c1eaaee335d1afc3b5a39bae7f Mon Sep 17 00:00:00 2001 From: anna Date: Tue, 1 Jun 2010 18:30:49 +0400 Subject: [PATCH] introduce field: extract error expression on class level ( IDEA-41570 ) --- .../intellij/codeInsight/CodeInsightUtil.java | 1 + .../BaseExpressionToFieldHandler.java | 15 ++++++- .../introduceField/ElementToWorkOn.java | 1 + .../IntroduceVariableBase.java | 41 +++++++++++++------ .../refactoring/util/RefactoringUtil.java | 2 + .../afterOnClassLevelBinary.java | 3 ++ .../afterOnClassLevelClassForName.java | 7 ++++ .../afterOnClassLevelDuplicates.java | 5 +++ .../afterOnClassLevelDuplicates1.java | 4 ++ .../afterOnClassLevelNewExpression.java | 7 ++++ .../afterOnClassLevelNoDuplicates.java | 3 ++ .../beforeOnClassLevelBinary.java | 3 ++ .../beforeOnClassLevelClassForName.java | 7 ++++ .../beforeOnClassLevelDuplicates.java | 4 ++ .../beforeOnClassLevelDuplicates1.java | 4 ++ .../beforeOnClassLevelNewExpression.java | 7 ++++ .../beforeOnClassLevelNoDuplicates.java | 3 ++ .../IntroduceFieldInSameClassTest.java | 37 +++++++++++++++++ 18 files changed, 140 insertions(+), 14 deletions(-) create mode 100644 java/java-tests/testData/refactoring/introduceField/afterOnClassLevelBinary.java create mode 100644 java/java-tests/testData/refactoring/introduceField/afterOnClassLevelClassForName.java create mode 100644 java/java-tests/testData/refactoring/introduceField/afterOnClassLevelDuplicates.java create mode 100644 java/java-tests/testData/refactoring/introduceField/afterOnClassLevelDuplicates1.java create mode 100644 java/java-tests/testData/refactoring/introduceField/afterOnClassLevelNewExpression.java create mode 100644 java/java-tests/testData/refactoring/introduceField/afterOnClassLevelNoDuplicates.java create mode 100644 java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelBinary.java create mode 100644 java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelClassForName.java create mode 100644 java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelDuplicates.java create mode 100644 java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelDuplicates1.java create mode 100644 java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelNewExpression.java create mode 100644 java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelNoDuplicates.java diff --git a/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java b/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java index e9a2f6502f15..05f038b3b55a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java @@ -157,6 +157,7 @@ public class CodeInsightUtil { public static PsiExpression[] findExpressionOccurrences(PsiElement scope, PsiExpression expr) { List array = new ArrayList(); addExpressionOccurrences(RefactoringUtil.unparenthesizeExpression(expr), array, scope); + if (!array.contains(expr)) array.add(expr); return array.toArray(new PsiExpression[array.size()]); } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceField/BaseExpressionToFieldHandler.java b/java/java-impl/src/com/intellij/refactoring/introduceField/BaseExpressionToFieldHandler.java index e3a1c82ef74b..1ff4002c11b0 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceField/BaseExpressionToFieldHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceField/BaseExpressionToFieldHandler.java @@ -39,6 +39,7 @@ import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Pass; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.WindowManager; @@ -164,7 +165,8 @@ public abstract class BaseExpressionToFieldHandler extends IntroduceHandlerBase PsiElement anchor = getNormalizedAnchor(anchorElement); - boolean tempDeleteSelf = false; + final Boolean outOfCodeBlockExtraction = selectedExpr.getUserData(ElementToWorkOn.OUT_OF_CODE_BLOCK); + boolean tempDeleteSelf = outOfCodeBlockExtraction != null; if (element.getParent() instanceof PsiExpressionStatement && anchor.equals(anchorElement)) { PsiStatement statement = (PsiStatement)element.getParent(); if (statement.getParent() instanceof PsiCodeBlock) { @@ -252,7 +254,16 @@ public abstract class BaseExpressionToFieldHandler extends IntroduceHandlerBase if (expr.getParent() instanceof PsiParenthesizedExpression) { expr = (PsiExpression)expr.getParent(); } - if (deleteSelf) { + if (outOfCodeBlockExtraction != null) { + final int endOffset = selectedExpr.getUserData(ElementToWorkOn.TEXT_RANGE).getEndOffset(); + PsiElement endElement = element.getContainingFile().findElementAt(endOffset); + while (true) { + final PsiElement parent = endElement.getParent(); + if (parent instanceof PsiClass) break; + endElement = parent; + } + element.getParent().deleteChildRange(element, PsiTreeUtil.skipSiblingsBackward(endElement, PsiWhiteSpace.class)); + } else if (deleteSelf) { element.getParent().delete(); } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceField/ElementToWorkOn.java b/java/java-impl/src/com/intellij/refactoring/introduceField/ElementToWorkOn.java index b36428b82a12..984f10e44152 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceField/ElementToWorkOn.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceField/ElementToWorkOn.java @@ -43,6 +43,7 @@ public class ElementToWorkOn { public static final Key PREFIX = Key.create("prefix"); public static final Key SUFFIX = Key.create("suffix"); public static final Key TEXT_RANGE = Key.create("range"); + public static final Key OUT_OF_CODE_BLOCK= Key.create("out_of_code_block"); private ElementToWorkOn(PsiLocalVariable localVariable, PsiExpression expr) { myLocalVariable = localVariable; diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java index 2f66a39e5113..9d373db01edc 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java @@ -199,8 +199,10 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme if (elementAtEnd == null) return null; PsiExpression tempExpr; - final PsiElement elementAt = PsiTreeUtil.findCommonParent(elementAtStart, elementAtEnd); - if (PsiTreeUtil.getParentOfType(elementAt, PsiExpression.class, false) == null) return null; + PsiElement elementAt = PsiTreeUtil.findCommonParent(elementAtStart, elementAtEnd); + if (PsiTreeUtil.getParentOfType(elementAt, PsiExpression.class, false) == null) { + elementAt = null; + } final PsiLiteralExpression literalExpression = PsiTreeUtil.getParentOfType(elementAt, PsiLiteralExpression.class); final PsiLiteralExpression startLiteralExpression = PsiTreeUtil.getParentOfType(elementAtStart, PsiLiteralExpression.class); @@ -286,7 +288,19 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme FileDocumentManager.getInstance().getDocument(file.getVirtualFile()).createRangeMarker(startOffset, endOffset); tempExpr.putUserData(ElementToWorkOn.TEXT_RANGE, rangeMarker); - tempExpr.putUserData(ElementToWorkOn.PARENT, parent); + if (parent != null) { + tempExpr.putUserData(ElementToWorkOn.PARENT, parent); + } + else { + PsiErrorElement errorElement = PsiTreeUtil.getNextSiblingOfType(elementAtStart, PsiErrorElement.class); + if (errorElement == null) { + errorElement = PsiTreeUtil.getParentOfType(elementAtStart, PsiErrorElement.class); + } + if (errorElement == null) return null; + if (!(errorElement.getParent() instanceof PsiClass)) return null; + tempExpr.putUserData(ElementToWorkOn.PARENT, errorElement); + tempExpr.putUserData(ElementToWorkOn.OUT_OF_CODE_BLOCK, Boolean.TRUE); + } final String fakeInitializer = "intellijidearulezzz"; final int[] refIdx = new int[1]; @@ -567,18 +581,21 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase impleme final String prefix, final String suffix, final PsiElement parent, final RangeMarker rangeMarker, int[] refIdx) { - final String allText = parent.getContainingFile().getText(); - final TextRange parentRange = parent.getTextRange(); + String text = refText; + if (parent != null) { + final String allText = parent.getContainingFile().getText(); + final TextRange parentRange = parent.getTextRange(); - String beg = allText.substring(parentRange.getStartOffset(), rangeMarker.getStartOffset()); - if (StringUtil.stripQuotesAroundValue(beg).trim().length() == 0 && prefix == null) beg = ""; + String beg = allText.substring(parentRange.getStartOffset(), rangeMarker.getStartOffset()); + if (StringUtil.stripQuotesAroundValue(beg).trim().length() == 0 && prefix == null) beg = ""; - String end = allText.substring(rangeMarker.getEndOffset(), parentRange.getEndOffset()); - if (StringUtil.stripQuotesAroundValue(end).trim().length() == 0 && suffix == null) end = ""; + String end = allText.substring(rangeMarker.getEndOffset(), parentRange.getEndOffset()); + if (StringUtil.stripQuotesAroundValue(end).trim().length() == 0 && suffix == null) end = ""; - final String start = beg + (prefix != null ? prefix : ""); - refIdx[0] = start.length(); - final String text = start + refText + (suffix != null ? suffix : "") + end; + final String start = beg + (prefix != null ? prefix : ""); + refIdx[0] = start.length(); + text = start + refText + (suffix != null ? suffix : "") + end; + } return JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(text, parent); } diff --git a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java index 8249dc4eb6ce..5d690a7d6d53 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java @@ -307,6 +307,7 @@ public class RefactoringUtil { public static PsiElement getParentExpressionAnchorElement(PsiElement place) { PsiElement parent = place.getUserData(ElementToWorkOn.PARENT); + if (place.getUserData(ElementToWorkOn.OUT_OF_CODE_BLOCK) != null) return parent; if (parent == null) parent = place; while (true) { if (isExpressionAnchorElement(parent)) return parent; @@ -545,6 +546,7 @@ public class RefactoringUtil { public static PsiElement getAnchorElementForMultipleExpressions(PsiExpression[] occurrences, PsiElement scope) { PsiElement anchor = null; for (PsiExpression occurrence : occurrences) { + // if (!occurrence.isPhysical()) continue; if (scope != null && !PsiTreeUtil.isAncestor(scope, occurrence, false)) { continue; } diff --git a/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelBinary.java b/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelBinary.java new file mode 100644 index 000000000000..07800bbf4d59 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelBinary.java @@ -0,0 +1,3 @@ +class Test { + public final int anInt = 2 + 2; +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelClassForName.java b/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelClassForName.java new file mode 100644 index 000000000000..cf61fc4b18e0 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelClassForName.java @@ -0,0 +1,7 @@ +class Test { + public final Class aClass = Class.forName(Test.class.getName); + + void foo() { + Class clazz = aClass; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelDuplicates.java b/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelDuplicates.java new file mode 100644 index 000000000000..5b4a9efa8258 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelDuplicates.java @@ -0,0 +1,5 @@ +class Test { + public final int anInt = Integer.parseInt(""); + + void foo() {int i = anInt;} +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelDuplicates1.java b/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelDuplicates1.java new file mode 100644 index 000000000000..5b39765a853d --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelDuplicates1.java @@ -0,0 +1,4 @@ +class Test { + public final int anInt = Integer.parseInt(""); + int i = anInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelNewExpression.java b/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelNewExpression.java new file mode 100644 index 000000000000..b93660972f0b --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelNewExpression.java @@ -0,0 +1,7 @@ +class Test { + public final Integer integer = new Integer(0); + + void foo() { + Integer i = integer; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelNoDuplicates.java b/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelNoDuplicates.java new file mode 100644 index 000000000000..83e9bfa4b7ec --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceField/afterOnClassLevelNoDuplicates.java @@ -0,0 +1,3 @@ +class Test { + public final int anInt = Integer.parseInt(""); +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelBinary.java b/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelBinary.java new file mode 100644 index 000000000000..4faafe3331fe --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelBinary.java @@ -0,0 +1,3 @@ +class Test { + 2 + 2 +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelClassForName.java b/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelClassForName.java new file mode 100644 index 000000000000..fedf7f3c0fbc --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelClassForName.java @@ -0,0 +1,7 @@ +class Test { + Class.forName(Test.class.getName) + + void foo() { + Class clazz = Class.forName(Test.class.getName); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelDuplicates.java b/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelDuplicates.java new file mode 100644 index 000000000000..e1866e86312f --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelDuplicates.java @@ -0,0 +1,4 @@ +class Test { + Integer.parseInt("") + void foo() {int i = Integer.parseInt("");} +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelDuplicates1.java b/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelDuplicates1.java new file mode 100644 index 000000000000..7679c9e6e501 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelDuplicates1.java @@ -0,0 +1,4 @@ +class Test { + int i = Integer.parseInt(""); + Integer.parseInt("") +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelNewExpression.java b/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelNewExpression.java new file mode 100644 index 000000000000..e39504b6ec22 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelNewExpression.java @@ -0,0 +1,7 @@ +class Test { + new Integer(0) + + void foo() { + Integer i = new Integer(0); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelNoDuplicates.java b/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelNoDuplicates.java new file mode 100644 index 000000000000..671bb2ea5053 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceField/beforeOnClassLevelNoDuplicates.java @@ -0,0 +1,3 @@ +class Test { + Integer.parseInt("") +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceFieldInSameClassTest.java b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceFieldInSameClassTest.java index 2048ed9afe39..ea4b6a3d5887 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceFieldInSameClassTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceFieldInSameClassTest.java @@ -33,6 +33,43 @@ public class IntroduceFieldInSameClassTest extends LightCodeInsightTestCase { checkResultByFile("/refactoring/introduceField/afterOuterClass.java"); } + public void testOnClassLevelNoDuplicates() throws Exception { + configureByFile("/refactoring/introduceField/beforeOnClassLevelNoDuplicates.java"); + performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false); + checkResultByFile("/refactoring/introduceField/afterOnClassLevelNoDuplicates.java"); + } + + public void testOnClassLevelDuplicates() throws Exception { + configureByFile("/refactoring/introduceField/beforeOnClassLevelDuplicates.java"); + performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false); + checkResultByFile("/refactoring/introduceField/afterOnClassLevelDuplicates.java"); + } + + public void testOnClassLevelDuplicates1() throws Exception { + configureByFile("/refactoring/introduceField/beforeOnClassLevelDuplicates1.java"); + performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false); + checkResultByFile("/refactoring/introduceField/afterOnClassLevelDuplicates1.java"); + } + + public void testOnClassLevelBinary() throws Exception { + configureByFile("/refactoring/introduceField/beforeOnClassLevelBinary.java"); + performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false); + checkResultByFile("/refactoring/introduceField/afterOnClassLevelBinary.java"); + } + //multiple error elements on class level corresponding to the extracted fragment ------------------ + public void testOnClassLevelNewExpression() throws Exception { + configureByFile("/refactoring/introduceField/beforeOnClassLevelNewExpression.java"); + performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false); + checkResultByFile("/refactoring/introduceField/afterOnClassLevelNewExpression.java"); + } + + public void testOnClassLevelClassForName() throws Exception { + configureByFile("/refactoring/introduceField/beforeOnClassLevelClassForName.java"); + performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false); + checkResultByFile("/refactoring/introduceField/afterOnClassLevelClassForName.java"); + } + //------------------------------------------------------------------------------------------------- + private static void performRefactoring(final BaseExpressionToFieldHandler.InitializationPlace initializationPlace, final boolean declareStatic) { new MockIntroduceFieldHandler(initializationPlace, declareStatic).invoke(getProject(), myEditor, myFile, null); }