mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Simplify Stream API call chain inspection improvements: IDEA-179118
This commit is contained in:
+95
-16
@@ -18,6 +18,7 @@ package com.intellij.codeInspection;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.RedundantCastUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
@@ -42,10 +43,14 @@ public class ReplaceInefficientStreamCountInspection extends BaseJavaBatchLocalI
|
||||
private static final CallMatcher STREAM_FLAT_MAP =
|
||||
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_STREAM_STREAM, "flatMap").parameterTypes(
|
||||
CommonClassNames.JAVA_UTIL_FUNCTION_FUNCTION);
|
||||
private static final CallMatcher STREAM_FILTER =
|
||||
CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM, "filter").parameterCount(1);
|
||||
|
||||
private static final CallMapper<CountFix> FIX_MAPPER = new CallMapper<CountFix>()
|
||||
.register(COLLECTION_STREAM, call -> new CountFix(false))
|
||||
.register(STREAM_FLAT_MAP, call -> doesFlatMapCallCollectionStream(call) ? new CountFix(true) : null);
|
||||
.register(COLLECTION_STREAM, call -> new CountFix(SimplificationMode.COLLECTION_SIZE))
|
||||
.register(STREAM_FLAT_MAP, call -> doesFlatMapCallCollectionStream(call) ? new CountFix(SimplificationMode.SUM) : null)
|
||||
.register(STREAM_FILTER, call -> extractComparisonWithZero(call) != null ? new CountFix(SimplificationMode.ANY_MATCH) : null)
|
||||
.register(STREAM_FILTER, call -> extractComparisonWithZeroEq(call) != null ? new CountFix(SimplificationMode.NONE_MATCH) : null);
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(ReplaceInefficientStreamCountInspection.class);
|
||||
|
||||
@@ -80,6 +85,39 @@ public class ReplaceInefficientStreamCountInspection extends BaseJavaBatchLocalI
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiBinaryExpression extractComparisonWithZero(PsiMethodCallExpression filterCall) {
|
||||
PsiBinaryExpression binary = extractBinary(filterCall);
|
||||
if (binary == null) return null;
|
||||
IElementType tokenType = binary.getOperationTokenType();
|
||||
if(ExpressionUtils.isZero(binary.getLOperand()) && (tokenType == JavaTokenType.LT || tokenType == JavaTokenType.NE) ||
|
||||
ExpressionUtils.isZero(binary.getROperand()) && (tokenType == JavaTokenType.GT || tokenType == JavaTokenType.NE)) {
|
||||
return binary;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiBinaryExpression extractComparisonWithZeroEq(PsiMethodCallExpression filterCall) {
|
||||
PsiBinaryExpression binary = extractBinary(filterCall);
|
||||
if (binary == null) return null;
|
||||
IElementType tokenType = binary.getOperationTokenType();
|
||||
if(ExpressionUtils.isZero(binary.getLOperand()) && tokenType == JavaTokenType.EQEQ ||
|
||||
ExpressionUtils.isZero(binary.getROperand()) && tokenType == JavaTokenType.EQEQ) {
|
||||
return binary;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiBinaryExpression extractBinary(PsiMethodCallExpression filterCall) {
|
||||
PsiMethodCallExpression countCall = ExpressionUtils.getCallForQualifier(filterCall);
|
||||
if(countCall == null) return null;
|
||||
PsiElement parent = PsiUtil.skipParenthesizedExprUp(countCall.getParent());
|
||||
if(parent == null) return null;
|
||||
return ObjectUtils.tryCast(parent, PsiBinaryExpression.class);
|
||||
}
|
||||
|
||||
static boolean doesFlatMapCallCollectionStream(PsiMethodCallExpression flatMapCall) {
|
||||
PsiElement function = flatMapCall.getArgumentList().getExpressions()[0];
|
||||
if (function instanceof PsiMethodReferenceExpression) {
|
||||
@@ -116,20 +154,41 @@ public class ReplaceInefficientStreamCountInspection extends BaseJavaBatchLocalI
|
||||
return PsiUtil.skipParenthesizedExprDown(expression);
|
||||
}
|
||||
|
||||
private static class CountFix implements LocalQuickFix {
|
||||
private final boolean myFlatMapMode;
|
||||
private enum SimplificationMode {
|
||||
SUM("Replace Stream.flatMap().count() with Stream.mapToLong().sum()", "Stream.flatMap().count() can be replaced with Stream.mapToLong().sum()"),
|
||||
COLLECTION_SIZE("Replace Collection.stream().count() with Collection.size()", "Collection.stream().count() can be replaced with Collection.size()"),
|
||||
ANY_MATCH("Replace Stream().filter().count() > 0 with stream.anyMatch()", "Stream().filter().count() > 0 can be replaced with stream.anyMatch()"),
|
||||
NONE_MATCH("Replace Stream().filter().count() == 0 with stream.noneMatch()", "Stream().filter().count() == 0 can be replaced with stream.noneMatch()");
|
||||
|
||||
CountFix(boolean flatMapMode) {
|
||||
myFlatMapMode = flatMapMode;
|
||||
private final String myName;
|
||||
private final String myMessage;
|
||||
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
SimplificationMode(String name, String message) {
|
||||
myName = name;
|
||||
myMessage = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return myMessage;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CountFix implements LocalQuickFix {
|
||||
private final SimplificationMode mySimplificationMode;
|
||||
|
||||
CountFix(SimplificationMode simplificationMode) {
|
||||
mySimplificationMode = simplificationMode;
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return myFlatMapMode
|
||||
? "Replace Stream.flatMap().count() with Stream.mapToLong().sum()"
|
||||
: "Replace Collection.stream().count() with Collection.size()";
|
||||
return mySimplificationMode.getName();
|
||||
}
|
||||
|
||||
@Nls
|
||||
@@ -148,14 +207,35 @@ public class ReplaceInefficientStreamCountInspection extends BaseJavaBatchLocalI
|
||||
if (countName == null) return;
|
||||
PsiMethodCallExpression qualifierCall = getQualifierMethodCall(countCall);
|
||||
if (qualifierCall == null) return;
|
||||
if(myFlatMapMode) {
|
||||
replaceFlatMap(countName, qualifierCall);
|
||||
}
|
||||
else {
|
||||
replaceSimpleCount(countCall, qualifierCall);
|
||||
switch (mySimplificationMode) {
|
||||
case SUM:
|
||||
replaceFlatMap(countName, qualifierCall);
|
||||
break;
|
||||
case COLLECTION_SIZE:
|
||||
replaceSimpleCount(countCall, qualifierCall);
|
||||
break;
|
||||
case ANY_MATCH:
|
||||
replaceFilterCountComparison(qualifierCall, true);
|
||||
break;
|
||||
case NONE_MATCH:
|
||||
replaceFilterCountComparison(qualifierCall, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void replaceFilterCountComparison(PsiMethodCallExpression filterCall, boolean isAnyMatch) {
|
||||
if(!STREAM_FILTER.test(filterCall)) return;
|
||||
PsiBinaryExpression comparison = isAnyMatch? extractComparisonWithZero(filterCall) : extractComparisonWithZeroEq(filterCall);
|
||||
if(comparison == null) return;
|
||||
String filterText = filterCall.getArgumentList().getExpressions()[0].getText();
|
||||
PsiExpression filterQualifier = filterCall.getMethodExpression().getQualifierExpression();
|
||||
if(filterQualifier == null) return;
|
||||
String base = filterQualifier.getText();
|
||||
CommentTracker ct = new CommentTracker();
|
||||
ct.markUnchanged(filterQualifier);
|
||||
ct.replaceAndRestoreComments(comparison, base + "." + (isAnyMatch? "anyMatch" : "noneMatch") + "(" + filterText + ")");
|
||||
}
|
||||
|
||||
private static void replaceSimpleCount(PsiMethodCallExpression countCall, PsiMethodCallExpression qualifierCall) {
|
||||
if (!COLLECTION_STREAM.test(qualifierCall)) return;
|
||||
PsiReferenceExpression methodExpression = qualifierCall.getMethodExpression();
|
||||
@@ -214,8 +294,7 @@ public class ReplaceInefficientStreamCountInspection extends BaseJavaBatchLocalI
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return myFlatMapMode ? "Stream.flatMap().count() can be replaced with Stream.mapToLong().sum()" :
|
||||
"Collection.stream().count() can be replaced with Collection.size()";
|
||||
return mySimplificationMode.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+199
-3
@@ -32,6 +32,7 @@ import com.intellij.psi.impl.PsiDiamondTypeUtil;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.refactoring.util.LambdaRefactoringUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.siyeh.ig.callMatcher.CallHandler;
|
||||
@@ -65,12 +66,18 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns
|
||||
instanceCall(CommonClassNames.JAVA_UTIL_STREAM_STREAM, "findFirst", "findAny").parameterCount(0);
|
||||
private static final CallMatcher STREAM_FILTER =
|
||||
instanceCall(CommonClassNames.JAVA_UTIL_STREAM_STREAM, "filter").parameterTypes(CommonClassNames.JAVA_UTIL_FUNCTION_PREDICATE);
|
||||
private static final CallMatcher STREAM_FIND_FIRST =
|
||||
instanceCall(CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM, "findFirst").parameterCount(0);
|
||||
private static final CallMatcher STREAM_SORTED =
|
||||
instanceCall(CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM, "sorted");
|
||||
private static final CallMatcher STREAM_MAP =
|
||||
instanceCall(CommonClassNames.JAVA_UTIL_STREAM_STREAM, "map").parameterTypes(CommonClassNames.JAVA_UTIL_FUNCTION_FUNCTION);
|
||||
private static final CallMatcher BASE_STREAM_MAP =
|
||||
instanceCall(CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM, "map").parameterCount(1);
|
||||
private static final CallMatcher STREAM_ANY_MATCH =
|
||||
instanceCall(CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM, "anyMatch").parameterCount(1);
|
||||
private static final CallMatcher INT_STREAM_RANGE =
|
||||
staticCall(CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM, "range").parameterTypes("int", "int");
|
||||
private static final CallMatcher STREAM_NONE_MATCH =
|
||||
instanceCall(CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM, "noneMatch").parameterCount(1);
|
||||
private static final CallMatcher STREAM_ALL_MATCH =
|
||||
@@ -84,6 +91,16 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns
|
||||
private static final CallMatcher STREAM_OF =
|
||||
staticCall(CommonClassNames.JAVA_UTIL_STREAM_STREAM, "of").parameterTypes("T");
|
||||
|
||||
private static final CallMatcher N_COPIES =
|
||||
staticCall(CommonClassNames.JAVA_UTIL_COLLECTIONS, "nCopies").parameterTypes("int", "T");
|
||||
private static final CallMatcher COMPARATOR_REVERSED =
|
||||
instanceCall(CommonClassNames.JAVA_UTIL_COMPARATOR, "reversed").parameterCount(0);
|
||||
|
||||
private static final CallMatcher STREAM_INT_MAP_TO_ALL =
|
||||
instanceCall(CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM, "map", "mapToObj", "mapToDouble", "mapToLong").parameterCount(1);
|
||||
private static final CallMatcher STREAM_MAP_TO_ALL =
|
||||
instanceCall(CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM, "map", "mapToInt", "mapToDouble", "mapToLong").parameterCount(1);
|
||||
|
||||
private static final CallMatcher STREAM_MATCH = anyOf(STREAM_ANY_MATCH, STREAM_NONE_MATCH, STREAM_ALL_MATCH);
|
||||
|
||||
private static final CallMapper<CallChainSimplification> CALL_TO_FIX_MAPPER = new CallMapper<>(
|
||||
@@ -95,7 +112,10 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns
|
||||
ReplaceForEachMethodFix.handler(),
|
||||
RemoveBooleanIdentityFix.handler(),
|
||||
ReplaceWithPeekFix.handler(),
|
||||
SimpleStreamOfFix.handler()
|
||||
SimpleStreamOfFix.handler(),
|
||||
RangeToArrayStreamFix.handler(),
|
||||
NCopiesToGenerateStreamFix.handler(),
|
||||
SortedFirstToMinMaxFix.handler()
|
||||
).registerAll(SimplifyMatchNegationFix.handlers());
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(SimplifyStreamApiCallChainsInspection.class);
|
||||
@@ -1061,8 +1081,6 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns
|
||||
private static final CallMatcher INT_STREAM_MAP =
|
||||
instanceCall(CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM, "map", "mapToLong", "mapToDouble", "mapToObj")
|
||||
.parameterCount(1);
|
||||
private static final CallMatcher INT_STREAM_RANGE =
|
||||
staticCall(CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM, "range").parameterTypes("int", "int");
|
||||
private static final CallMatcher MIN_INT =
|
||||
anyOf(
|
||||
staticCall(CommonClassNames.JAVA_LANG_MATH, "min").parameterTypes("int", "int"),
|
||||
@@ -1432,4 +1450,182 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static class RangeToArrayStreamFix implements CallChainSimplification {
|
||||
|
||||
private final @NotNull String myReplacement;
|
||||
|
||||
RangeToArrayStreamFix(@NotNull String replacement) {this.myReplacement = replacement;}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Replace with Arrays.stream()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "Can be replaced with Arrays.stream()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiElement simplify(PsiMethodCallExpression call) {
|
||||
PsiMethodCallExpression mapCall = ExpressionUtils.getCallForQualifier(call);
|
||||
if(mapCall == null) return null;
|
||||
return new CommentTracker().replaceAndRestoreComments(mapCall, myReplacement);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static CallHandler<CallChainSimplification> handler() {
|
||||
return CallHandler.of(INT_STREAM_RANGE, call -> {
|
||||
PsiExpression[] args = call.getArgumentList().getExpressions();
|
||||
PsiMethodCallExpression maybeMap = ExpressionUtils.getCallForQualifier(call);
|
||||
if (!STREAM_INT_MAP_TO_ALL.test(maybeMap)) return null;
|
||||
PsiExpression arg = maybeMap.getArgumentList().getExpressions()[0];
|
||||
PsiLambdaExpression lambda = tryCast(arg, PsiLambdaExpression.class);
|
||||
if (lambda == null) return null;
|
||||
PsiParameter[] parameters = lambda.getParameterList().getParameters();
|
||||
if (parameters.length != 1) return null;
|
||||
PsiExpression lambdaExpr = tryCast(lambda.getBody(), PsiExpression.class);
|
||||
if (lambdaExpr == null) return null;
|
||||
PsiArrayAccessExpression arrayAccess = tryCast(PsiUtil.skipParenthesizedExprDown(lambdaExpr), PsiArrayAccessExpression.class);
|
||||
if (arrayAccess == null) return null;
|
||||
PsiExpression index = arrayAccess.getIndexExpression();
|
||||
|
||||
if (!ExpressionUtils.isReferenceTo(index, parameters[0])) return null;
|
||||
|
||||
PsiExpression arrayExpr = arrayAccess.getArrayExpression();
|
||||
PsiArrayType arrayType = tryCast(arrayExpr.getType(), PsiArrayType.class);
|
||||
if (arrayType == null) return null;
|
||||
if (!StreamApiUtil.isSupportedStreamElement(arrayType.getComponentType())) return null;
|
||||
|
||||
PsiExpression leftBound = args[0];
|
||||
PsiExpression rightBound = args[1];
|
||||
return new RangeToArrayStreamFix(
|
||||
CommonClassNames.JAVA_UTIL_ARRAYS + ".stream(" + arrayExpr.getText() + "," + leftBound.getText() + "," + rightBound.getText() + ")");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static class NCopiesToGenerateStreamFix implements CallChainSimplification {
|
||||
|
||||
private final @NotNull String myReplacement;
|
||||
|
||||
NCopiesToGenerateStreamFix(@NotNull String replacement) {myReplacement = replacement;}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Replace with Stream.generate()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "Can be replaced with Stream.generate()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiElement simplify(PsiMethodCallExpression streamCall) {
|
||||
PsiElement maybeMap = ExpressionUtils.getCallForQualifier(streamCall);
|
||||
if(maybeMap == null) return null;
|
||||
Project project = streamCall.getProject();
|
||||
PsiElement result = new CommentTracker().replaceAndRestoreComments(maybeMap, myReplacement);
|
||||
return JavaCodeStyleManager.getInstance(project).shortenClassReferences(result);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static CallHandler<CallChainSimplification> handler() {
|
||||
return CallHandler.of(COLLECTION_STREAM, call -> {
|
||||
PsiMethodCallExpression maybeNCopies = getQualifierMethodCall(call);
|
||||
if(!N_COPIES.test(maybeNCopies)) return null;
|
||||
PsiExpression[] nCopiesArgs = maybeNCopies.getArgumentList().getExpressions();
|
||||
PsiExpression count = nCopiesArgs[0];
|
||||
PsiExpression obj = nCopiesArgs[1];
|
||||
if(!ExpressionUtils.isSimpleExpression(obj)) return null;
|
||||
|
||||
PsiMethodCallExpression maybeMap = ExpressionUtils.getCallForQualifier(call);
|
||||
if(!STREAM_MAP_TO_ALL.test(maybeMap)) return null;
|
||||
PsiExpression arg = maybeMap.getArgumentList().getExpressions()[0];
|
||||
PsiLambdaExpression lambda = tryCast(PsiUtil.skipParenthesizedExprDown(arg), PsiLambdaExpression.class);
|
||||
if(lambda == null) return null;
|
||||
PsiParameter[] parameters = lambda.getParameterList().getParameters();
|
||||
if(parameters.length != 1) return null;
|
||||
PsiParameter lambdaVar = parameters[0];
|
||||
PsiExpression body = tryCast(lambda.getBody(), PsiExpression.class);
|
||||
if (body == null || body.getType() == null) return null;
|
||||
String streamClass = getStreamClassName(maybeMap);
|
||||
if (VariableAccessUtils.variableIsUsed(lambdaVar, body)) return null;
|
||||
return new NCopiesToGenerateStreamFix(streamClass + ".generate(()->" + body.getText() + ").limit(" + count.getText() + ")");
|
||||
});
|
||||
}
|
||||
|
||||
private static String getStreamClassName(@NotNull PsiMethodCallExpression call) {
|
||||
String name = MethodCallUtils.getMethodName(call);
|
||||
if (name == null) return CommonClassNames.JAVA_UTIL_STREAM_STREAM;
|
||||
switch (name) {
|
||||
case "mapToInt":
|
||||
return CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM;
|
||||
case "mapToLong":
|
||||
return CommonClassNames.JAVA_UTIL_STREAM_LONG_STREAM;
|
||||
case "mapToDouble":
|
||||
return CommonClassNames.JAVA_UTIL_STREAM_DOUBLE_STREAM;
|
||||
}
|
||||
return CommonClassNames.JAVA_UTIL_STREAM_STREAM;
|
||||
}
|
||||
}
|
||||
|
||||
static class SortedFirstToMinMaxFix implements CallChainSimplification {
|
||||
private final String myMethodName;
|
||||
private final String myReplacement;
|
||||
|
||||
SortedFirstToMinMaxFix(String methodName, String replacement) {
|
||||
myMethodName = methodName;
|
||||
myReplacement = replacement;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Replace with " + myMethodName + "()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "Can be replaced with " + myMethodName + "()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiElement simplify(PsiMethodCallExpression call) {
|
||||
return new CommentTracker().replaceAndRestoreComments(call, myReplacement);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static CallHandler<CallChainSimplification> handler() {
|
||||
return CallHandler.of(STREAM_FIND_FIRST, call -> {
|
||||
PsiMethodCallExpression maybeSorted = getQualifierMethodCall(call);
|
||||
if (!STREAM_SORTED.test(maybeSorted)) return null;
|
||||
PsiExpression[] args = maybeSorted.getArgumentList().getExpressions();
|
||||
PsiExpression qualifier = maybeSorted.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier == null) return null;
|
||||
|
||||
final String comparator;
|
||||
boolean reversed = false;
|
||||
if (args.length == 1) {
|
||||
PsiExpression maybeComparator = PsiUtil.skipParenthesizedExprDown(args[0]);
|
||||
if (maybeComparator instanceof PsiMethodCallExpression && COMPARATOR_REVERSED.test((PsiMethodCallExpression)maybeComparator)) {
|
||||
PsiExpression comparatorQualifier = ((PsiMethodCallExpression)maybeComparator).getMethodExpression().getQualifierExpression();
|
||||
if(comparatorQualifier == null) return null;
|
||||
comparator = comparatorQualifier.getText();
|
||||
reversed = true;
|
||||
} else {
|
||||
if (maybeComparator == null) return null;
|
||||
PsiType comparatorType = maybeComparator.getType();
|
||||
if (comparatorType == null || !InheritanceUtil.isInheritor(comparatorType, CommonClassNames.JAVA_UTIL_COMPARATOR)) return null;
|
||||
comparator = maybeComparator.getText();
|
||||
}
|
||||
} else return null;
|
||||
|
||||
String methodName = reversed ? "max" : "min";
|
||||
return new SortedFirstToMinMaxFix(methodName, qualifier.getText() + "." + methodName + "(" + comparator + ")");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+87
-65
@@ -63,13 +63,17 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
PsiStatement loopStatement = block.getStreamSourceStatement();
|
||||
String stream = terminal.generateStreamCode();
|
||||
restoreComments(loopStatement, body);
|
||||
PsiLocalVariable builder = terminal.getBuilder();
|
||||
PsiVariable builder = terminal.getBuilder();
|
||||
terminal.preCleanUp();
|
||||
ControlFlowUtils.InitializerUsageStatus status = getInitializerUsageStatus(builder, loopStatement);
|
||||
PsiElement result = replaceInitializer(loopStatement, builder, builder.getInitializer(), stream, status);
|
||||
terminal.cleanUp(builder);
|
||||
JoiningTerminal.replaceUsages(terminal.getBuilder());
|
||||
return result;
|
||||
if(builder instanceof PsiLocalVariable) {
|
||||
PsiElement result = replaceInitializer(loopStatement, builder, builder.getInitializer(), stream, status);
|
||||
terminal.cleanUp((PsiLocalVariable)builder);
|
||||
JoiningTerminal.replaceUsages((PsiLocalVariable)terminal.getBuilder());
|
||||
return result;
|
||||
} else {
|
||||
return new CommentTracker().replaceAndRestoreComments(tb.getStreamSourceStatement(), builder.getName() + ".append(" + stream + ");");
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -109,7 +113,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
private static final EquivalenceChecker ourEquivalence = EquivalenceChecker.getCanonicalPsiEquivalence();
|
||||
|
||||
private final @NotNull TerminalBlock myTerminalBlock;
|
||||
private final @NotNull PsiLocalVariable myBuilder;
|
||||
private final @NotNull PsiVariable myBuilder;
|
||||
private final @NotNull PsiVariable myLoopVariable;
|
||||
private final @NotNull List<PsiExpression> myMainJoinParts;
|
||||
private final @NotNull List<PsiExpression> myPrefixJoinParts;
|
||||
@@ -124,12 +128,12 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiLocalVariable getBuilder() {
|
||||
public PsiVariable getBuilder() {
|
||||
return myBuilder;
|
||||
}
|
||||
|
||||
protected JoiningTerminal(@NotNull TerminalBlock block,
|
||||
@NotNull PsiLocalVariable targetBuilder,
|
||||
@NotNull PsiVariable targetBuilder,
|
||||
@NotNull PsiVariable variable,
|
||||
@NotNull List<PsiExpression> mainJoinParts,
|
||||
@NotNull List<PsiExpression> prefixJoinParts,
|
||||
@@ -359,7 +363,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
*/
|
||||
//
|
||||
@Nullable("when failed to extract")
|
||||
private static PsiLocalVariable extractStringBuilder(@NotNull PsiStatement statement) {
|
||||
private static PsiVariable extractStringBuilder(@NotNull PsiStatement statement) {
|
||||
PsiExpressionStatement expressionStatement = tryCast(statement, PsiExpressionStatement.class);
|
||||
if (expressionStatement == null) return null;
|
||||
PsiMethodCallExpression methodCallExpression = tryCast(expressionStatement.getExpression(), PsiMethodCallExpression.class);
|
||||
@@ -369,7 +373,9 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
PsiExpression qualifierExpression = currentExpression.getMethodExpression().getQualifierExpression();
|
||||
PsiMethodCallExpression callerExpression = MethodCallUtils.getQualifierMethodCall(currentExpression);
|
||||
if (callerExpression == null) {
|
||||
return resolveLocalVariable(qualifierExpression);
|
||||
PsiReferenceExpression refExpression = tryCast(qualifierExpression, PsiReferenceExpression.class);
|
||||
if(refExpression == null) return null;
|
||||
return tryCast(refExpression.resolve(), PsiVariable.class);
|
||||
}
|
||||
currentExpression = callerExpression;
|
||||
}
|
||||
@@ -380,7 +386,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
private static List<PsiExpression> extractJoinParts(@Nullable PsiExpression expression) {
|
||||
List<PsiExpression> joinParts = new ArrayList<>();
|
||||
if (expression == null) return joinParts;
|
||||
return !tryExtractJoinPart(expression, joinParts) ? null : joinParts;
|
||||
return tryExtractJoinPart(expression, joinParts) ? joinParts : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -450,9 +456,23 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiExpression extractStringBuilderInitializer(PsiExpression construction) {
|
||||
PsiNewExpression newExpression = tryCast(PsiUtil.skipParenthesizedExprDown(construction), PsiNewExpression.class);
|
||||
@Nullable("when failed to extract join parts from initializer statement")
|
||||
private static List<PsiExpression> extractStringBuilderInitializer(PsiExpression construction) {
|
||||
List<PsiExpression> joinParts = new ArrayList<>();
|
||||
PsiExpression expression = construction;
|
||||
PsiMethodCallExpression current = tryCast(construction, PsiMethodCallExpression.class);
|
||||
while(current != null) {
|
||||
if (APPEND.test(current)) {
|
||||
joinParts.add(current.getArgumentList().getExpressions()[0]);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
expression = current.getMethodExpression().getQualifierExpression();
|
||||
current = MethodCallUtils.getQualifierMethodCall(current);
|
||||
}
|
||||
|
||||
PsiNewExpression newExpression = tryCast(PsiUtil.skipParenthesizedExprDown(expression), PsiNewExpression.class);
|
||||
if (newExpression == null) return null;
|
||||
final PsiJavaCodeReferenceElement classReference = newExpression.getClassReference();
|
||||
if (classReference == null) return null;
|
||||
@@ -466,11 +486,16 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
final PsiExpressionList argumentList = newExpression.getArgumentList();
|
||||
if (argumentList == null) return null;
|
||||
final PsiExpression[] arguments = argumentList.getExpressions();
|
||||
if (arguments.length != 1) return null;
|
||||
final PsiExpression argument = arguments[0];
|
||||
final PsiType argumentType = argument.getType();
|
||||
if (PsiType.INT.equals(argumentType)) return null;
|
||||
return argument;
|
||||
if (arguments.length != 0) {
|
||||
if(arguments.length != 1) return null;
|
||||
final PsiExpression argument = arguments[0];
|
||||
final PsiType argumentType = argument.getType();
|
||||
if (!PsiType.INT.equals(argumentType)) {
|
||||
joinParts.add(argument);
|
||||
}
|
||||
}
|
||||
Collections.reverse(joinParts);
|
||||
return joinParts;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -557,14 +582,14 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
* Like: if(!sb.isEmpty()) => prefixLength == 0 or if(sb.length() > 2) => prefixLength == 2
|
||||
*/
|
||||
@Nullable
|
||||
private static Integer extractConditionPrefixLength(@NotNull PsiExpression expression, PsiLocalVariable targetBuilder) {
|
||||
private static Integer extractConditionPrefixLength(@NotNull PsiExpression expression, PsiVariable targetBuilder) {
|
||||
Integer explicitLengthCondition = extractExplicitLengthCheck(expression, targetBuilder);
|
||||
if (explicitLengthCondition != null) return explicitLengthCondition;
|
||||
return extractEmptyLengthCheck(expression, targetBuilder);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Integer extractEmptyLengthCheck(@NotNull PsiExpression expression, PsiLocalVariable targetBuilder) {
|
||||
private static Integer extractEmptyLengthCheck(@NotNull PsiExpression expression, PsiVariable targetBuilder) {
|
||||
PsiMethodCallExpression maybeEmptyCall = tryCast(BoolUtils.getNegated(expression), PsiMethodCallExpression.class);
|
||||
if (!EMPTY_LENGTH.test(maybeEmptyCall)) return null; // extract call matcher
|
||||
if (!ExpressionUtils.isReferenceTo(maybeEmptyCall.getMethodExpression().getQualifierExpression(), targetBuilder)) return null;
|
||||
@@ -572,7 +597,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
|
||||
@Nullable("when failed to extract length")
|
||||
private static Integer extractExplicitLengthCheck(@NotNull PsiExpression expression, PsiLocalVariable targetBuilder) {
|
||||
private static Integer extractExplicitLengthCheck(@NotNull PsiExpression expression, PsiVariable targetBuilder) {
|
||||
PsiBinaryExpression condition = tryCast(expression, PsiBinaryExpression.class);
|
||||
if (condition == null) return null;
|
||||
PsiExpression rOperand = condition.getROperand();
|
||||
@@ -588,12 +613,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
}
|
||||
else {
|
||||
int rSize = computeConstantIntExpression(condition.getROperand());
|
||||
if (rSize >= 0) {
|
||||
return extractLength(lOperand, relation, rSize, targetBuilder);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
return rSize >= 0 ? extractLength(lOperand, relation, rSize, targetBuilder) : null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -601,7 +621,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
private static Integer extractLength(PsiExpression rOperand,
|
||||
DfaRelationValue.RelationType relation,
|
||||
int size,
|
||||
PsiLocalVariable targetBuilder) {
|
||||
PsiVariable targetBuilder) {
|
||||
if (!isStringBuilderLengthCall(rOperand, targetBuilder)) return null;
|
||||
LongRangeSet rangeSet = LongRangeSet.point(size).fromRelation(relation);
|
||||
if (rangeSet == null || rangeSet.max() != Long.MAX_VALUE) return null;
|
||||
@@ -609,7 +629,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
return min > 0 ? (int)(min - 1) : null;
|
||||
}
|
||||
|
||||
private static boolean isStringBuilderLengthCall(@NotNull PsiExpression expression, PsiLocalVariable targetBuilder) {
|
||||
private static boolean isStringBuilderLengthCall(@NotNull PsiExpression expression, PsiVariable targetBuilder) {
|
||||
PsiMethodCallExpression methodCallExpression = tryCast(expression, PsiMethodCallExpression.class);
|
||||
return LENGTH.test(methodCallExpression) &&
|
||||
ExpressionUtils.isReferenceTo(methodCallExpression.getMethodExpression().getQualifierExpression(), targetBuilder);
|
||||
@@ -673,7 +693,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
@Nullable
|
||||
static PrefixSuffixContext extractAndVerifyRefs(@NotNull PsiStatement finalAppendPredecessor,
|
||||
@NotNull PsiStatement firstAppendSuccessor,
|
||||
@NotNull PsiLocalVariable targetBuilder,
|
||||
@NotNull PsiVariable targetBuilder,
|
||||
@NotNull TerminalBlock terminalBlock,
|
||||
@NotNull List<PsiLocalVariable> possibleVariablesBeforeLoop,
|
||||
@NotNull Set<PsiMethodCallExpression> allowedReferencePlaces) {
|
||||
@@ -682,30 +702,32 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
List<PsiDeclarationStatement> declarations = getDeclarations(possibleVariablesBeforeLoop);
|
||||
if(declarations == null) return null;
|
||||
PsiMethodCallExpression beforeLoopAppend = getCallBeforeStatement(firstAppendSuccessor, targetBuilder, APPEND, declarations);
|
||||
List<PsiExpression> builderStrInitializers = null;
|
||||
if(targetBuilder instanceof PsiLocalVariable) {
|
||||
if(!canBeMadeNonFinal((PsiLocalVariable)targetBuilder, terminalBlock.getStreamSourceStatement())) return null;
|
||||
|
||||
List<PsiElement> refs = StreamEx.of(ReferencesSearch.search(targetBuilder).findAll())
|
||||
.map(PsiReference::getElement)
|
||||
.remove(e -> PsiTreeUtil.isAncestor(targetBuilder, e, false) ||
|
||||
PsiTreeUtil.isAncestor(terminalBlock.getStreamSourceStatement(), e, false))
|
||||
.toList();
|
||||
|
||||
if (!canBeMadeNonFinal(targetBuilder, terminalBlock.getStreamSourceStatement())) return null;
|
||||
allowedReferencePlaces.add(afterLoopAppend);
|
||||
allowedReferencePlaces.add(beforeLoopAppend);
|
||||
|
||||
List<PsiElement> refs = StreamEx.of(ReferencesSearch.search(targetBuilder).findAll())
|
||||
.map(PsiReference::getElement)
|
||||
.remove(e -> PsiTreeUtil.isAncestor(targetBuilder, e, false) ||
|
||||
PsiTreeUtil.isAncestor(terminalBlock.getStreamSourceStatement(), e, false))
|
||||
.toList();
|
||||
|
||||
allowedReferencePlaces.add(afterLoopAppend);
|
||||
allowedReferencePlaces.add(beforeLoopAppend);
|
||||
|
||||
boolean allowed = areReferencesAllowed(refs, allowedReferencePlaces);
|
||||
if (!allowed) {
|
||||
PsiMethodCallExpression newAfterLoopAppend = tryExtractCombinedToString(afterLoopAppend, refs);
|
||||
if (newAfterLoopAppend == null) return null;
|
||||
afterLoopAppend = newAfterLoopAppend;
|
||||
boolean allowed = areReferencesAllowed(refs, allowedReferencePlaces);
|
||||
if (!allowed) {
|
||||
PsiMethodCallExpression newAfterLoopAppend = tryExtractCombinedToString(afterLoopAppend, refs);
|
||||
if (newAfterLoopAppend == null) return null;
|
||||
afterLoopAppend = newAfterLoopAppend;
|
||||
}
|
||||
builderStrInitializers = extractStringBuilderInitializer(targetBuilder.getInitializer());
|
||||
if(builderStrInitializers == null) return null;
|
||||
}
|
||||
PsiExpression builderStrInitializer = extractStringBuilderInitializer(targetBuilder.getInitializer());
|
||||
List<PsiExpression> prefixJoinParts = extractJoinParts(beforeLoopAppend);
|
||||
if (prefixJoinParts == null) return null;
|
||||
if (builderStrInitializer != null) {
|
||||
prefixJoinParts.add(0, builderStrInitializer);
|
||||
if(builderStrInitializers != null) {
|
||||
prefixJoinParts.addAll(0, builderStrInitializers);
|
||||
}
|
||||
if (prefixJoinParts.stream().anyMatch(joinPart -> SideEffectChecker.mayHaveSideEffects(joinPart))) return null;
|
||||
if (afterLoopAppend != null && VariableAccessUtils.variableIsUsed(targetBuilder, afterLoopAppend.getArgumentList())) return null;
|
||||
@@ -734,7 +756,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
* Joining without delimiter, but maybe with prefix and suffix
|
||||
*/
|
||||
private static class PlainJoiningTerminal extends JoiningTerminal {
|
||||
protected PlainJoiningTerminal(@NotNull PsiLocalVariable targetBuilder,
|
||||
protected PlainJoiningTerminal(@NotNull PsiVariable targetBuilder,
|
||||
@NotNull PsiVariable variable,
|
||||
@NotNull List<PsiExpression> mainJoinParts,
|
||||
@NotNull PrefixSuffixContext prefixSuffixContext,
|
||||
@@ -750,7 +772,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
List<PsiStatement> statements = Arrays.asList(terminalBlock.getStatements());
|
||||
List<PsiExpression> mainJoinParts = extractJoinParts(statements);
|
||||
if (mainJoinParts == null || mainJoinParts.isEmpty()) return null;
|
||||
PsiLocalVariable targetBuilder = extractStringBuilder(statements.get(0));
|
||||
PsiVariable targetBuilder = extractStringBuilder(statements.get(0));
|
||||
if (targetBuilder == null) return null;
|
||||
PsiStatement loop = terminalBlock.getStreamSourceStatement();
|
||||
PrefixSuffixContext context =
|
||||
@@ -765,7 +787,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
*/
|
||||
private static class LengthBasedJoiningTerminal extends JoiningTerminal {
|
||||
|
||||
protected LengthBasedJoiningTerminal(@NotNull PsiLocalVariable targetBuilder,
|
||||
protected LengthBasedJoiningTerminal(@NotNull PsiVariable targetBuilder,
|
||||
@NotNull PsiVariable variable,
|
||||
@NotNull List<PsiExpression> mainJoinParts,
|
||||
@NotNull PrefixSuffixContext prefixSuffixContext,
|
||||
@@ -789,8 +811,8 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
List<PsiExpression> delimiter = extractDelimiter(ifStatement);
|
||||
if (delimiter == null) return null;
|
||||
List<PsiStatement> withoutCondition = statements.subList(1, statements.size());
|
||||
PsiLocalVariable targetBuilder = extractStringBuilder(withoutCondition.get(0));
|
||||
if (targetBuilder == null) return null;
|
||||
PsiVariable targetBuilder = extractStringBuilder(withoutCondition.get(0));
|
||||
if(!(targetBuilder instanceof PsiLocalVariable)) return null;
|
||||
Integer conditionPrefixLength = extractConditionPrefixLength(condition, targetBuilder);
|
||||
if (conditionPrefixLength == null) return null;
|
||||
|
||||
@@ -824,7 +846,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
|
||||
private final @NotNull PsiVariable myBoolVariable;
|
||||
|
||||
protected BoolFlagJoiningTerminal(@NotNull PsiLocalVariable targetBuilder,
|
||||
protected BoolFlagJoiningTerminal(@NotNull PsiVariable targetBuilder,
|
||||
@NotNull PsiVariable variable,
|
||||
@NotNull List<PsiExpression> mainJoinParts,
|
||||
@NotNull PrefixSuffixContext prefixSuffixContext,
|
||||
@@ -864,7 +886,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
if (!joinPartsAreEquivalent(joinData.getMainJoinParts(), firstIterationJoinParts)) return null;
|
||||
|
||||
|
||||
PsiLocalVariable targetBuilder = extractStringBuilder(firstIterationStatements.get(0));
|
||||
PsiVariable targetBuilder = extractStringBuilder(firstIterationStatements.get(0));
|
||||
if (targetBuilder == null) return null;
|
||||
PsiStatement loop = terminalBlock.getStreamSourceStatement();
|
||||
PrefixSuffixContext context =
|
||||
@@ -884,7 +906,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
private static class LengthTruncateJoiningTerminal extends JoiningTerminal {
|
||||
private final @NotNull PsiIfStatement myTruncateIfStatement;
|
||||
|
||||
protected LengthTruncateJoiningTerminal(@NotNull PsiLocalVariable targetBuilder,
|
||||
protected LengthTruncateJoiningTerminal(@NotNull PsiVariable targetBuilder,
|
||||
@NotNull PsiVariable variable,
|
||||
@NotNull List<PsiExpression> mainJoinParts,
|
||||
@NotNull PrefixSuffixContext prefixSuffixContext,
|
||||
@@ -908,8 +930,8 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
if (nonFinalVariables != null && !nonFinalVariables.isEmpty()) return null;
|
||||
List<PsiStatement> statements = Arrays.asList(terminalBlock.getStatements());
|
||||
if (statements.size() < 1) return null;
|
||||
PsiLocalVariable targetBuilder = extractStringBuilder(statements.get(0));
|
||||
if (targetBuilder == null) return null;
|
||||
PsiVariable targetBuilder = extractStringBuilder(statements.get(0));
|
||||
if(!(targetBuilder instanceof PsiLocalVariable)) return null;
|
||||
List<PsiExpression> joinParts = extractJoinParts(statements);
|
||||
if (joinParts == null) return null;
|
||||
JoinData joinData = JoinData.extractRightDelimiter(joinParts);
|
||||
@@ -997,7 +1019,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
private static class DelimiterRewriteJoiningTerminal extends JoiningTerminal {
|
||||
private final @NotNull PsiVariable myDelimiterVariable;
|
||||
|
||||
protected DelimiterRewriteJoiningTerminal(@NotNull PsiLocalVariable targetBuilder,
|
||||
protected DelimiterRewriteJoiningTerminal(@NotNull PsiVariable targetBuilder,
|
||||
@NotNull PsiVariable variable,
|
||||
@NotNull List<PsiExpression> mainJoinParts,
|
||||
@NotNull PrefixSuffixContext prefixSuffixContext,
|
||||
@@ -1036,7 +1058,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
joinParts.remove(0);
|
||||
if (ReferencesSearch.search(delimiterVar, new LocalSearchScope(terminalBlock.getStatements())).findAll().size() != 2) return null;
|
||||
|
||||
PsiLocalVariable targetBuilder = extractStringBuilder(mainStatements.get(0));
|
||||
PsiVariable targetBuilder = extractStringBuilder(mainStatements.get(0));
|
||||
if (targetBuilder == null) return null;
|
||||
|
||||
PsiStatement loop = terminalBlock.getStreamSourceStatement();
|
||||
@@ -1092,7 +1114,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
*/
|
||||
private static class IndexBasedJoiningTerminal extends JoiningTerminal {
|
||||
|
||||
protected IndexBasedJoiningTerminal(@NotNull PsiLocalVariable targetBuilder,
|
||||
protected IndexBasedJoiningTerminal(@NotNull PsiVariable targetBuilder,
|
||||
@NotNull PsiVariable variable,
|
||||
@NotNull List<PsiExpression> mainJoinParts,
|
||||
@NotNull PrefixSuffixContext prefixSuffixContext,
|
||||
@@ -1121,7 +1143,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
if (!joinPartsAreEquivalent(joinData.getMainJoinParts(), firstIterationJoinParts)) return null;
|
||||
|
||||
|
||||
PsiLocalVariable targetBuilder = extractStringBuilder(firstIterationStatements.get(0));
|
||||
PsiVariable targetBuilder = extractStringBuilder(firstIterationStatements.get(0));
|
||||
if (targetBuilder == null) return null;
|
||||
PsiStatement loop = terminalBlock.getStreamSourceStatement();
|
||||
PrefixSuffixContext context =
|
||||
@@ -1144,7 +1166,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
@NotNull private final StreamApiMigrationInspection.CountingLoopSource mySource;
|
||||
@NotNull private final PsiStatement myBeforeLoopAppend;
|
||||
|
||||
protected CountedLoopJoiningTerminal(@NotNull PsiLocalVariable targetBuilder,
|
||||
protected CountedLoopJoiningTerminal(@NotNull PsiVariable targetBuilder,
|
||||
@NotNull PsiVariable variable,
|
||||
@NotNull List<PsiExpression> mainJoinParts,
|
||||
@NotNull PrefixSuffixContext prefixSuffixContext,
|
||||
@@ -1203,7 +1225,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
|
||||
PsiStatement loop = terminalBlock.getStreamSourceStatement();
|
||||
PsiLocalVariable variable = tryCast(terminalBlock.getVariable(), PsiLocalVariable.class);
|
||||
if (variable == null) return null;
|
||||
PsiLocalVariable targetBuilder = extractStringBuilder(statements.get(0));
|
||||
PsiVariable targetBuilder = extractStringBuilder(statements.get(0));
|
||||
if (targetBuilder == null) return null;
|
||||
PsiMethodCallExpression beforeLoopAppend = JoiningTerminal.getCallBeforeStatement(loop, targetBuilder, APPEND, emptyList());
|
||||
if (beforeLoopAppend == null) return null;
|
||||
|
||||
-1
@@ -60,7 +60,6 @@ import static com.siyeh.ig.psiutils.ControlFlowUtils.InitializerUsageStatus.UNKN
|
||||
public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
private static final Logger LOG = Logger.getInstance(StreamApiMigrationInspection.class);
|
||||
|
||||
|
||||
public boolean REPLACE_TRIVIAL_FOREACH;
|
||||
public boolean SUGGEST_FOREACH;
|
||||
private static final String SHORT_NAME = "Convert2streamapi";
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Test {
|
||||
static String test(List<String> list) {
|
||||
String sb;
|
||||
System.out.println("hello");
|
||||
sb = list.stream().filter(s -> !s.isEmpty()).map(String::trim).collect(Collectors.joining("", "1" + 2, ""));
|
||||
return sb.length() == 0 ? null : sb;
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Test {
|
||||
static String test(List<String> list, StringBuilder sb) {
|
||||
sb.append(list.stream().filter(s -> !s.isEmpty()).map(String::trim).collect(Collectors.joining()));
|
||||
return sb.length() == 0 ? null : sb.toString();
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Test {
|
||||
static String test(List<String> list) {
|
||||
StringBuilder sb = new StringBuilder().append("1").append(2);
|
||||
System.out.println("hello");
|
||||
for(String s : li<caret>st) {
|
||||
if(!s.isEmpty()) {
|
||||
sb.append(s.trim());
|
||||
}
|
||||
}
|
||||
return sb.length() == 0 ? null : sb.toString();
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Replace with collect" "true"
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Test {
|
||||
static String test(List<String> list, StringBuilder sb) {
|
||||
for(String s : li<caret>st) {
|
||||
if(!s.isEmpty()) {
|
||||
sb.append(s.trim());
|
||||
}
|
||||
}
|
||||
return sb.length() == 0 ? null : sb.toString();
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace Stream().filter().count() > 0 with stream.anyMatch()" "true"
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
class Test {
|
||||
long cnt() {
|
||||
/*c*/
|
||||
/*d*/
|
||||
return Arrays.asList("ds", "e", "fe")./*a*/stream(/*b*/).anyMatch(s -> s.length() > 1);
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Replace Stream().filter().count() > 0 with stream.anyMatch()" "true"
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
class Test {
|
||||
long cnt() {
|
||||
return Arrays.asList("ds", "e", "fe").stream().anyMatch(s -> s.length() > 1);
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Replace Stream().filter().count() == 0 with stream.noneMatch()" "true"
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
class Test {
|
||||
long cnt() {
|
||||
return Arrays.asList("ds", "e", "fe").stream().noneMatch(s -> s.length() > 1);
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Replace Stream().filter().count() > 0 with stream.anyMatch()" "true"
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
class Test {
|
||||
long cnt() {
|
||||
return Arrays.asList("ds", "e", "fe")./*a*/stream(/*b*/)/*c*/.filter(s -> s.length() > 1).c<caret>ount() > /*d*/0;
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Replace Stream().filter().count() > 0 with stream.anyMatch()" "true"
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
class Test {
|
||||
long cnt() {
|
||||
return 0 < Arrays.asList("ds", "e", "fe").stream().filter(s -> s.length() > 1).c<caret>ount();
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Replace Stream().filter().count() == 0 with stream.noneMatch()" "true"
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
class Test {
|
||||
long cnt() {
|
||||
return Arrays.asList("ds", "e", "fe").stream().filter(s -> s.length() > 1).c<caret>ount() == 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// "Replace with Stream.generate()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class Test {
|
||||
public void test(Object[] array) {
|
||||
Stream.generate(() -> new Object()).limit(10);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Replace with Stream.generate()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class Test {
|
||||
public void test() {
|
||||
IntStream.generate(() -> 42).limit(10).filter(x -> x > 12);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// "Replace with Stream.generate()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
class Test {
|
||||
public void test() {
|
||||
LongStream.generate(() -> 42l).limit(10).filter(x -> x > 12);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Replace with Arrays.stream()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class Test {
|
||||
public void test(double[] array) {
|
||||
Arrays.stream(array, 1, 5).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Replace with Arrays.stream()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class Test {
|
||||
public void test(int[] array) {
|
||||
Arrays.stream(array, 1, 5).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Replace with Arrays.stream()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class Test {
|
||||
public void test(int[] array) {
|
||||
/*a*/
|
||||
Arrays.stream(array, 1, 5)/*b*/.collect(Collectors.toList())/*c*/;/*d*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Replace with Arrays.stream()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class Test {
|
||||
public void test(Object[] array) {
|
||||
Arrays.stream(array, 1, 5).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// "Replace with min()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class StringComparator implements Comparator<String> {
|
||||
@Override
|
||||
public int compare(String o1, String o2) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class Test {
|
||||
public void test(String[] array) {
|
||||
StringComparator comparator = new StringComparator();
|
||||
Arrays.stream(array).min(comparator);
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// "Replace with max()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class StringComparator implements Comparator<String> {
|
||||
@Override
|
||||
public int compare(String o1, String o2) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class Test {
|
||||
public void test(String[] array) {
|
||||
StringComparator comparator = new StringComparator();
|
||||
Arrays.stream(array).max(comparator);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Replace with Stream.generate()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class Test {
|
||||
public void test(Object[] array) {
|
||||
Collections.nCopies(10, "").<caret>stream().map(x -> new Object());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Replace with Stream.generate()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class Test {
|
||||
public void test() {
|
||||
Collections.nCopies(10, "").<caret>stream().mapToInt(x -> 42).filter(x -> x > 12);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Replace with Stream.generate()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class Test {
|
||||
public void test() {
|
||||
Collections.nCopies(10, "").<caret>stream().mapToLong(x -> 42l).filter(x -> x > 12);
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace with Stream.generate()" "false"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class Test {
|
||||
public void test(Object[] array) {
|
||||
Collections.nCopies(10, new Object()).<caret>stream().map(x -> new Object());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Replace with Arrays.stream()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class Test {
|
||||
public void test(double[] array) {
|
||||
IntStream.<caret>range(1, 5).mapToDouble(x -> array[x]).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Replace with Arrays.stream()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class Test {
|
||||
public void test(int[] array) {
|
||||
IntStream.<caret>range(1, 5).map(x -> array[x]).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace with Arrays.stream()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class Test {
|
||||
public void test(int[] array) {
|
||||
IntStream.<caret>range(1, 5).map(x ->/*a*/ array[x])/*b*/.collect(Collectors.toList())/*c*/;/*d*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Replace with Arrays.stream()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class Test {
|
||||
public void test(Object[] array) {
|
||||
IntStream.<caret>range(1, 5).mapToObj(x -> array[x]).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace with Arrays.stream()" "false"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class Test {
|
||||
public void test(byte[] array) {
|
||||
IntStream.<caret>range(1, 5).mapToObj(x -> array[x]).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// "Replace with min()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class StringComparator implements Comparator<String> {
|
||||
@Override
|
||||
public int compare(String o1, String o2) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class Test {
|
||||
public void test(String[] array) {
|
||||
StringComparator comparator = new StringComparator();
|
||||
Arrays.stream(array).sorted(comparator).<caret>findFirst();
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// "Replace with max()" "true"
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
class StringComparator implements Comparator<String> {
|
||||
@Override
|
||||
public int compare(String o1, String o2) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class Test {
|
||||
public void test(String[] array) {
|
||||
StringComparator comparator = new StringComparator();
|
||||
Arrays.stream(array).sorted(comparator.reversed()).<caret>findFirst();
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ could be optimized.
|
||||
actually iterates over collection elements to count them while Collection.size() is much faster for most of collections.</li>
|
||||
<li><code>Stream.flatMap(Collection::stream).count()</code> → <code>Stream.mapToLong(Collection::size).sum()</code>. Similarly
|
||||
there's no need to iterate all the nested collections. Instead, their sizes could be summed up.</li>
|
||||
<li><code>collection.stream().filter(o -> ...).count() > 0</code> → <code>collection.stream().anyMatch(o -> ...)</code></li>
|
||||
<li><code>collection.stream().filter(o -> ...).count() == 0</code> → <code>collection.stream().noneMatch(o -> ...)</code></li>
|
||||
</ul>
|
||||
<small>New in 2016.3</small>
|
||||
</body>
|
||||
|
||||
@@ -25,6 +25,9 @@ It allows to avoid creating redundant temporary objects when traversing a collec
|
||||
<li><code>!stream.anyMatch()</code> → <code>stream.noneMatch()</code></li>
|
||||
<li><code>!stream.anyMatch(x -> !(...))</code> → <code>stream.allMatch()</code></li>
|
||||
<li><code>stream.map().anyMatch(Boolean::booleanValue)</code> -> <code>stream.anyMatch()</code></li>
|
||||
<li><code>IntStream.range(expr1, expr2).mapToObj(x -> array[x])</code> -> <code>Arrays.stream(array, expr1, expr2)</code></li>
|
||||
<li><code>Collection.nCopies(count, ...)</code> -> <code>Stream.generate().limit(count)</code></li>
|
||||
<li><code>stream.sorted(comparator).findFirst()</code> -> <code>Stream.min(comparator)</code></li>
|
||||
</ul>
|
||||
<p>
|
||||
Note that the replacements semantic may have minor difference in some cases.
|
||||
|
||||
Reference in New Issue
Block a user