Simplify Stream API call chain inspection improvements: IDEA-179118

This commit is contained in:
Roman
2017-09-27 09:43:29 +07:00
parent 1f3ec303dd
commit c20bbf70d2
36 changed files with 751 additions and 85 deletions
@@ -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();
}
}
}
@@ -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 + ")");
});
}
}
}
@@ -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;
@@ -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";
@@ -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;
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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;
}
}
@@ -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();
}
}
@@ -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());
}
}
@@ -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());
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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());
}
}
@@ -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());
}
}
@@ -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());
}
}
@@ -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();
}
}
@@ -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> &rarr; <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> &rarr; <code>collection.stream().anyMatch(o -> ...)</code></li>
<li><code>collection.stream().filter(o -> ...).count() == 0</code> &rarr; <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> &rarr; <code>stream.noneMatch()</code></li>
<li><code>!stream.anyMatch(x -> !(...))</code> &rarr; <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.