mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
compound filter from filters chain (IDEA-146147)
This commit is contained in:
+163
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class MergeFilterChainAction extends PsiElementBaseIntentionAction {
|
||||
private static final Logger LOG = Logger.getInstance(MergeFilterChainAction.class.getName());
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull final PsiElement element) {
|
||||
if (!(element instanceof PsiIdentifier)) return false;
|
||||
final PsiElement parent = element.getParent();
|
||||
if (!(parent instanceof PsiReferenceExpression)) return false;
|
||||
final PsiElement gParent = parent.getParent();
|
||||
if (!(gParent instanceof PsiMethodCallExpression)) return false;
|
||||
|
||||
if (!isFilterCall((PsiMethodCallExpression)gParent)) return false;
|
||||
|
||||
return getFilterToMerge((PsiMethodCallExpression)gParent) != null;
|
||||
}
|
||||
|
||||
private static PsiMethodCallExpression getFilterToMerge(PsiMethodCallExpression methodCallExpression) {
|
||||
final PsiExpression qualifierExpression = methodCallExpression.getMethodExpression().getQualifierExpression();
|
||||
if (qualifierExpression instanceof PsiMethodCallExpression && isFilterCall((PsiMethodCallExpression)qualifierExpression)) {
|
||||
return (PsiMethodCallExpression)qualifierExpression;
|
||||
}
|
||||
|
||||
final PsiElement parent = methodCallExpression.getParent();
|
||||
if (parent instanceof PsiReferenceExpression) {
|
||||
final PsiElement gParent = parent.getParent();
|
||||
if (gParent instanceof PsiMethodCallExpression && isFilterCall((PsiMethodCallExpression)gParent)) {
|
||||
return (PsiMethodCallExpression)gParent;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isFilterCall(PsiMethodCallExpression methodCallExpression) {
|
||||
if (!"filter".equals(methodCallExpression.getMethodExpression().getReferenceName())) return false;
|
||||
|
||||
final PsiExpressionList argumentList = methodCallExpression.getArgumentList();
|
||||
final PsiExpression[] expressions = argumentList.getExpressions();
|
||||
if (expressions.length != 1) return false;
|
||||
if (!(expressions[0] instanceof PsiLambdaExpression)) return false;
|
||||
final PsiElement lambdaBody = ((PsiLambdaExpression)expressions[0]).getBody();
|
||||
if (!(lambdaBody instanceof PsiExpression)) return false;
|
||||
|
||||
final PsiMethod method = methodCallExpression.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.merge.filter.text");
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getFamilyName() {
|
||||
return CodeInsightBundle.message("intention.merge.filter.family");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
|
||||
try {
|
||||
if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
|
||||
|
||||
final PsiMethodCallExpression filterCall = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
|
||||
LOG.assertTrue(filterCall != null);
|
||||
|
||||
final PsiMethodCallExpression filterToMerge = getFilterToMerge(filterCall);
|
||||
LOG.assertTrue(filterToMerge != null);
|
||||
|
||||
final PsiMethodCallExpression callToStay = filterCall.getTextLength() < filterToMerge.getTextLength() ? filterCall : filterToMerge;
|
||||
final PsiMethodCallExpression callToEliminate = callToStay == filterCall ? filterToMerge : filterCall;
|
||||
|
||||
final PsiLambdaExpression targetLambda = (PsiLambdaExpression)callToStay.getArgumentList().getExpressions()[0];
|
||||
final PsiParameter[] parameters = targetLambda.getParameterList().getParameters();
|
||||
final String name = parameters.length > 0 ? parameters[0].getName() : null;
|
||||
|
||||
final PsiLambdaExpression sourceLambda = (PsiLambdaExpression)callToEliminate.getArgumentList().getExpressions()[0];
|
||||
if (name != null) {
|
||||
final PsiParameter[] sourceLambdaParams = sourceLambda.getParameterList().getParameters();
|
||||
if (sourceLambdaParams.length > 0 && !name.equals(sourceLambdaParams[0].getName())) {
|
||||
for (PsiReference reference : ReferencesSearch.search(sourceLambdaParams[0]).findAll()) {
|
||||
final PsiElement referenceElement = reference.getElement();
|
||||
if (referenceElement instanceof PsiReferenceExpression) {
|
||||
((PsiReferenceExpression)referenceElement).handleElementRename(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PsiElement targetBody = targetLambda.getBody();
|
||||
LOG.assertTrue(targetBody instanceof PsiExpression);
|
||||
final PsiElement sourceLambdaBody = sourceLambda.getBody();
|
||||
|
||||
LOG.assertTrue(sourceLambdaBody instanceof PsiExpression);
|
||||
|
||||
|
||||
final PsiExpression compoundExpression = JavaPsiFacade.getElementFactory(project)
|
||||
.createExpressionFromText(targetBody.getText() + " && " + sourceLambdaBody.getText(), sourceLambda);
|
||||
targetBody = targetBody.replace(compoundExpression);
|
||||
CodeStyleManager.getInstance(project).reformat(targetBody);
|
||||
|
||||
final PsiExpression qualifierExpression = callToEliminate.getMethodExpression().getQualifierExpression();
|
||||
LOG.assertTrue(qualifierExpression != null, callToEliminate);
|
||||
final Collection<PsiComment> comments = PsiTreeUtil.findChildrenOfType(callToEliminate, PsiComment.class);
|
||||
for (PsiComment comment : comments) {
|
||||
final TextRange commentRange = comment.getTextRange();
|
||||
if (!sourceLambdaBody.getTextRange().contains(commentRange) &&
|
||||
!qualifierExpression.getTextRange().contains(commentRange)) {
|
||||
targetBody.add(comment);
|
||||
}
|
||||
}
|
||||
callToEliminate.replace(qualifierExpression);
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,6 @@ 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;
|
||||
@@ -50,18 +49,7 @@ public class SplitFilterAction extends PsiElementBaseIntentionAction {
|
||||
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)) {
|
||||
if (MergeFilterChainAction.isFilterCall((PsiMethodCallExpression)gParent)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Merge filter's chain" "true"
|
||||
|
||||
import java.util.stream.Stream;
|
||||
class Test {
|
||||
void foo(Stream<String> stringStream ) {
|
||||
stringStream.filter(name -> name.startsWith("A") && name.//comment2
|
||||
length() > 1//comment
|
||||
/*comment1*/
|
||||
).findAny();
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Merge filter's chain" "true"
|
||||
|
||||
import java.util.stream.Stream;
|
||||
class Test {
|
||||
void foo(Stream<String> stringStream ) {
|
||||
stringStream.filter(name -> name.startsWith("A") && name.length() > 1).findAny();
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Merge filter's chain" "true"
|
||||
|
||||
import java.util.stream.Stream;
|
||||
class Test {
|
||||
void foo(Stream<String> stringStream ) {
|
||||
stringStream.filt<caret>er(name -> name.startsWith("A"))//comment
|
||||
.filter(a -> a.//comment2
|
||||
length() > 1 /*comment1*/).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.fi<caret>lter(name -> name.startsWith("A")).filter().findAny();
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Merge filter's chain" "false"
|
||||
|
||||
import java.util.stream.Stream;
|
||||
class Test {
|
||||
void foo(Stream<String> stringStream ) {
|
||||
stringStream.fil<caret>ter(name -> name.startsWith("A")).map(a -> a).filter(b -> true);
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// "Merge filter's chain" "true"
|
||||
|
||||
import java.util.stream.Stream;
|
||||
class Test {
|
||||
void foo(Stream<String> stringStream ) {
|
||||
stringStream.filt<caret>er(name -> name.startsWith("A")).filter(a -> a.length() > 1).findAny();
|
||||
}
|
||||
}
|
||||
+35
@@ -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 MergeFilterChainActionTest extends LightIntentionActionTestCase {
|
||||
|
||||
public void test() throws Exception { doAllTests(); }
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/mergeFilter";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sdk getProjectJDK() {
|
||||
return IdeaTestUtil.getMockJdk18();
|
||||
}
|
||||
}
|
||||
@@ -175,6 +175,8 @@ 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.merge.filter.text=Merge filter's chain
|
||||
intention.merge.filter.family=Merge filters
|
||||
intention.introduce.variable.text=Introduce local variable
|
||||
intention.encapsulate.field.text=Encapsulate field
|
||||
intention.implement.abstract.method.family=Implement Abstract Method
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
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,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,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention converts chain of Stream.filter calls into one filter call with conjunction condition.
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention converts stream.filter(a && b) expression into 2 chained filter calls.
|
||||
This intention converts stream.filter(s -> a && b) expression into 2 chained filter calls.
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -769,6 +769,10 @@
|
||||
<className>com.intellij.codeInsight.intention.impl.SplitFilterAction</className>
|
||||
<category>Java/Streams</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>com.intellij.codeInsight.intention.impl.MergeFilterChainAction</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