mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
split compound filter into filters chain (IDEA-146147)
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
|
||||
public class SplitConditionUtil {
|
||||
public static PsiPolyadicExpression findCondition(PsiElement element) {
|
||||
return findCondition(element, true, true);
|
||||
}
|
||||
|
||||
public static PsiPolyadicExpression findCondition(PsiElement element, boolean acceptAnd, boolean acceptOr) {
|
||||
if (!(element instanceof PsiJavaToken)) {
|
||||
return null;
|
||||
}
|
||||
PsiJavaToken token = (PsiJavaToken)element;
|
||||
if (!(token.getParent() instanceof PsiPolyadicExpression)) return null;
|
||||
|
||||
PsiPolyadicExpression expression = (PsiPolyadicExpression)token.getParent();
|
||||
boolean isAndExpression = acceptAnd && expression.getOperationTokenType() == JavaTokenType.ANDAND;
|
||||
boolean isOrExpression = acceptOr && expression.getOperationTokenType() == JavaTokenType.OROR;
|
||||
if (!isAndExpression && !isOrExpression) return null;
|
||||
|
||||
while (expression.getParent() instanceof PsiPolyadicExpression) {
|
||||
expression = (PsiPolyadicExpression)expression.getParent();
|
||||
if (isAndExpression && expression.getOperationTokenType() != JavaTokenType.ANDAND) return null;
|
||||
if (isOrExpression && expression.getOperationTokenType() != JavaTokenType.OROR) return null;
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
public static PsiExpression getROperands(PsiPolyadicExpression expression, PsiJavaToken separator) throws IncorrectOperationException {
|
||||
PsiElement next = PsiTreeUtil.skipSiblingsForward(separator, PsiWhiteSpace.class, PsiComment.class);
|
||||
final int offsetInParent;
|
||||
if (next == null) {
|
||||
offsetInParent = separator.getStartOffsetInParent() + separator.getTextLength();
|
||||
} else {
|
||||
offsetInParent = next.getStartOffsetInParent();
|
||||
}
|
||||
|
||||
PsiElementFactory factory = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory();
|
||||
String rOperands = expression.getText().substring(offsetInParent);
|
||||
return factory.createExpressionFromText(rOperands, expression.getParent());
|
||||
}
|
||||
|
||||
public static PsiExpression getLOperands(PsiPolyadicExpression expression, PsiJavaToken separator) throws IncorrectOperationException {
|
||||
PsiElement prev = separator;
|
||||
if (prev.getPrevSibling() instanceof PsiWhiteSpace) prev = prev.getPrevSibling();
|
||||
if (prev == null) {
|
||||
throw new IncorrectOperationException("Unable to split '"+expression.getText()+"' left to '"+separator+"' (offset "+separator.getStartOffsetInParent()+")");
|
||||
}
|
||||
|
||||
PsiElementFactory factory = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory();
|
||||
String rOperands = expression.getText().substring(0, prev.getStartOffsetInParent());
|
||||
return factory.createExpressionFromText(rOperands, expression.getParent());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.FileModificationService;
|
||||
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import static com.intellij.codeInsight.intention.impl.SplitConditionUtil.getLOperands;
|
||||
import static com.intellij.codeInsight.intention.impl.SplitConditionUtil.getROperands;
|
||||
|
||||
public class SplitFilterAction extends PsiElementBaseIntentionAction {
|
||||
private static final Logger LOG = Logger.getInstance(SplitFilterAction.class.getName());
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
|
||||
final PsiPolyadicExpression expression = SplitConditionUtil.findCondition(element, true, false);
|
||||
if (expression == null || expression.getOperands().length < 2) return false;
|
||||
|
||||
PsiElement parent = PsiUtil.skipParenthesizedExprUp(expression.getParent());
|
||||
if (!(parent instanceof PsiLambdaExpression)) return false;
|
||||
if (((PsiLambdaExpression)parent).getParameterList().getParametersCount() != 1) return false;
|
||||
parent = parent.getParent();
|
||||
|
||||
if (!(parent instanceof PsiExpressionList)) return false;
|
||||
final PsiElement gParent = parent.getParent();
|
||||
if (!(gParent instanceof PsiMethodCallExpression)) return false;
|
||||
|
||||
final PsiReferenceExpression methodExpression = ((PsiMethodCallExpression)gParent).getMethodExpression();
|
||||
if (!"filter".equals(methodExpression.getReferenceName())) return false;
|
||||
final PsiExpressionList argumentList = ((PsiMethodCallExpression)gParent).getArgumentList();
|
||||
if (argumentList.getExpressions().length != 1) return false;
|
||||
|
||||
final PsiMethod method = ((PsiMethodCallExpression)gParent).resolveMethod();
|
||||
if (method == null) return false;
|
||||
final PsiClass containingClass = method.getContainingClass();
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
if (parameters.length == 1 &&
|
||||
InheritanceUtil.isInheritor(containingClass, false, CommonClassNames.JAVA_UTIL_STREAM_STREAM) &&
|
||||
InheritanceUtil.isInheritor(parameters[0].getType(), CommonClassNames.JAVA_UTIL_FUNCTION_PREDICATE)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return CodeInsightBundle.message("intention.split.filter.text");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getFamilyName() {
|
||||
return CodeInsightBundle.message("intention.split.filter.family");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
|
||||
try {
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
|
||||
|
||||
final PsiJavaToken token = (PsiJavaToken)element;
|
||||
final PsiPolyadicExpression expression = SplitConditionUtil.findCondition(element, true, false);
|
||||
|
||||
final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(expression, PsiLambdaExpression.class);
|
||||
LOG.assertTrue(lambdaExpression != null);
|
||||
final String lambdaParameterName = lambdaExpression.getParameterList().getParameters()[0].getName();
|
||||
|
||||
final PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(expression, PsiMethodCallExpression.class);
|
||||
LOG.assertTrue(methodCallExpression != null, expression);
|
||||
|
||||
PsiExpression lOperand = getLOperands(expression, token);
|
||||
PsiExpression rOperand = getROperands(expression, token);
|
||||
|
||||
final Collection<PsiComment> comments = PsiTreeUtil.findChildrenOfType(expression, PsiComment.class);
|
||||
|
||||
final PsiMethodCallExpression chainedCall =
|
||||
(PsiMethodCallExpression)JavaPsiFacade.getElementFactory(project).createExpressionFromText("a.filter(" + lambdaParameterName + " -> x)", expression);
|
||||
final PsiExpression argExpression = chainedCall.getArgumentList().getExpressions()[0];
|
||||
final PsiElement rReplaced = ((PsiLambdaExpression)argExpression).getBody().replace(rOperand);
|
||||
|
||||
final PsiExpression compoundArg = methodCallExpression.getArgumentList().getExpressions()[0];
|
||||
|
||||
final int separatorOffset = token.getTextOffset();
|
||||
for (PsiComment comment : comments) {
|
||||
if (comment.getTextOffset() < separatorOffset) {
|
||||
compoundArg.getParent().add(comment);
|
||||
}
|
||||
else {
|
||||
rReplaced.getParent().add(comment);
|
||||
}
|
||||
}
|
||||
|
||||
((PsiLambdaExpression)compoundArg).getBody().replace(lOperand);
|
||||
|
||||
chainedCall.getMethodExpression().getQualifierExpression().replace(methodCallExpression);
|
||||
methodCallExpression.replace(chainedCall);
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,6 +29,9 @@ import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.codeInsight.intention.impl.SplitConditionUtil.getLOperands;
|
||||
import static com.intellij.codeInsight.intention.impl.SplitConditionUtil.getROperands;
|
||||
|
||||
/**
|
||||
* @author mike
|
||||
*/
|
||||
@@ -37,22 +40,8 @@ public class SplitIfAction extends PsiElementBaseIntentionAction {
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
|
||||
if (!(element instanceof PsiJavaToken)) {
|
||||
return false;
|
||||
}
|
||||
PsiJavaToken token = (PsiJavaToken)element;
|
||||
if (!(token.getParent() instanceof PsiPolyadicExpression)) return false;
|
||||
|
||||
PsiPolyadicExpression expression = (PsiPolyadicExpression)token.getParent();
|
||||
boolean isAndExpression = expression.getOperationTokenType() == JavaTokenType.ANDAND;
|
||||
boolean isOrExpression = expression.getOperationTokenType() == JavaTokenType.OROR;
|
||||
if (!isAndExpression && !isOrExpression) return false;
|
||||
|
||||
while (expression.getParent() instanceof PsiPolyadicExpression) {
|
||||
expression = (PsiPolyadicExpression)expression.getParent();
|
||||
if (isAndExpression && expression.getOperationTokenType() != JavaTokenType.ANDAND) return false;
|
||||
if (isOrExpression && expression.getOperationTokenType() != JavaTokenType.OROR) return false;
|
||||
}
|
||||
final PsiPolyadicExpression expression = SplitConditionUtil.findCondition(element);
|
||||
if (expression == null) return false;
|
||||
|
||||
if (!(expression.getParent() instanceof PsiIfStatement)) return false;
|
||||
PsiIfStatement ifStatement = (PsiIfStatement)expression.getParent();
|
||||
@@ -124,32 +113,6 @@ public class SplitIfAction extends PsiElementBaseIntentionAction {
|
||||
editor.getSelectionModel().removeSelection();
|
||||
}
|
||||
|
||||
private static PsiExpression getROperands(PsiPolyadicExpression expression, PsiJavaToken separator) throws IncorrectOperationException {
|
||||
PsiElement next = PsiTreeUtil.skipSiblingsForward(separator, PsiWhiteSpace.class, PsiComment.class);
|
||||
final int offsetInParent;
|
||||
if (next == null) {
|
||||
offsetInParent = separator.getStartOffsetInParent() + separator.getTextLength();
|
||||
} else {
|
||||
offsetInParent = next.getStartOffsetInParent();
|
||||
}
|
||||
|
||||
PsiElementFactory factory = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory();
|
||||
String rOperands = expression.getText().substring(offsetInParent);
|
||||
return factory.createExpressionFromText(rOperands, expression.getParent());
|
||||
}
|
||||
|
||||
private static PsiExpression getLOperands(PsiPolyadicExpression expression, PsiJavaToken separator) throws IncorrectOperationException {
|
||||
PsiElement prev = separator;
|
||||
if (prev.getPrevSibling() instanceof PsiWhiteSpace) prev = prev.getPrevSibling();
|
||||
if (prev == null) {
|
||||
throw new IncorrectOperationException("Unable to split '"+expression.getText()+"' left to '"+separator+"' (offset "+separator.getStartOffsetInParent()+")");
|
||||
}
|
||||
|
||||
PsiElementFactory factory = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory();
|
||||
String rOperands = expression.getText().substring(0, prev.getStartOffsetInParent());
|
||||
return factory.createExpressionFromText(rOperands, expression.getParent());
|
||||
}
|
||||
|
||||
private static void doOrSplit(PsiIfStatement ifStatement, PsiPolyadicExpression expression, PsiJavaToken token, Editor editor) throws IncorrectOperationException {
|
||||
PsiExpression lOperand = getLOperands(expression, token);
|
||||
PsiExpression rOperand = getROperands(expression, token);
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Split into filter's chain" "true"
|
||||
|
||||
import java.util.stream.Stream;
|
||||
class Test {
|
||||
void foo(Stream<String> stringStream ) {
|
||||
stringStream.filter(name -> name.startsWith("A")//starts with A
|
||||
).filter(name -> name.length() > 1/*comment*/).findAny();
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Split into filter's chain" "false"
|
||||
|
||||
import java.util.stream.Stream;
|
||||
class Test {
|
||||
void foo(Stream<String> stringStream ) {
|
||||
stringStream.filter(name -> name.startsWith("A") &<caret>&).findAny();
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Split into filter's chain" "false"
|
||||
|
||||
import java.util.stream.Stream;
|
||||
class Test {
|
||||
void foo(Stream<String> stringStream ) {
|
||||
stringStream.filter(name -> name.startsWith("A") |<caret>| name.length() > 1).findAny();
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Split into filter's chain" "true"
|
||||
|
||||
import java.util.stream.Stream;
|
||||
class Test {
|
||||
void foo(Stream<String> stringStream ) {
|
||||
stringStream.filter(name -> name.startsWith("A") //starts with A
|
||||
&<caret>& /*comment*/name.length() > 1).findAny();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.testFramework.IdeaTestUtil;
|
||||
|
||||
public class SplitFilterActionTest extends LightIntentionActionTestCase {
|
||||
|
||||
public void test() throws Exception { doAllTests(); }
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/splitFilter";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sdk getProjectJDK() {
|
||||
return IdeaTestUtil.getMockJdk18();
|
||||
}
|
||||
}
|
||||
@@ -103,6 +103,7 @@ public interface CommonClassNames {
|
||||
@NonNls String JAVA_UTIL_STREAM_BASE_STREAM = "java.util.stream.BaseStream";
|
||||
@NonNls String JAVA_UTIL_STREAM_STREAM = "java.util.stream.Stream";
|
||||
@NonNls String JAVA_UTIL_STREAM_COLLECTORS = "java.util.stream.Collectors";
|
||||
@NonNls String JAVA_UTIL_FUNCTION_PREDICATE = "java.util.function.Predicate";
|
||||
|
||||
@NonNls String JAVA_LANG_INVOKE_MH_POLYMORPHIC = "java.lang.invoke.MethodHandle.PolymorphicSignature";
|
||||
|
||||
|
||||
@@ -173,6 +173,8 @@ intention.make.type.generic.family=Make Type Generic
|
||||
intention.make.type.generic.text=Change type of {0} to {1}
|
||||
intention.split.if.family=Split If
|
||||
intention.split.if.text=Split into 2 if's
|
||||
intention.split.filter.text=Split into filter's chain
|
||||
intention.split.filter.family=Split filter
|
||||
intention.introduce.variable.text=Introduce local variable
|
||||
intention.encapsulate.field.text=Encapsulate field
|
||||
intention.implement.abstract.method.family=Implement Abstract Method
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class X {
|
||||
Optional<String> foo(Stream<String> stream) {
|
||||
return stream<spot>.filter(name -> name.startsWith("A")).filter(name -> name.length() > 1)</spot>.findFirst();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class X {
|
||||
Optional<String> foo(Stream<String> stream) {
|
||||
return stream.filter(<spot>name -> name.startsWith("A") && name.length() > 1</spot>).findFirst();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention converts stream.filter(a && b) expression into 2 chained filter calls.
|
||||
</body>
|
||||
</html>
|
||||
@@ -765,6 +765,10 @@
|
||||
<className>com.intellij.codeInsight.intention.impl.SplitIfAction</className>
|
||||
<category>Java/Control Flow</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>com.intellij.codeInsight.intention.impl.SplitFilterAction</className>
|
||||
<category>Java/Streams</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>com.intellij.codeInsight.intention.impl.InvertIfConditionAction</className>
|
||||
<category>Java/Control Flow</category>
|
||||
|
||||
Reference in New Issue
Block a user