diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/PushConditionInCallAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/PushConditionInCallAction.java index 600aa3510220..004e92ffb813 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/PushConditionInCallAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/PushConditionInCallAction.java @@ -20,6 +20,7 @@ import com.intellij.codeInsight.PsiEquivalenceUtil; import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.util.PsiTreeUtil; @@ -42,14 +43,34 @@ public class PushConditionInCallAction extends PsiElementBaseIntentionAction { // if (!(element instanceof PsiJavaToken && ((PsiJavaToken)element).getTokenType() == JavaTokenType.QUEST)) return false; final PsiConditionalExpression conditionalExpression = PsiTreeUtil.getParentOfType(element, PsiConditionalExpression.class); if (conditionalExpression == null) return false; + final String conditionText = conditionalExpression.getCondition().getText(); final PsiExpression thenExpression = conditionalExpression.getThenExpression(); + final PsiExpression elseExpression = conditionalExpression.getElseExpression(); + + return isAvailable(conditionText, thenExpression, elseExpression, 0); + } + + @Override + public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { + if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return; + + final PsiConditionalExpression conditionalExpression = PsiTreeUtil.getParentOfType(element, PsiConditionalExpression.class); + if (conditionalExpression == null) return; + PsiExpression thenExpression = conditionalExpression.getThenExpression(); + if (thenExpression == null) return; + + thenExpression = (PsiExpression)thenExpression.copy(); + replaceRecursively(project, conditionalExpression, thenExpression, conditionalExpression.getElseExpression()); + CodeStyleManager.getInstance(project).reformat(conditionalExpression.replace(thenExpression)); + } + + private boolean isAvailable(String conditionText, PsiExpression thenExpression, PsiExpression elseExpression, int level) { if (!(thenExpression instanceof PsiCallExpression)) return false; final PsiMethod thenMethod = ((PsiCallExpression)thenExpression).resolveMethod(); final PsiExpressionList thenArgsList = ((PsiCallExpression)thenExpression).getArgumentList(); if (thenArgsList == null) return false; final PsiExpression[] thenExpressions = thenArgsList.getExpressions(); - final PsiExpression elseExpression = conditionalExpression.getElseExpression(); if (!(elseExpression instanceof PsiCallExpression)) return false; final PsiMethod elseMethod = ((PsiCallExpression)elseExpression).resolveMethod(); final PsiExpressionList elseArgsList = ((PsiCallExpression)elseExpression).getArgumentList(); @@ -60,51 +81,107 @@ public class PushConditionInCallAction extends PsiElementBaseIntentionAction { if (thenExpressions.length != elseExpressions.length) return false; + final Pair qualifiers = getQualifiers(thenExpression, elseExpression); + if (qualifiers != null) { + if (!isSameCall(thenExpressions, elseExpressions) && level == 0){ + return false; + } + if (level > 0) { + setText("Push condition '" + conditionText + "' inside " + (thenMethod.isConstructor() ? "constructor" : "method") + " call"); + return true; + } + return level > 0 || isAvailable(conditionText, qualifiers.first, qualifiers.second, level + 1); + } + PsiExpression tExpr = null; - PsiExpression eExpr = null; for (int i = 0; i < thenExpressions.length; i++) { PsiExpression lExpr = thenExpressions[i]; PsiExpression rExpr = elseExpressions[i]; if (!PsiEquivalenceUtil.areElementsEquivalent(lExpr, rExpr)) { - if (tExpr == null || eExpr == null) { + if (tExpr == null) { tExpr = lExpr; - eExpr = rExpr; } else { return false; } } } - setText("Push condition '" + conditionalExpression.getCondition().getText() + "' inside " + - (thenMethod.isConstructor() ? "constructor" : "method") + " call"); + setText("Push condition '" + conditionText + "' inside " + (thenMethod.isConstructor() ? "constructor" : "method") + " call"); return true; } - - @Override - public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException { - if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return; - - final PsiConditionalExpression conditionalExpression = PsiTreeUtil.getParentOfType(element, PsiConditionalExpression.class); - final PsiExpression thenExpression = (PsiExpression)conditionalExpression.getThenExpression().copy(); - final PsiExpressionList thenArgsList = ((PsiCallExpression)thenExpression).getArgumentList(); - final PsiExpression[] thenExpressions = thenArgsList.getExpressions(); - - final PsiExpression elseExpression = conditionalExpression.getElseExpression(); - final PsiExpressionList elseArgsList = ((PsiCallExpression)elseExpression).getArgumentList(); - final PsiExpression[] elseExpressions = elseArgsList.getExpressions(); - - + private static boolean isSameCall(PsiExpression[] thenExpressions, PsiExpression[] elseExpressions) { for (int i = 0; i < thenExpressions.length; i++) { - PsiExpression lExpr = thenExpressions[i]; - PsiExpression rExpr = elseExpressions[i]; + final PsiExpression lExpr = thenExpressions[i]; + final PsiExpression rExpr = elseExpressions[i]; if (!PsiEquivalenceUtil.areElementsEquivalent(lExpr, rExpr)) { - lExpr.replace(JavaPsiFacade.getElementFactory(project).createExpressionFromText( - conditionalExpression.getCondition().getText() + "?" + lExpr.getText() + ":" + rExpr.getText(), lExpr)); - break; + return false; } } + return true; + } - CodeStyleManager.getInstance(project).reformat(conditionalExpression.replace(thenExpression)); + private static Pair getQualifiers(PsiExpression thenExpression, PsiExpression elseExpression) { + PsiExpression thenQualifier = null; + PsiExpression elseQualifier = null; + if (thenExpression instanceof PsiMethodCallExpression && elseExpression instanceof PsiMethodCallExpression) { + thenQualifier = ((PsiMethodCallExpression)thenExpression).getMethodExpression().getQualifierExpression(); + elseQualifier = ((PsiMethodCallExpression)elseExpression).getMethodExpression().getQualifierExpression(); + } + else if (thenExpression instanceof PsiNewExpression && elseExpression instanceof PsiNewExpression) { + thenQualifier = ((PsiNewExpression)thenExpression).getQualifier(); + elseQualifier = ((PsiNewExpression)elseExpression).getQualifier(); + } + + if (thenQualifier == null ^ elseQualifier == null || + thenQualifier != null && !PsiEquivalenceUtil.areElementsEquivalent(thenQualifier, elseQualifier)) { + return Pair.create(thenQualifier, elseQualifier); + } + + return null; + } + + private static void replaceRecursively(@NotNull Project project, + PsiConditionalExpression conditionalExpression, + PsiExpression thenExpression, + PsiExpression elseExpression) { + final PsiExpressionList thenArgsList = ((PsiCallExpression)thenExpression).getArgumentList(); + if (thenArgsList == null) return; + final PsiExpression[] thenExpressions = thenArgsList.getExpressions(); + + final PsiExpressionList elseArgsList = ((PsiCallExpression)elseExpression).getArgumentList(); + if (elseArgsList == null) return; + final PsiExpression[] elseExpressions = elseArgsList.getExpressions(); + + final Pair qualifiers = getQualifiers(thenExpression, elseExpression); + if (qualifiers != null) { + if (isSameCall(thenExpressions, elseExpressions)) { + replaceRecursively(project, conditionalExpression, qualifiers.first, qualifiers.second); + } + else { + pushConditional(project, conditionalExpression, thenExpression, elseExpression); + } + } + else { + for (int i = 0; i < thenExpressions.length; i++) { + PsiExpression lExpr = thenExpressions[i]; + PsiExpression rExpr = elseExpressions[i]; + if (pushConditional(project, conditionalExpression, lExpr, rExpr)) { + break; + } + } + } + } + + private static boolean pushConditional(@NotNull Project project, + PsiConditionalExpression conditionalExpression, + PsiExpression thenExpression, + PsiExpression elseExpression) { + if (!PsiEquivalenceUtil.areElementsEquivalent(thenExpression, elseExpression)) { + thenExpression.replace(JavaPsiFacade.getElementFactory(project).createExpressionFromText( + conditionalExpression.getCondition().getText() + "?" + thenExpression.getText() + ":" + elseExpression.getText(), thenExpression)); + return true; + } + return false; } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/pushConditionInCall/afterDifferentQualifiers.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/pushConditionInCall/afterDifferentQualifiers.java new file mode 100644 index 000000000000..ddd1486f8bc0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/pushConditionInCall/afterDifferentQualifiers.java @@ -0,0 +1,7 @@ +// "Push condition 'b' inside method call" "true" +class Foo { + void bar(boolean b){ + String s = foo(b ? "true" : "false", true).substring(0); + } + String foo(String p, boolean b) {return p;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/pushConditionInCall/afterDifferentQualifiersInside.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/pushConditionInCall/afterDifferentQualifiersInside.java new file mode 100644 index 000000000000..00a73dcaf260 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/pushConditionInCall/afterDifferentQualifiersInside.java @@ -0,0 +1,7 @@ +// "Push condition 'b' inside method call" "true" +class Foo { + void bar(boolean b){ + String s = foo("true", true).substring(b ? 1 : 2).substring(0); + } + String foo(String p, boolean b) {return p;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/pushConditionInCall/beforeDifferentQualifiers.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/pushConditionInCall/beforeDifferentQualifiers.java new file mode 100644 index 000000000000..e43b51f55e07 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/pushConditionInCall/beforeDifferentQualifiers.java @@ -0,0 +1,7 @@ +// "Push condition 'b' inside method call" "true" +class Foo { + void bar(boolean b){ + String s = b ? foo("true", true).substring(0) : foo("false", true).substring(0); + } + String foo(String p, boolean b) {return p;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/pushConditionInCall/beforeDifferentQualifiersDifferentCalls.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/pushConditionInCall/beforeDifferentQualifiersDifferentCalls.java new file mode 100644 index 000000000000..6c4583f9fe19 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/pushConditionInCall/beforeDifferentQualifiersDifferentCalls.java @@ -0,0 +1,7 @@ +// "Push condition 'b' inside method call" "false" +class Foo { + void bar(boolean b){ + String s = b ? foo("true", true).substring(0) : foo("false", true).substring(1); + } + String foo(String p, boolean b) {return p;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/pushConditionInCall/beforeDifferentQualifiersInside.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/pushConditionInCall/beforeDifferentQualifiersInside.java new file mode 100644 index 000000000000..80fc198ede86 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/pushConditionInCall/beforeDifferentQualifiersInside.java @@ -0,0 +1,7 @@ +// "Push condition 'b' inside method call" "true" +class Foo { + void bar(boolean b){ + String s = b ? foo("true", true).substring(1).substring(0) : foo("true", true).substring(2).substring(0); + } + String foo(String p, boolean b) {return p;} +} \ No newline at end of file