From aaee667ca7b309673bfdc7c4633166f00770cf79 Mon Sep 17 00:00:00 2001 From: Roman Ivanov Date: Wed, 16 Aug 2017 17:15:18 +0700 Subject: [PATCH] add nested stream filter recognition --- .../StreamApiMigrationInspection.java | 27 ++++++++++------- .../streamMigration/TerminalBlock.java | 29 +++++++++++++++++-- .../filter/afterFilterNestedStream.java | 15 ++++++++++ .../filter/beforeFilterNestedStream.java | 22 ++++++++++++++ 4 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/filter/afterFilterNestedStream.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/filter/beforeFilterNestedStream.java diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java index 6714a9a1ed25..7921d64a762e 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/StreamApiMigrationInspection.java @@ -743,30 +743,33 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo } } - static class CompoundFilterOp extends FilterOp { - private final FlatMapOp myFlatMapOp; + static class CompoundFilterOp extends FilterOp { + private final PsiExpression myMatchExpression; + private final StreamSource mySource; private final PsiVariable myMatchVariable; - CompoundFilterOp(FilterOp source, FlatMapOp flatMapOp) { - super(source.getExpression(), flatMapOp.myVariable, source.myNegated); - myMatchVariable = source.myVariable; - myFlatMapOp = flatMapOp; + protected CompoundFilterOp(StreamSource source, PsiVariable matchVariable, FilterOp sourceFilter) { + super(sourceFilter.getExpression(), matchVariable, sourceFilter.isNegated()); + myMatchExpression = sourceFilter.getExpression(); + mySource = source; + myMatchVariable = sourceFilter.getVariable(); } + @Override PsiExpression makeIntermediateExpression(PsiElementFactory factory) { - return factory.createExpressionFromText(myFlatMapOp.getStreamExpression() + ".anyMatch(" + - LambdaUtil.createLambda(myMatchVariable, myExpression) + ")", myExpression); + return factory.createExpressionFromText(mySource.createReplacement() + ".anyMatch(" + + LambdaUtil.createLambda(myMatchVariable, myMatchExpression) + ")", myMatchExpression); } @Override boolean isWriteAllowed(PsiVariable variable, PsiExpression reference) { - return myFlatMapOp.isWriteAllowed(variable, reference); + return mySource.isWriteAllowed(variable, reference); } @Override StreamEx expressions() { - return StreamEx.of(myExpression, myFlatMapOp.myExpression); + return StreamEx.of(myMatchExpression, mySource.getExpression()); } } @@ -820,6 +823,10 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo return "." + operation + "(" + lambda + ")"; } + public StreamSource getSource() { + return mySource; + } + @NotNull String getStreamExpression() { return mySource.createReplacement(); diff --git a/java/java-impl/src/com/intellij/codeInspection/streamMigration/TerminalBlock.java b/java/java-impl/src/com/intellij/codeInspection/streamMigration/TerminalBlock.java index b374178faaae..fef157cf5ab6 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamMigration/TerminalBlock.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamMigration/TerminalBlock.java @@ -113,8 +113,9 @@ class TerminalBlock { @Nullable private TerminalBlock extractFilter() { - if(getSingleStatement() instanceof PsiIfStatement) { - PsiIfStatement ifStatement = (PsiIfStatement)getSingleStatement(); + PsiStatement single = getSingleStatement(); + if (single instanceof PsiIfStatement) { + PsiIfStatement ifStatement = (PsiIfStatement)single; if(ifStatement.getElseBranch() == null && ifStatement.getCondition() != null) { PsiStatement thenBranch = ifStatement.getThenBranch(); if(thenBranch != null) { @@ -122,6 +123,27 @@ class TerminalBlock { } } } + else if (single instanceof PsiLoopStatement) { + // Try extract nested filter like this: + // for(List subList : list) for(T t : subList) if(condition.test(t)) { ...; break; } + // if t is not used in "...", then this could be converted to + // list.stream().filter(subList -> subList.stream().anyMatch(condition)).forEach(subList -> ...) + PsiLoopStatement loopStatement = (PsiLoopStatement)single; + StreamSource source = StreamSource.tryCreate(loopStatement); + final PsiStatement body = loopStatement.getBody(); + if (source == null || body == null) return null; + TerminalBlock innerTb = from(source, body); + FilterOp innerFilter = innerTb.getLastOperation(FilterOp.class); + if (innerFilter == null) return null; + if (!VariableAccessUtils.variableIsUsed(myVariable, body)) return null; + PsiStatement[] statements = innerTb.getStatements(); + PsiStatement lastStatement = statements[statements.length - 1]; + PsiReturnStatement returnStatement = tryCast(lastStatement, PsiReturnStatement.class); + if (returnStatement == null) return null; + if (!ExpressionUtils.isReferenceTo(returnStatement.getReturnValue(), getVariable())) return null; + return new TerminalBlock(this, new CompoundFilterOp(source, myVariable, innerFilter), + myVariable, statements); + } if(myStatements.length >= 1) { PsiStatement first = myStatements[0]; // extract filter with negation @@ -179,7 +201,8 @@ class TerminalBlock { PsiStatement lastStatement = statements[statements.length-1]; if (lastStatement instanceof PsiBreakStatement && op.breaksMe((PsiBreakStatement)lastStatement) && ReferencesSearch.search(withFlatMapFilter.getVariable(), new LocalSearchScope(statements)).findFirst() == null) { - return new TerminalBlock(this, new CompoundFilterOp((FilterOp)withFlatMapFilter.getLastOperation(), op), + FilterOp filterOp = (FilterOp)withFlatMapFilter.getLastOperation(); + return new TerminalBlock(this, new CompoundFilterOp(op.getSource(), op.getVariable(), filterOp), myVariable, Arrays.copyOfRange(statements, 0, statements.length-1)); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/filter/afterFilterNestedStream.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/filter/afterFilterNestedStream.java new file mode 100644 index 000000000000..805373fdb332 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/filter/afterFilterNestedStream.java @@ -0,0 +1,15 @@ +// "Replace with findFirst()" "true" +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class Main { + class MappedField{ + List getLoadNames(){return null;} + } + + public MappedField getMappedField(final String storedName) { + List persistenceFields = new ArrayList<>(); + return persistenceFields.stream().filter(mf -> mf.getLoadNames().stream().anyMatch(storedName::equals)).findFirst().orElse(null); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/filter/beforeFilterNestedStream.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/filter/beforeFilterNestedStream.java new file mode 100644 index 000000000000..bcfa34601b17 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/filter/beforeFilterNestedStream.java @@ -0,0 +1,22 @@ +// "Replace with findFirst()" "true" +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class Main { + class MappedField{ + List getLoadNames(){return null;} + } + + public MappedField getMappedField(final String storedName) { + List persistenceFields = new ArrayList<>(); + for (final MappedField mf : persistenceFields) { + for (final String n : mf.getLoadNames()) { + if (storedName.equals(n)) { + return mf; + } + } + } + return null; + } +} \ No newline at end of file