add nested stream filter recognition

This commit is contained in:
Roman Ivanov
2017-08-24 11:35:17 +07:00
parent 930ec09d12
commit aaee667ca7
4 changed files with 80 additions and 13 deletions
@@ -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<PsiExpression> 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();
@@ -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));
}
}
@@ -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<MappedField> getLoadNames(){return null;}
}
public MappedField getMappedField(final String storedName) {
List<MappedField> persistenceFields = new ArrayList<>();
return persistenceFields.stream().filter(mf -> mf.getLoadNames().stream().anyMatch(storedName::equals)).findFirst().orElse(null);
}
}
@@ -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<MappedField> getLoadNames(){return null;}
}
public MappedField getMappedField(final String storedName) {
List<MappedField> persistenceFields = new ArrayList<>();
for<caret> (final MappedField mf : persistenceFields) {
for (final String n : mf.getLoadNames()) {
if (storedName.equals(n)) {
return mf;
}
}
}
return null;
}
}