IDEA-163405 Migration from Stream API back to for loops: iteration#2

This commit is contained in:
Tagir Valeev
2016-11-24 18:15:51 +07:00
parent 16469a3b99
commit c2e815fc97
86 changed files with 2019 additions and 206 deletions
@@ -17,6 +17,7 @@ package com.intellij.codeInspection.streamToLoop;
import com.intellij.codeInspection.streamToLoop.StreamToLoopInspection.StreamToLoopReplacementContext;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.SuggestedNameInfo;
@@ -24,14 +25,16 @@ import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.refactoring.util.LambdaRefactoringUtil;
import com.intellij.util.ArrayUtil;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.MethodCallUtils;
import one.util.streamex.EntryStream;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.OptionalLong;
import java.util.*;
import java.util.function.Consumer;
/**
@@ -42,6 +45,16 @@ import java.util.function.Consumer;
abstract class FunctionHelper {
private static final Logger LOG = Logger.getInstance(FunctionHelper.class);
private String myResultType;
FunctionHelper(PsiType resultType) {
myResultType = resultType.getCanonicalText();
}
String getResultType() {
return myResultType;
}
String getText() {
return getExpression().getText();
}
@@ -55,7 +68,9 @@ abstract class FunctionHelper {
* @return SAM argument name or null if function helper refused to perform a transformation.
* @param type
*/
abstract String tryLightTransform(PsiType type);
String tryLightTransform(PsiType type) {
return null;
}
/**
* Perform an adaptation of current function helper to the replacement context with given parameter names.
@@ -73,12 +88,14 @@ abstract class FunctionHelper {
* @param newName new variable name
* @param context a context
*/
abstract void rename(String oldName, String newName, StreamToLoopReplacementContext context);
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {}
abstract void registerUsedNames(Consumer<String> consumer);
void registerUsedNames(Consumer<String> consumer) {}
@Nullable
abstract String getParameterName(int index);
String getParameterName(int index) {
return null;
}
void suggestVariableName(StreamVariable var, int index) {
String name = getParameterName(index);
@@ -87,36 +104,72 @@ abstract class FunctionHelper {
}
}
void suggestOutputNames(StreamVariable var) {}
List<String> suggestFinalOutputNames(StreamToLoopReplacementContext context, String desiredName, String worstCaseName) {
List<String> candidates = Arrays.asList(JavaCodeStyleManager.getInstance(context.getProject())
.suggestVariableName(VariableKind.LOCAL_VARIABLE, desiredName,
context.createExpression(getText()),
context.createType(getResultType())).names);
if(candidates.isEmpty() && worstCaseName != null) candidates = Collections.singletonList(worstCaseName);
return candidates;
}
@Contract("null, _ -> null")
@Nullable
static FunctionHelper create(PsiExpression expression, int paramCount) {
if(expression == null) return null;
PsiType type = expression instanceof PsiFunctionalExpression
? ((PsiFunctionalExpression)expression).getFunctionalInterfaceType()
: expression.getType();
if(!(type instanceof PsiClassType)) return null;
PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(type);
if (interfaceMethod == null || interfaceMethod.getParameterList().getParametersCount() != paramCount) return null;
PsiType returnType = interfaceMethod.getReturnType();
if (returnType == null) return null;
returnType = ((PsiClassType)type).resolveGenerics().getSubstitutor().substitute(returnType);
if (expression instanceof PsiLambdaExpression) {
PsiLambdaExpression lambda = (PsiLambdaExpression)expression;
PsiType functionalInterfaceType = lambda.getFunctionalInterfaceType();
if(functionalInterfaceType == null) return null;
PsiParameterList list = lambda.getParameterList();
if (list.getParametersCount() != paramCount) return null;
String[] parameters = StreamEx.of(list.getParameters()).map(PsiVariable::getName).toArray(String[]::new);
PsiExpression body = LambdaUtil.extractSingleExpressionFromBody(lambda.getBody());
if (body == null) return null;
return new LambdaFunctionHelper(body, parameters);
return new LambdaFunctionHelper(returnType, body, parameters);
}
if (expression instanceof PsiMethodReferenceExpression) {
PsiMethodReferenceExpression methodRef = (PsiMethodReferenceExpression)expression;
if (methodRef.resolve() == null) return null;
PsiType functionalInterfaceType = methodRef.getFunctionalInterfaceType();
PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
if (interfaceMethod == null) return null;
if (interfaceMethod.getParameterList().getParametersCount() != paramCount) return null;
return new MethodReferenceFunctionHelper(functionalInterfaceType, methodRef);
return new MethodReferenceFunctionHelper(returnType, type, methodRef);
}
if (expression instanceof PsiReferenceExpression && ExpressionUtils.isSimpleExpression(expression)) {
PsiType functionalInterfaceType = expression.getType();
PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
if (interfaceMethod == null || interfaceMethod.getParameterList().getParametersCount() != paramCount) return null;
return new SimpleReferenceFunctionHelper(expression, interfaceMethod.getName());
return new SimpleReferenceFunctionHelper(returnType, expression, interfaceMethod.getName());
}
return null;
if (expression instanceof PsiMethodCallExpression &&
MethodCallUtils
.isCallToStaticMethod((PsiMethodCallExpression)expression, CommonClassNames.JAVA_UTIL_FUNCTION_FUNCTION, "identity", 0)) {
return paramCount == 1 ? new IdentityFunctionHelper(returnType) : null;
}
return new ComplexExpressionFunctionHelper(returnType, type, interfaceMethod.getName(), expression);
}
@NotNull
@Contract(pure = true)
static FunctionHelper hashMapSupplier(PsiType type) {
return new FunctionHelper(type) {
PsiExpression myExpression;
@Override
PsiExpression getExpression() {
return myExpression;
}
@Override
void transform(StreamToLoopReplacementContext context, String... newNames) {
LOG.assertTrue(newNames.length == 0);
myExpression = context.createExpression("new java.util.HashMap<>()");
}
};
}
/**
@@ -165,7 +218,8 @@ abstract class FunctionHelper {
private PsiMethodReferenceExpression myMethodRef;
private PsiExpression myExpression;
public MethodReferenceFunctionHelper(PsiType functionalInterfaceType, PsiMethodReferenceExpression methodRef) {
public MethodReferenceFunctionHelper(PsiType returnType, PsiType functionalInterfaceType, PsiMethodReferenceExpression methodRef) {
super(returnType);
myMethodRef = methodRef;
myType = functionalInterfaceType.getCanonicalText();
PsiExpression qualifier = methodRef.getQualifierExpression();
@@ -258,11 +312,61 @@ abstract class FunctionHelper {
qualifier = renameVarReference(qualifier, oldName, newName, context);
myMethodRef = fromText(context, qualifier.getText()+"::"+myMethodRef.getReferenceName());
}
}
private static class ComplexExpressionFunctionHelper extends FunctionHelper {
private final String myMethodName;
private final String myNameCandidate;
private final String myFnType;
private PsiExpression myExpression;
private PsiExpression myFinalExpression;
private ComplexExpressionFunctionHelper(PsiType type, PsiType functionalInterface, String name, PsiExpression expression) {
super(type);
myMethodName = name;
myExpression = expression;
myNameCandidate = getNameCandidate(functionalInterface);
myFnType = functionalInterface.getCanonicalText();
}
private String getNameCandidate(PsiType functionalInterface) {
PsiElement parent = myExpression.getParent();
if(parent instanceof PsiExpressionList) {
int idx = ArrayUtil.indexOf(((PsiExpressionList)parent).getExpressions(), myExpression);
PsiElement gParent = parent.getParent();
if(gParent instanceof PsiMethodCallExpression && idx >= 0) {
PsiMethod method = ((PsiMethodCallExpression)gParent).resolveMethod();
if(method != null) {
PsiParameter[] parameters = method.getParameterList().getParameters();
if(idx < parameters.length) {
return parameters[idx].getName();
}
}
}
}
return functionalInterface.getPresentableText().toLowerCase(Locale.ENGLISH);
}
@Override
@Nullable
String getParameterName(int index) {
return null;
PsiExpression getExpression() {
LOG.assertTrue(myFinalExpression != null);
return myFinalExpression;
}
@Override
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
myExpression = renameVarReference(myExpression, oldName, newName, context);
}
@Override
void registerUsedNames(Consumer<String> consumer) {
processUsedNames(myExpression, consumer);
}
@Override
void transform(StreamToLoopReplacementContext context, String... newNames) {
String varName = context.declare(myNameCandidate, myFnType, myExpression.getText());
myFinalExpression = context.createExpression(varName + "." + myMethodName + "(" + String.join(",", newNames) + ")");
}
}
@@ -271,16 +375,12 @@ abstract class FunctionHelper {
private final String myName;
private PsiExpression myExpression;
public SimpleReferenceFunctionHelper(PsiExpression reference, String methodName) {
public SimpleReferenceFunctionHelper(PsiType returnType, PsiExpression reference, String methodName) {
super(returnType);
myReference = reference;
myName = methodName;
}
@Override
String tryLightTransform(PsiType type) {
return null;
}
@Override
PsiExpression getExpression() {
LOG.assertTrue(myExpression != null);
@@ -301,11 +401,25 @@ abstract class FunctionHelper {
void registerUsedNames(Consumer<String> consumer) {
processUsedNames(myReference, consumer);
}
}
private static class IdentityFunctionHelper extends FunctionHelper {
private PsiExpression myExpression;
public IdentityFunctionHelper(PsiType type) {
super(type);
}
@Nullable
@Override
String getParameterName(int index) {
return null;
PsiExpression getExpression() {
LOG.assertTrue(myExpression != null);
return myExpression;
}
@Override
void transform(StreamToLoopReplacementContext context, String... newNames) {
LOG.assertTrue(newNames.length == 1);
myExpression = context.createExpression(newNames[0]);
}
}
@@ -313,7 +427,8 @@ abstract class FunctionHelper {
private String[] myParameters;
private PsiExpression myBody;
LambdaFunctionHelper(PsiExpression body, String[] parameters) {
LambdaFunctionHelper(PsiType returnType, PsiExpression body, String[] parameters) {
super(returnType);
myParameters = parameters;
myBody = body;
}
@@ -365,5 +480,17 @@ abstract class FunctionHelper {
String getParameterName(int index) {
return myParameters[index];
}
@Override
void suggestOutputNames(StreamVariable var) {
String text = "("+var.getType()+")"+getText();
Project project = myBody.getProject();
PsiExpression expr = JavaPsiFacade.getElementFactory(project).createExpressionFromText(text, myBody);
SuggestedNameInfo info =
JavaCodeStyleManager.getInstance(project).suggestVariableName(VariableKind.LOCAL_VARIABLE, null, expr, null, true);
for (String name : info.names) {
var.addOtherNameCandidate(name);
}
}
}
}
@@ -46,10 +46,12 @@ abstract class Operation {
String code,
StreamToLoopReplacementContext context);
void registerUsedNames(Consumer<String> usedNameConsumer) {
Operation combineWithNext(Operation next) {
return null;
}
public void registerUsedNames(Consumer<String> usedNameConsumer) {}
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {}
@Nullable
@@ -98,7 +100,7 @@ abstract class Operation {
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
public void registerUsedNames(Consumer<String> usedNameConsumer) {
myFn.registerUsedNames(usedNameConsumer);
}
@@ -148,6 +150,12 @@ abstract class Operation {
super(fn);
}
@Override
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
super.suggestNames(inVar, outVar);
myFn.suggestOutputNames(outVar);
}
@Override
String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
return outVar.getDeclaration() + " = " + myFn.getText() + ";\n" + code;
@@ -201,7 +209,7 @@ abstract class Operation {
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
public void registerUsedNames(Consumer<String> usedNameConsumer) {
myRecords.forEach(or -> or.myOperation.registerUsedNames(usedNameConsumer));
}
@@ -260,7 +268,7 @@ abstract class Operation {
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
public void registerUsedNames(Consumer<String> usedNameConsumer) {
FunctionHelper.processUsedNames(myExpression, usedNameConsumer);
}
@@ -272,25 +280,25 @@ abstract class Operation {
}
static class LimitOperation extends Operation {
PsiExpression myExpression;
PsiExpression myLimit;
LimitOperation(PsiExpression expression) {
myExpression = expression;
myLimit = expression;
}
@Override
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
myExpression = FunctionHelper.renameVarReference(myExpression, oldName, newName, context);
myLimit = FunctionHelper.renameVarReference(myLimit, oldName, newName, context);
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
FunctionHelper.processUsedNames(myExpression, usedNameConsumer);
public void registerUsedNames(Consumer<String> usedNameConsumer) {
FunctionHelper.processUsedNames(myLimit, usedNameConsumer);
}
@Override
String wrap(StreamVariable inVar, StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
String limit = context.declare("limit", "long", myExpression.getText());
String limit = context.declare("limit", "long", myLimit.getText());
return "if(" + limit + "--==0) " + context.getBreakStatement() + code;
}
}
@@ -82,7 +82,7 @@ abstract class SourceOperation extends Operation {
if (name.equals("generate") && args.length == 1 && method.getModifierList().hasExplicitModifier(
PsiModifier.STATIC) && className.startsWith("java.util.stream.")) {
FunctionHelper fn = FunctionHelper.create(args[0], 0);
return fn == null ? null : new GenerateSource(fn);
return fn == null ? null : new GenerateSource(fn, null);
}
if (name.equals("iterate") && args.length == 2 && method.getModifierList().hasExplicitModifier(
PsiModifier.STATIC) && className.startsWith("java.util.stream.")) {
@@ -113,7 +113,7 @@ abstract class SourceOperation extends Operation {
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
public void registerUsedNames(Consumer<String> usedNameConsumer) {
processUsedNames(myQualifier, usedNameConsumer);
}
@@ -150,7 +150,7 @@ abstract class SourceOperation extends Operation {
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
public void registerUsedNames(Consumer<String> usedNameConsumer) {
for(PsiExpression arg : myArgList) {
processUsedNames(arg, usedNameConsumer);
}
@@ -170,26 +170,47 @@ abstract class SourceOperation extends Operation {
static class GenerateSource extends SourceOperation {
private FunctionHelper myFn;
private PsiExpression myLimit;
GenerateSource(FunctionHelper fn) {
GenerateSource(FunctionHelper fn, PsiExpression limit) {
myFn = fn;
myLimit = limit;
}
@Override
Operation combineWithNext(Operation next) {
if(myLimit == null && next instanceof LimitOperation) {
return new GenerateSource(myFn, ((LimitOperation)next).myLimit);
}
return null;
}
@Override
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
myFn.rename(oldName, newName, context);
if(myLimit != null) {
myLimit = renameVarReference(myLimit, oldName, newName, context);
}
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
public void registerUsedNames(Consumer<String> usedNameConsumer) {
myFn.registerUsedNames(usedNameConsumer);
if(myLimit != null) {
processUsedNames(myLimit, usedNameConsumer);
}
}
@Override
String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
myFn.transform(context);
String loop = "while(true)";
if(myLimit != null) {
String loopIdx = context.registerVarName(Arrays.asList("count", "limit"));
loop = "for(long "+loopIdx+"="+myLimit.getText()+";"+loopIdx+">0;"+loopIdx+"--)";
}
return context.getLoopLabel() +
"while(true) {\n" +
loop+"{\n" +
outVar.getDeclaration() + "=" + myFn.getText() + ";\n" + code +
"}\n";
}
@@ -211,7 +232,7 @@ abstract class SourceOperation extends Operation {
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
public void registerUsedNames(Consumer<String> usedNameConsumer) {
processUsedNames(myInitializer, usedNameConsumer);
myFn.registerUsedNames(usedNameConsumer);
}
@@ -248,7 +269,7 @@ abstract class SourceOperation extends Operation {
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
public void registerUsedNames(Consumer<String> usedNameConsumer) {
processUsedNames(myOrigin, usedNameConsumer);
processUsedNames(myBound, usedNameConsumer);
}
@@ -20,6 +20,7 @@ import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.codeInspection.util.OptionalUtil;
import com.intellij.lang.java.lexer.JavaLexer;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
@@ -27,11 +28,9 @@ import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.PsiDiamondTypeUtil;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.psi.util.*;
import com.siyeh.ig.psiutils.StreamApiUtil;
import one.util.streamex.IntStreamEx;
import one.util.streamex.StreamEx;
@@ -53,7 +52,7 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
// To quickly filter out most of the non-interesting method calls
private static final Set<String> SUPPORTED_TERMINALS = StreamEx.of("count", "sum", "summaryStatistics", "reduce", "collect",
"findFirst", "findAny", "anyMatch", "allMatch", "noneMatch",
"toArray", "average", "forEach", "forEachOrdered").toSet();
"toArray", "average", "forEach", "forEachOrdered", "min", "max").toSet();
@NotNull
@Override
@@ -157,9 +156,17 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
List<OperationRecord> operations = new ArrayList<>();
PsiMethodCallExpression currentCall = terminalCall;
StreamVariable lastVar = outVar;
Operation next = null;
while(true) {
Operation op = createOperationFromCall(lastVar, currentCall);
if(op == null) return null;
if(next != null) {
Operation combined = op.combineWithNext(next);
if (combined != null) {
op = combined;
operations.remove(operations.size() - 1);
}
}
OperationRecord or = new OperationRecord();
or.myOperation = op;
or.myOutVar = lastVar;
@@ -175,9 +182,10 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
if(op.changesVariable()) {
PsiType type = StreamApiUtil.getStreamElementType(currentCall.getType());
if(type == null) return null;
lastVar = new StreamVariable(type);
lastVar = new StreamVariable(type.getCanonicalText());
}
or.myInVar = lastVar;
next = op;
}
}
@@ -237,7 +245,7 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
temporaryStreamPlaceholder.delete();
}
else {
temporaryStreamPlaceholder.replace(factory.createExpressionFromText(finisher, temporaryStreamPlaceholder));
normalize(project, temporaryStreamPlaceholder.replace(factory.createExpressionFromText(finisher, temporaryStreamPlaceholder)));
}
}
catch (Exception ex) {
@@ -250,9 +258,15 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
}
}
void addStatement(@NotNull Project project, PsiStatement statement, PsiStatement context) {
CodeStyleManager.getInstance(project)
.reformat(JavaCodeStyleManager.getInstance(project).shortenClassReferences(statement.getParent().addBefore(context, statement)));
private static void addStatement(@NotNull Project project, PsiStatement statement, PsiStatement context) {
PsiElement element = statement.getParent().addBefore(context, statement);
normalize(project, element);
}
private static void normalize(@NotNull Project project, PsiElement element) {
element = JavaCodeStyleManager.getInstance(project).shortenClassReferences(element);
PsiDiamondTypeUtil.removeRedundantTypeArguments(element);
CodeStyleManager.getInstance(project).reformat(element);
}
@Nullable
@@ -381,6 +395,10 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
return found + " = " +foundValue+";\n" + getBreakStatement();
}
public void addInitStep(String initStatement) {
myDeclarations.add(initStatement);
}
public String declareResult(String desiredName, String type, String initializer) {
String name = registerVarName(Arrays.asList(desiredName, "result"));
myDeclarations.add(type + " " + name + " = " + initializer + ";");
@@ -399,6 +417,12 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
myFinisher = finisher;
}
public void setOptionalUnwrapperFinisher(String seenVariable, String accVariable, String type) {
String optionalClass = OptionalUtil.getOptionalClass(type);
setFinisher("(" + seenVariable + "?" + optionalClass + ".of(" + accVariable + "):" + optionalClass +
"." + (TypeConversionUtil.isPrimitive(type) ? "" : "<" + type + ">") + "empty())");
}
public Project getProject() {
return myStatement.getProject();
}
@@ -17,7 +17,6 @@ package com.intellij.codeInspection.streamToLoop;
import com.intellij.codeInspection.streamToLoop.StreamToLoopInspection.StreamToLoopReplacementContext;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.PsiType;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import one.util.streamex.StreamEx;
@@ -39,7 +38,7 @@ import java.util.List;
class StreamVariable {
private static final Logger LOG = Logger.getInstance(StreamVariable.class);
static StreamVariable STUB = new StreamVariable(PsiType.VOID) {
static StreamVariable STUB = new StreamVariable("") {
@Override
public void addBestNameCandidate(String candidate) {
}
@@ -60,8 +59,13 @@ class StreamVariable {
private Collection<String> myBestCandidates = new LinkedHashSet<>();
private Collection<String> myOtherCandidates = new LinkedHashSet<>();
StreamVariable(@NotNull PsiType type) {
myType = type.getCanonicalText();
StreamVariable(@NotNull String type) {
myType = type;
}
StreamVariable(@NotNull String type, @NotNull String name) {
myType = type;
myName = name;
}
/**
@@ -18,13 +18,20 @@ package com.intellij.codeInspection.streamToLoop;
import com.intellij.codeInspection.streamToLoop.StreamToLoopInspection.StreamToLoopReplacementContext;
import com.intellij.codeInspection.util.OptionalUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.siyeh.ig.psiutils.BoolUtils;
import com.siyeh.ig.psiutils.ExpressionUtils;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* @author Tagir Valeev
@@ -45,6 +52,10 @@ abstract class TerminalOperation extends Operation {
return true;
}
CollectorOperation asCollector() {
return null;
}
abstract String generate(StreamVariable inVar, StreamToLoopReplacementContext context);
@Nullable
@@ -61,19 +72,18 @@ abstract class TerminalOperation extends Operation {
return new AccumulatedTerminalOperation("count", "long", "0", "{acc}++;");
}
if(name.equals("sum") && args.length == 0) {
return new AccumulatedTerminalOperation("sum", resultType.getCanonicalText(), "0", "{acc}+={item};");
return AccumulatedTerminalOperation.summing(resultType);
}
if(name.equals("average") && args.length == 0) {
if(elementType.equals(PsiType.DOUBLE)) {
return new AverageTerminalOperation(true);
return new AverageTerminalOperation(true, true);
}
else if(elementType.equals(PsiType.INT) || elementType.equals(PsiType.LONG)) {
return new AverageTerminalOperation(false);
return new AverageTerminalOperation(false, true);
}
}
if(name.equals("summaryStatistics") && args.length == 0) {
return new AccumulatedTerminalOperation("stat", resultType.getCanonicalText(), "new " + resultType.getCanonicalText() + "()",
"{acc}.accept({item});");
return AccumulatedTerminalOperation.summarizing(resultType);
}
if((name.equals("findFirst") || name.equals("findAny")) && args.length == 0) {
return new FindTerminalOperation(resultType.getCanonicalText());
@@ -90,11 +100,7 @@ abstract class TerminalOperation extends Operation {
}
}
if(args.length == 1) {
PsiType optionalElementType = OptionalUtil.getOptionalElementType(resultType);
FunctionHelper fn = FunctionHelper.create(args[0], 2);
if(fn != null && optionalElementType != null) {
return new ReduceToOptionalTerminalOperation(fn, optionalElementType.getCanonicalText());
}
return ReduceToOptionalTerminalOperation.create(args[0], resultType);
}
}
if(name.equals("toArray") && args.length < 2) {
@@ -120,60 +126,173 @@ abstract class TerminalOperation extends Operation {
"{acc}.toArray("+arr+")");
}
}
if(name.equals("collect") && args.length == 3) {
FunctionHelper supplier = FunctionHelper.create(args[0], 0);
if(supplier == null) return null;
FunctionHelper accumulator = FunctionHelper.create(args[1], 2);
if(accumulator == null) return null;
return new ExplicitCollectTerminalOperation(supplier, accumulator, resultType.getCanonicalText());
if ((name.equals("max") || name.equals("min")) && args.length < 2) {
return MinMaxTerminalOperation.create(args.length == 1 ? args[0] : null, elementType.getCanonicalText(), name.equals("max"));
}
if(name.equals("collect") && args.length == 1) {
if(args[0] instanceof PsiMethodCallExpression) {
PsiMethodCallExpression collectorCall = (PsiMethodCallExpression)args[0];
PsiExpression[] collectorArgs = collectorCall.getArgumentList().getExpressions();
PsiMethod collector = collectorCall.resolveMethod();
if(collector == null) return null;
PsiClass collectorClass = collector.getContainingClass();
if(collectorClass != null && CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS.equals(collectorClass.getQualifiedName())) {
if(collector.getName().equals("toList") && collectorArgs.length == 0) {
return AccumulatedTerminalOperation.toCollection(resultType, CommonClassNames.JAVA_UTIL_ARRAY_LIST, "list");
}
if(collector.getName().equals("toSet") && collectorArgs.length == 0) {
return AccumulatedTerminalOperation.toCollection(resultType, CommonClassNames.JAVA_UTIL_HASH_SET, "set");
}
if(collector.getName().equals("toCollection") && collectorArgs.length == 1) {
FunctionHelper fn = FunctionHelper.create(collectorArgs[0], 0);
if(fn != null) {
return new ToCollectionTerminalOperation(fn, resultType);
}
}
if(collector.getName().equals("reducing") && collectorArgs.length == 2) {
FunctionHelper fn = FunctionHelper.create(collectorArgs[1], 2);
if(fn != null) {
return new ReduceTerminalOperation(collectorArgs[0], fn, resultType.getCanonicalText());
}
}
if(collector.getName().equals("reducing") && collectorArgs.length == 1) {
PsiType optionalElementType = OptionalUtil.getOptionalElementType(resultType);
FunctionHelper fn = FunctionHelper.create(collectorArgs[0], 2);
if(fn != null && optionalElementType != null) {
return new ReduceToOptionalTerminalOperation(fn, optionalElementType.getCanonicalText());
}
}
if(collector.getName().equals("joining")) {
if(collectorArgs.length == 0) {
return new AccumulatedTerminalOperation("sb", CommonClassNames.JAVA_LANG_STRING_BUILDER,
"new " + CommonClassNames.JAVA_LANG_STRING_BUILDER + "()", "{acc}.append({item});",
"{acc}.toString()");
}
if(collectorArgs.length == 1 || collectorArgs.length == 3) {
String initializer = "new java.util.StringJoiner(" + StreamEx.of(collectorArgs).map(PsiElement::getText).joining(",") + ")";
return new AccumulatedTerminalOperation("joiner", "java.util.StringJoiner", initializer,
"{acc}.add({item});", "{acc}.toString()");
}
}
}
if (name.equals("collect")) {
if (args.length == 3) {
FunctionHelper supplier = FunctionHelper.create(args[0], 0);
if (supplier == null) return null;
FunctionHelper accumulator = FunctionHelper.create(args[1], 2);
if (accumulator == null) return null;
return new ExplicitCollectTerminalOperation(supplier, accumulator);
}
if (args.length == 1) {
return fromCollector(elementType.getCanonicalText(), resultType, args[0]);
}
}
return null;
}
@Contract("_, _, null -> null")
@Nullable
private static TerminalOperation fromCollector(@NotNull String elementType, @NotNull PsiType resultType, PsiExpression expr) {
if (!(expr instanceof PsiMethodCallExpression)) return null;
PsiMethodCallExpression collectorCall = (PsiMethodCallExpression)expr;
PsiExpression[] collectorArgs = collectorCall.getArgumentList().getExpressions();
PsiMethod collector = collectorCall.resolveMethod();
if (collector == null) return null;
PsiClass collectorClass = collector.getContainingClass();
if (collectorClass != null && CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS.equals(collectorClass.getQualifiedName())) {
return fromCollector(elementType, resultType, collector, collectorArgs);
}
return null;
}
@Nullable
private static TerminalOperation fromCollector(@NotNull String elementType,
@NotNull PsiType resultType,
PsiMethod collector,
PsiExpression[] collectorArgs) {
String collectorName = collector.getName();
FunctionHelper fn;
switch (collectorName) {
case "toList":
if (collectorArgs.length != 0) return null;
return AccumulatedTerminalOperation.toList(resultType);
case "toSet":
if (collectorArgs.length != 0) return null;
return AccumulatedTerminalOperation.toCollection(resultType, CommonClassNames.JAVA_UTIL_HASH_SET, "set");
case "toCollection":
if (collectorArgs.length != 1) return null;
fn = FunctionHelper.create(collectorArgs[0], 0);
return fn == null ? null : new ToCollectionTerminalOperation(fn);
case "toMap": {
if (collectorArgs.length < 2 || collectorArgs.length > 4) return null;
FunctionHelper key = FunctionHelper.create(collectorArgs[0], 1);
FunctionHelper value = FunctionHelper.create(collectorArgs[1], 1);
if(key == null || value == null) return null;
PsiExpression merger = collectorArgs.length > 2 ? collectorArgs[2] : null;
FunctionHelper supplier = collectorArgs.length == 4
? FunctionHelper.create(collectorArgs[3], 0)
: FunctionHelper.hashMapSupplier(resultType);
if(supplier == null) return null;
return new ToMapTerminalOperation(key, value, merger, supplier, resultType);
}
case "reducing":
switch (collectorArgs.length) {
case 1:
return ReduceToOptionalTerminalOperation.create(collectorArgs[0], resultType);
case 2:
fn = FunctionHelper.create(collectorArgs[1], 2);
return fn == null ? null : new ReduceTerminalOperation(collectorArgs[0], fn, resultType.getCanonicalText());
case 3:
FunctionHelper mapper = FunctionHelper.create(collectorArgs[1], 1);
fn = FunctionHelper.create(collectorArgs[2], 2);
return fn == null || mapper == null
? null
: new MappingTerminalOperation(mapper, new ReduceTerminalOperation(collectorArgs[0], fn, resultType.getCanonicalText()));
}
return null;
case "counting":
if (collectorArgs.length != 0) return null;
return new AccumulatedTerminalOperation("count", "long", "0", "{acc}++;");
case "summingInt":
case "summingLong":
case "summingDouble": {
if (collectorArgs.length != 1) return null;
fn = FunctionHelper.create(collectorArgs[0], 1);
PsiPrimitiveType type = PsiPrimitiveType.getUnboxedType(resultType);
return fn == null || type == null ? null : new InlineMappingTerminalOperation(fn, AccumulatedTerminalOperation.summing(type));
}
case "summarizingInt":
case "summarizingLong":
case "summarizingDouble": {
if (collectorArgs.length != 1) return null;
fn = FunctionHelper.create(collectorArgs[0], 1);
return fn == null ? null : new InlineMappingTerminalOperation(fn, AccumulatedTerminalOperation.summarizing(resultType));
}
case "averagingInt":
case "averagingLong":
case "averagingDouble": {
if (collectorArgs.length != 1) return null;
fn = FunctionHelper.create(collectorArgs[0], 1);
return fn == null
? null
: new InlineMappingTerminalOperation(fn, new AverageTerminalOperation(collectorName.equals("averagingDouble"), false));
}
case "mapping": {
if (collectorArgs.length != 2) return null;
fn = FunctionHelper.create(collectorArgs[0], 1);
if (fn == null) return null;
TerminalOperation downstreamOp = fromCollector(fn.getResultType(), resultType, collectorArgs[1]);
return downstreamOp == null ? null : new MappingTerminalOperation(fn, downstreamOp);
}
case "groupingBy":
case "partitioningBy": {
if (collectorArgs.length == 0 || collectorArgs.length > 3
|| collectorArgs.length == 3 && collectorName.equals("partitioningBy")) return null;
fn = FunctionHelper.create(collectorArgs[0], 1);
if (fn == null) return null;
if (!(resultType instanceof PsiClassType)) return null;
PsiClass aClass = ((PsiClassType)resultType).resolve();
if (aClass == null) return null;
PsiSubstitutor substitutor = ((PsiClassType)resultType).resolveGenerics().getSubstitutor();
PsiClass mapClass =
JavaPsiFacade.getInstance(aClass.getProject()).findClass(CommonClassNames.JAVA_UTIL_MAP, aClass.getResolveScope());
if (mapClass == null) return null;
PsiTypeParameter[] parameters = mapClass.getTypeParameters();
if (parameters.length != 2) return null;
PsiType resultSubType = substitutor.substitute(parameters[1]);
if (resultSubType == null) return null;
CollectorOperation downstreamCollector;
if (collectorArgs.length == 1) {
downstreamCollector = AccumulatedTerminalOperation.toList(resultSubType).asCollector();
}
else {
PsiExpression downstream = collectorArgs[collectorArgs.length - 1];
TerminalOperation downstreamOp = fromCollector(elementType, resultSubType, downstream);
if (downstreamOp == null) return null;
downstreamCollector = downstreamOp.asCollector();
}
if (downstreamCollector == null) return null;
if (collectorName.equals("partitioningBy")) {
return new PartitionByTerminalOperation(fn, resultType, downstreamCollector);
}
FunctionHelper supplier = collectorArgs.length == 3
? FunctionHelper.create(collectorArgs[1], 0)
: FunctionHelper.hashMapSupplier(resultType);
return new GroupByTerminalOperation(fn, supplier, resultType, downstreamCollector);
}
case "minBy":
case "maxBy":
if (collectorArgs.length != 1) return null;
return MinMaxTerminalOperation.create(collectorArgs[0], elementType, collectorName.equals("maxBy"));
case "joining":
switch (collectorArgs.length) {
case 0:
return new AccumulatedTerminalOperation("sb", CommonClassNames.JAVA_LANG_STRING_BUILDER,
"new " + CommonClassNames.JAVA_LANG_STRING_BUILDER + "()",
"{acc}.append({item});",
"{acc}.toString()");
case 1:
case 3:
String initializer =
"new java.util.StringJoiner(" + StreamEx.of(collectorArgs).map(PsiElement::getText).joining(",") + ")";
return new AccumulatedTerminalOperation("joiner", "java.util.StringJoiner", initializer,
"{acc}.add({item});", "{acc}.toString()");
}
return null;
}
return null;
}
@@ -190,7 +309,7 @@ abstract class TerminalOperation extends Operation {
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
public void registerUsedNames(Consumer<String> usedNameConsumer) {
FunctionHelper.processUsedNames(myIdentity, usedNameConsumer);
myUpdater.registerUsedNames(usedNameConsumer);
}
@@ -213,7 +332,7 @@ abstract class TerminalOperation extends Operation {
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
public void registerUsedNames(Consumer<String> usedNameConsumer) {
myUpdater.registerUsedNames(usedNameConsumer);
}
@@ -222,8 +341,7 @@ abstract class TerminalOperation extends Operation {
String seen = context.declare("seen", "boolean", "false");
String accumulator = context.declareResult("acc", myType, TypeConversionUtil.isPrimitive(myType) ? "0" : "null");
myUpdater.transform(context, accumulator, inVar.getName());
String optionalClass = OptionalUtil.getOptionalClass(myType);
context.setFinisher("(" + seen + "?" + optionalClass + ".of(" + accumulator + "):" + optionalClass + ".empty())");
context.setOptionalUnwrapperFinisher(seen, accumulator, myType);
return "if(!" + seen + ") {\n" +
seen + "=true;\n" +
accumulator + "=" + inVar + ";\n" +
@@ -231,21 +349,29 @@ abstract class TerminalOperation extends Operation {
accumulator + "=" + myUpdater.getText() + ";\n" +
"}\n";
}
@Nullable
static ReduceToOptionalTerminalOperation create(PsiExpression arg, PsiType resultType) {
PsiType optionalElementType = OptionalUtil.getOptionalElementType(resultType);
FunctionHelper fn = FunctionHelper.create(arg, 2);
if(fn != null && optionalElementType != null) {
return new ReduceToOptionalTerminalOperation(fn, optionalElementType.getCanonicalText());
}
return null;
}
}
static class ExplicitCollectTerminalOperation extends TerminalOperation {
private final FunctionHelper mySupplier;
private final FunctionHelper myAccumulator;
private final String myResultType;
public ExplicitCollectTerminalOperation(FunctionHelper supplier, FunctionHelper accumulator, String resultType) {
public ExplicitCollectTerminalOperation(FunctionHelper supplier, FunctionHelper accumulator) {
mySupplier = supplier;
myAccumulator = accumulator;
myResultType = resultType;
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
public void registerUsedNames(Consumer<String> usedNameConsumer) {
mySupplier.registerUsedNames(usedNameConsumer);
myAccumulator.registerUsedNames(usedNameConsumer);
}
@@ -258,26 +384,32 @@ abstract class TerminalOperation extends Operation {
@Override
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
mySupplier.transform(context);
String candidate = myAccumulator.getParameterName(0);
String acc = context.declareResult(candidate == null ? "acc" : candidate, myResultType, mySupplier.getText());
String candidate = mySupplier.suggestFinalOutputNames(context, myAccumulator.getParameterName(0), "acc").get(0);
String acc = context.declareResult(candidate, mySupplier.getResultType(), mySupplier.getText());
myAccumulator.transform(context, acc, inVar.getName());
return myAccumulator.getText()+";\n";
}
}
static class AverageTerminalOperation extends TerminalOperation {
private boolean myDoubleAccumulator;
private final boolean myDoubleAccumulator;
private final boolean myUseOptional;
public AverageTerminalOperation(boolean doubleAccumulator) {
public AverageTerminalOperation(boolean doubleAccumulator, boolean useOptional) {
myDoubleAccumulator = doubleAccumulator;
myUseOptional = useOptional;
}
@Override
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
String sum = context.declareResult("sum", myDoubleAccumulator ? "double" : "long", "0");
String count = context.declare("count", "long", "0");
context.setFinisher("("+count+"==0?java.util.OptionalDouble.empty():"
+"java.util.OptionalDouble.of("+(myDoubleAccumulator?"":"(double)")+sum+"/"+count+"))");
String emptyCheck = count + "==0";
String result = (myDoubleAccumulator ? "" : "(double)") + sum + "/" + count;
context.setFinisher(myUseOptional
? "(" + emptyCheck + "?java.util.OptionalDouble.empty():"
+ "java.util.OptionalDouble.of(" + result + "))"
: "(" + emptyCheck + "?0.0:" + result + ")");
return sum + "+=" + inVar + ";\n" + count + "++;\n";
}
}
@@ -343,7 +475,7 @@ abstract class TerminalOperation extends Operation {
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
public void registerUsedNames(Consumer<String> usedNameConsumer) {
myFn.registerUsedNames(usedNameConsumer);
}
@@ -369,7 +501,56 @@ abstract class TerminalOperation extends Operation {
}
}
static class AccumulatedTerminalOperation extends TerminalOperation {
interface CollectorOperation {
// Non-trivial finishers are not supported
default void transform(StreamToLoopReplacementContext context, String item) {}
default void suggestNames(StreamVariable inVar, StreamVariable outVar) {}
default void registerUsedNames(Consumer<String> usedNameConsumer) {}
String getSupplier();
String getAccumulator(String acc, String item);
}
abstract static class CollectorBasedTerminalOperation extends TerminalOperation implements CollectorOperation {
final String myType;
final Function<StreamToLoopReplacementContext, String> myAccNameSupplier;
final FunctionHelper mySupplier;
CollectorBasedTerminalOperation(String type, Function<StreamToLoopReplacementContext, String> accNameSupplier,
FunctionHelper accSupplier) {
myType = type;
myAccNameSupplier = accNameSupplier;
mySupplier = accSupplier;
}
@Override
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
transform(context, inVar.getName());
String acc = context.declareResult(myAccNameSupplier.apply(context), myType, getSupplier());
return getAccumulator(acc, inVar.getName());
}
@Override
CollectorOperation asCollector() {
return this;
}
@Override
public void registerUsedNames(Consumer<String> usedNameConsumer) {
mySupplier.registerUsedNames(usedNameConsumer);
}
@Override
public void transform(StreamToLoopReplacementContext context, String item) {
mySupplier.transform(context);
}
@Override
public String getSupplier() {
return mySupplier.getText();
}
}
static class AccumulatedTerminalOperation extends TerminalOperation implements CollectorOperation {
private String myAccName;
private String myAccType;
private String myAccInitializer;
@@ -404,32 +585,326 @@ abstract class TerminalOperation extends Operation {
return myUpdateTemplate.replace("{item}", inVar.getName()).replace("{acc}", varName);
}
public static AccumulatedTerminalOperation toCollection(PsiType collectionType, String implementationType, String varName) {
return new AccumulatedTerminalOperation(varName, collectionType.getCanonicalText(), "new " + implementationType + "<>()",
"{acc}.add({item});");
}
}
static class ToCollectionTerminalOperation extends TerminalOperation {
private final String myType;
private final FunctionHelper myFn;
public ToCollectionTerminalOperation(FunctionHelper fn, PsiType callType) {
myFn = fn;
myType = callType.getCanonicalText();
@Override
CollectorOperation asCollector() {
return myFinisherTemplate.equals("{acc}") && PsiTypesUtil.boxIfPossible(myAccType).equals(myAccType) ? this : null;
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
myFn.registerUsedNames(usedNameConsumer);
public String getSupplier() {
return myAccInitializer;
}
@Override
public String getAccumulator(String acc, String item) {
return myUpdateTemplate.replace("{acc}", acc).replace("{item}", item);
}
@NotNull
static AccumulatedTerminalOperation toCollection(PsiType collectionType, String implementationType, String varName) {
return new AccumulatedTerminalOperation(varName, collectionType.getCanonicalText(), "new " + implementationType + "<>()",
"{acc}.add({item});");
}
@NotNull
private static AccumulatedTerminalOperation toList(@NotNull PsiType resultType) {
return toCollection(resultType, CommonClassNames.JAVA_UTIL_ARRAY_LIST, "list");
}
@NotNull
static AccumulatedTerminalOperation summing(PsiType type) {
return new AccumulatedTerminalOperation("sum", type.getCanonicalText(), "0", "{acc}+={item};");
}
@NotNull
static AccumulatedTerminalOperation summarizing(@NotNull PsiType resultType) {
return new AccumulatedTerminalOperation("stat", resultType.getCanonicalText(), "new " + resultType.getCanonicalText() + "()",
"{acc}.accept({item});");
}
}
static class ToCollectionTerminalOperation extends CollectorBasedTerminalOperation {
public ToCollectionTerminalOperation(FunctionHelper fn) {
super(fn.getResultType(), context -> fn.suggestFinalOutputNames(context, null, "collection").get(0), fn);
}
@Override
public String getAccumulator(String acc, String item) {
return acc+".add("+item+");\n";
}
}
static class MinMaxTerminalOperation extends TerminalOperation {
private String myType;
private String myTemplate;
private String myComparatorType;
private @Nullable PsiExpression myComparator;
public MinMaxTerminalOperation(String type, String template, @Nullable PsiExpression comparator) {
myType = type;
myTemplate = template;
myComparator = comparator;
if(comparator != null) {
PsiType comparatorType = comparator.getType();
if(comparatorType != null) {
myComparatorType = comparatorType.getCanonicalText();
} else {
myComparatorType = CommonClassNames.JAVA_UTIL_COMPARATOR+"<"+myType+">";
}
}
}
@Override
public void registerUsedNames(Consumer<String> usedNameConsumer) {
if(myComparator != null) {
FunctionHelper.processUsedNames(myComparator, usedNameConsumer);
}
}
@Override
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
// TODO: remove redundant type arguments
myFn.transform(context);
String collection = context.declareResult("collection", myType, myFn.getText());
return collection+".add("+inVar+");\n";
String comparator = "";
if(myComparator != null) {
if(ExpressionUtils.isSimpleExpression(myComparator)) {
comparator = myComparator.getText();
} else {
comparator = context.declare("comparator", myComparatorType, myComparator.getText());
}
}
String seen = context.declare("seen", "boolean", "false");
String best = context.declareResult("best", myType, TypeConversionUtil.isPrimitive(myType) ? "0" : "null");
String type = myType;
context.setOptionalUnwrapperFinisher(seen, best, type);
return "if(!"+seen+" || "+myTemplate.replace("{best}", best).replace("{item}", inVar.getName()).replace("{comparator}", comparator)+") {\n" +
seen+"=true;\n"+
best+"="+inVar+";\n}\n";
}
@Nullable
static MinMaxTerminalOperation create(@Nullable PsiExpression comparator, String elementType, boolean max) {
String sign = max ? ">" : "<";
if(comparator == null) {
if ("int".equals(elementType) || "long".equals(elementType)) {
return new MinMaxTerminalOperation(elementType, "{item}" + sign + "{best}", null);
}
if ("double".equals(elementType)) {
return new MinMaxTerminalOperation(elementType, "java.lang.Double.compare({item},{best})" + sign + "0", null);
}
} else if(InheritanceUtil.isInheritor(PsiUtil.resolveClassInClassTypeOnly(comparator.getType()), false,
CommonClassNames.JAVA_UTIL_COMPARATOR)) {
return new MinMaxTerminalOperation(elementType, "{comparator}.compare({item},{best})" + sign + "0", comparator);
}
return null;
}
}
static class ToMapTerminalOperation extends CollectorBasedTerminalOperation {
private final FunctionHelper myKeyExtractor, myValueExtractor;
private final PsiExpression myMerger;
ToMapTerminalOperation(FunctionHelper keyExtractor,
FunctionHelper valueExtractor,
PsiExpression merger,
FunctionHelper supplier,
PsiType resultType) {
super(resultType.getCanonicalText(), context -> "map", supplier);
myKeyExtractor = keyExtractor;
myValueExtractor = valueExtractor;
myMerger = merger;
}
@Override
public void registerUsedNames(Consumer<String> usedNameConsumer) {
super.registerUsedNames(usedNameConsumer);
myKeyExtractor.registerUsedNames(usedNameConsumer);
myValueExtractor.registerUsedNames(usedNameConsumer);
if(myMerger != null) FunctionHelper.processUsedNames(myMerger, usedNameConsumer);
}
@Override
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
myKeyExtractor.suggestVariableName(inVar, 0);
myValueExtractor.suggestVariableName(inVar, 0);
}
@Override
public void transform(StreamToLoopReplacementContext context, String item) {
super.transform(context, item);
myKeyExtractor.transform(context, item);
myValueExtractor.transform(context, item);
}
@Override
public String getAccumulator(String map, String item) {
if(myMerger == null) {
return "if("+map+".put("+myKeyExtractor.getText()+","+myValueExtractor.getText()+")!=null) {\n"+
"throw new java.lang.IllegalStateException(\"Duplicate key\");\n}\n";
}
return map+".merge("+myKeyExtractor.getText()+","+myValueExtractor.getText()+","+myMerger.getText()+");\n";
}
}
static class GroupByTerminalOperation extends CollectorBasedTerminalOperation {
private final CollectorOperation myCollector;
private FunctionHelper myKeyExtractor;
private String myKeyVar;
public GroupByTerminalOperation(FunctionHelper keyExtractor, FunctionHelper supplier, PsiType resultType, CollectorOperation collector) {
super(resultType.getCanonicalText(), context -> "map", supplier);
myKeyExtractor = keyExtractor;
myCollector = collector;
}
@Override
public void registerUsedNames(Consumer<String> usedNameConsumer) {
super.registerUsedNames(usedNameConsumer);
myKeyExtractor.registerUsedNames(usedNameConsumer);
myCollector.registerUsedNames(usedNameConsumer);
}
@Override
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
myKeyExtractor.suggestVariableName(inVar, 0);
myCollector.suggestNames(inVar, outVar);
}
@Override
public void transform(StreamToLoopReplacementContext context, String item) {
super.transform(context, item);
myKeyExtractor.transform(context, item);
myCollector.transform(context, item);
myKeyVar = context.registerVarName(Arrays.asList("k", "key"));
}
@Override
public String getAccumulator(String map, String item) {
String acc = map+".computeIfAbsent("+myKeyExtractor.getText()+","+myKeyVar+"->"+myCollector.getSupplier()+")";
return myCollector.getAccumulator(acc, item);
}
}
static class PartitionByTerminalOperation extends TerminalOperation {
private final String myResultType;
private final CollectorOperation myCollector;
private FunctionHelper myPredicate;
public PartitionByTerminalOperation(FunctionHelper predicate, PsiType resultType, CollectorOperation collector) {
myPredicate = predicate;
myResultType = resultType.getCanonicalText();
myCollector = collector;
}
@Override
public void registerUsedNames(Consumer<String> usedNameConsumer) {
myPredicate.registerUsedNames(usedNameConsumer);
myCollector.registerUsedNames(usedNameConsumer);
}
@Override
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
myPredicate.suggestVariableName(inVar, 0);
myCollector.suggestNames(inVar, outVar);
}
@Override
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
String map = context.declareResult("map", myResultType, "new java.util.HashMap<>()");
myPredicate.transform(context, inVar.getName());
myCollector.transform(context, inVar.getName());
context.addInitStep(map+".put(false, "+myCollector.getSupplier()+");");
context.addInitStep(map+".put(true, "+myCollector.getSupplier()+");");
return myCollector.getAccumulator(map + ".get(" + myPredicate.getText() + ")", inVar.getName());
}
}
abstract static class AbstractMappingTerminalOperation extends TerminalOperation implements CollectorOperation {
final FunctionHelper myMapper;
final TerminalOperation myDownstream;
final CollectorOperation myDownstreamCollector;
AbstractMappingTerminalOperation(FunctionHelper mapper, TerminalOperation downstream) {
myMapper = mapper;
myDownstream = downstream;
myDownstreamCollector = downstream.asCollector();
}
@Override
public void registerUsedNames(Consumer<String> usedNameConsumer) {
myMapper.registerUsedNames(usedNameConsumer);
myDownstream.registerUsedNames(usedNameConsumer);
}
@Override
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
myMapper.suggestVariableName(inVar, 0);
}
@Override
CollectorOperation asCollector() {
return myDownstreamCollector == null ? null : this;
}
@Override
public String getSupplier() {
return myDownstreamCollector.getSupplier();
}
}
static class MappingTerminalOperation extends AbstractMappingTerminalOperation {
private StreamVariable myVariable;
MappingTerminalOperation(FunctionHelper mapper, TerminalOperation downstream) {
super(mapper, downstream);
}
@Override
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
createVariable(context, inVar.getName());
return myVariable.getDeclaration() + "=" + myMapper.getText() + ";\n" + myDownstream.generate(myVariable, context);
}
private void createVariable(StreamToLoopReplacementContext context, String item) {
myMapper.transform(context, item);
myVariable = new StreamVariable(myMapper.getResultType());
myDownstream.suggestNames(myVariable, StreamVariable.STUB);
myMapper.suggestFinalOutputNames(context, null, null).forEach(myVariable::addOtherNameCandidate);
myVariable.register(context);
}
@Override
public void transform(StreamToLoopReplacementContext context, String item) {
createVariable(context, item);
myDownstreamCollector.transform(context, myVariable.getName());
}
@Override
public String getAccumulator(String acc, String item) {
return myVariable.getDeclaration() + "=" + myMapper.getText() + ";\n" +
myDownstreamCollector.getAccumulator(acc, myVariable.getName());
}
}
static class InlineMappingTerminalOperation extends AbstractMappingTerminalOperation {
InlineMappingTerminalOperation(FunctionHelper mapper, TerminalOperation downstream) {
super(mapper, downstream);
}
@Override
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
myMapper.transform(context, inVar.getName());
StreamVariable updatedVar = new StreamVariable(myMapper.getResultType(), myMapper.getText());
return myDownstream.generate(updatedVar, context);
}
@Override
public void transform(StreamToLoopReplacementContext context, String item) {
myMapper.transform(context, item);
myDownstreamCollector.transform(context, myMapper.getText());
}
@Override
public String getAccumulator(String acc, String item) {
return myDownstreamCollector.getAccumulator(acc, myMapper.getText());
}
}
@@ -446,7 +921,7 @@ abstract class TerminalOperation extends Operation {
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
public void registerUsedNames(Consumer<String> usedNameConsumer) {
myFn.registerUsedNames(usedNameConsumer);
}
@@ -0,0 +1,23 @@
// "Replace Stream API chain with loop" "true"
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
public void test(String... list) {
double sum = 0;
long count = 0;
for (String s : list) {
if (Objects.nonNull(s)) {
sum += 1.0 / s;
count++;
}
}
System.out.println((count == 0 ? 0.0 : sum / count));
}
public static void main(String[] args) {
new Main().test("a", "bbb", null, "cc", "dd", "eedasfasdfs");
}
}
@@ -0,0 +1,23 @@
// "Replace Stream API chain with loop" "true"
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
public void test(String... list) {
long sum = 0;
long count = 0;
for (String s : list) {
if (Objects.nonNull(s)) {
sum += s.length();
count++;
}
}
System.out.println((count == 0 ? 0.0 : (double) sum / count));
}
public static void main(String[] args) {
new Main().test("a", "bbb", null, "cc", "dd", "eedasfasdfs");
}
}
@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static long test(List<String> strings) {
long count = 0;
for (String s : strings) {
if (!s.isEmpty()) {
count++;
}
}
return count;
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee", "")));
}
}
@@ -0,0 +1,26 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static Optional<String> test(List<String> strings) {
Comparator<String> comparator = Comparator.naturalOrder();
boolean seen = false;
String best = null;
for (String s : strings) {
if (!s.isEmpty()) {
if (!seen || comparator.compare(s, best) > 0) {
seen = true;
best = s;
}
}
}
return (seen ? Optional.of(best) : Optional.empty());
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee", "")));
}
}
@@ -0,0 +1,16 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main<T> {
void test() {
Integer acc = 0;
for (String s : Arrays.asList("a", "bb", "ccc")) {
Integer length = s.length();
acc = Integer.sum(acc, length);
}
Integer totalLength = acc;
}
}
@@ -0,0 +1,24 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.DoubleSummaryStatistics;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class Main {
public static DoubleSummaryStatistics test(List<String> strings) {
DoubleSummaryStatistics stat = new DoubleSummaryStatistics();
for (String str : strings) {
if (Objects.nonNull(str)) {
stat.accept(str.length() / 2.0);
}
}
return stat;
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList(null, null)));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee", "")));
}
}
@@ -0,0 +1,23 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class Main {
public static Double test(List<String> strings) {
double sum = 0;
for (String string : strings) {
if (Objects.nonNull(string)) {
sum += string.length();
}
}
return sum;
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList(null, null)));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee", "")));
}
}
@@ -0,0 +1,18 @@
// "Replace Stream API chain with loop" "true"
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public void test(List<String> list) {
List<String> result = new ArrayList<>();
Function<? super String, ? extends String> function = list.size() < 10 ? String::trim : Function.identity();
for (String s : list) {
String s1 = function.apply(s);
result.add(s1);
}
System.out.println(result);
}
}
@@ -10,8 +10,8 @@ public class Main {
for (List<String> a : list) {
if (a != null) {
for (String s : a) {
long l = s.length();
stat.accept(l);
long length = s.length();
stat.accept(length);
}
}
}
@@ -10,8 +10,8 @@ public class Main {
List<Integer> list = new ArrayList<>();
for (int x : input) {
if (x > 0) {
Integer integer = x * 2;
list.add(integer);
Integer i = x * 2;
list.add(i);
}
}
return list;
@@ -7,12 +7,12 @@ import java.util.stream.Stream;
public class Main {
private static long test(List<? extends String> list) {
long count = 0;
for (Object o : Arrays.<Object>asList(0, null, "1", list)) {
for (Object o1 : Arrays.<Object>asList(o)) {
for (Object o2 : Arrays.<Object>asList(o1)) {
for (Object o3 : Arrays.<Object>asList(o2)) {
for (Object o4 : Arrays.<Object>asList(o3)) {
for (Object o5 : Arrays.<Object>asList(o4)) {
for (Object o : Arrays.asList(0, null, "1", list)) {
for (Object o1 : Arrays.asList(o)) {
for (Object o2 : Arrays.asList(o1)) {
for (Object o3 : Arrays.asList(o2)) {
for (Object o4 : Arrays.asList(o3)) {
for (Object o5 : Arrays.asList(o4)) {
count++;
}
}
@@ -0,0 +1,30 @@
// "Replace Stream API chain with loop" "true"
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Main {
public static void test(List<String> list) {
List<Integer> result = new ArrayList<>();
for (String x : list) {
if (x != null) {
Predicate<Integer> predicate = Predicate.isEqual(x.length());
for (int i = 0; i < 10; i++) {
Integer integer = i;
if (predicate.test(integer)) {
result.add(integer);
}
}
}
}
System.out.println(result);
}
public static void main(String[] args) {
test(Arrays.asList("a", "bbbb", "cccccccccc", "dd", ""));
}
}
@@ -13,8 +13,8 @@ public class Main {
for (List<String> lst : l) {
if (lst != null) {
for (String str : lst) {
int i = str.length();
stat.accept(i);
int length = str.length();
stat.accept(length);
}
}
}
@@ -10,8 +10,8 @@ public class Main {
for (List<String> a : list) {
if (a != null) {
for (String s : a) {
long l = s.length();
stat.accept(l);
long length = s.length();
stat.accept(length);
}
}
}
@@ -0,0 +1,17 @@
// "Replace Stream API chain with loop" "true"
import java.util.SplittableRandom;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
Integer acc = 0;
SplittableRandom splittableRandom = new SplittableRandom(1);
for (long count = 100; count > 0; count--) {
Integer integer = 500;
acc = splittableRandom.nextInt(acc, integer);
}
int n1 = acc;
System.out.println(n1);
}
}
@@ -10,12 +10,10 @@ public class Main {
Random r = new Random();
DoubleSummaryStatistics stat = new DoubleSummaryStatistics();
for (int x = 0; x < 10; x++) {
double x1 = x;
long limit = (long) x1;
while (true) {
double v = r.nextDouble() * x1;
if (limit-- == 0) break;
stat.accept(v);
double v = x;
for (long count = (long) v; count > 0; count--) {
double v1 = r.nextDouble() * v;
stat.accept(v1);
}
}
return stat;
@@ -0,0 +1,19 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static Map<Integer, List<String>> test(List<String> strings) {
Map<Integer, List<String>> map = new HashMap<>();
for (String str : strings) {
map.computeIfAbsent(str.length(), k -> new ArrayList<>()).add(str);
}
return map;
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee")));
}
}
@@ -0,0 +1,19 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static Map<Integer, List<String>> test(List<String> strings, int k) {
Map<Integer, List<String>> map = new HashMap<>();
for (String string : strings) {
map.computeIfAbsent(string.length(), key -> new ArrayList<>()).add(string);
}
return map;
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee")));
}
}
@@ -0,0 +1,18 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
private static TreeMap<Integer, LinkedHashSet<String>> getMap(List<String> strings) {
TreeMap<Integer, LinkedHashSet<String>> map = new TreeMap<>(Comparator.reverseOrder());
for (String string : strings) {
map.computeIfAbsent(string.length(), k -> new LinkedHashSet<>()).add(string);
}
return map;
}
public static void main(String[] args) {
System.out.println(getMap(Arrays.asList("a", "bbb", "cccc", "dddd", "ee", "e")));
}
}
@@ -0,0 +1,19 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public static void test(List<String> strings) {
Map<Integer, Map<Character, Set<String>>> map = new HashMap<>();
for (String s : strings) {
map.computeIfAbsent(s.length(), key -> new HashMap<>()).computeIfAbsent(s.charAt(0), k -> new HashSet<>()).add(s);
}
System.out.println(map);
}
public static void main(String[] args) {
test(Arrays.asList("a", "bbb", "cccc", "dddd"));
}
}
@@ -0,0 +1,20 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
private static TreeMap<Integer, List<Integer>> getMap(List<String> strings) {
TreeMap<Integer, List<Integer>> map = new TreeMap<>(Comparator.reverseOrder());
for (String string : strings) {
Integer len = string.length();
Integer integer = len * 2;
map.computeIfAbsent(string.length(), k -> new ArrayList<>()).add(integer);
}
return map;
}
public static void main(String[] args) {
System.out.println(getMap(Arrays.asList("a", "bbb", "cccc", "dddd", "ee", "e")));
}
}
@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public static void test(List<String> strings) {
Map<Integer, DoubleSummaryStatistics> map = new HashMap<>();
for (String string : strings) {
if (Objects.nonNull(string)) {
map.computeIfAbsent(string.length(), k -> new DoubleSummaryStatistics()).accept(string.length());
}
}
System.out.println(map);
}
public static void main(String[] args) {
test(Arrays.asList("a", "bbb", "cccc", "dddd"));
}
}
@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public static void test(List<String> strings) {
Map<Integer, Map<Character, String>> map = new HashMap<>();
for (String s : strings) {
if (map.computeIfAbsent(s.length(), k -> new HashMap<>()).put(s.charAt(0), s) != null) {
throw new IllegalStateException("Duplicate key");
}
}
System.out.println(map);
}
public static void main(String[] args) {
test(Arrays.asList("a", "bbb", "cccc", "dddd"));
}
}
@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public static void test(List<String> strings) {
Map<Integer, Set<String>> map = new HashMap<>();
for (String string : strings) {
if (Objects.nonNull(string)) {
map.computeIfAbsent(string.length(), k -> new HashSet<>()).add(string);
}
}
System.out.println(map);
}
public static void main(String[] args) {
test(Arrays.asList("a", "bbb", "cccc", "dddd"));
}
}
@@ -9,8 +9,8 @@ public class Main {
long limit = 20;
for (String x = ""; ; x = x + "a") {
if (limit-- == 0) break;
int i = x.length();
stat.accept(i);
int length = x.length();
stat.accept(length);
}
return stat;
}
@@ -11,8 +11,8 @@ public class Main {
long limitInner = limit;
for (String x = ""; ; x = x + limit) {
if (limitInner-- == 0) break;
int i = x.length();
stat.accept(i);
int length = x.length();
stat.accept(length);
}
}
return stat;
@@ -12,8 +12,8 @@ public class Main {
long limit = x;
for (String s = String.valueOf(x); ; s = s + x) {
if (limit-- == 0) break;
int i = s.length();
stat.accept(i);
int length = s.length();
stat.accept(length);
}
}
}
@@ -7,8 +7,8 @@ public class Main {
private static long countNonEmpty(List<String> input) {
long count = 0;
for (String str : input) {
String s = str.trim();
if (!s.isEmpty()) {
String trim = str.trim();
if (!trim.isEmpty()) {
count++;
}
}
@@ -0,0 +1,20 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
private static void getMap(List<String> strings) {
List<Integer> list = new ArrayList<>();
for (String string : strings) {
Integer len = string.length();
Integer integer = len * 2;
list.add(integer);
}
System.out.println(list);
}
public static void main(String[] args) {
getMap(Arrays.asList("a", "bbb", "cccc", "dddd", "ee", "e"));
}
}
@@ -0,0 +1,26 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
public class Main {
public static String test(List<String> strings) {
Comparator<String> comparator = Comparator.comparing(String::length);
boolean seen = false;
String best = null;
for (String string : strings) {
if (!seen || comparator.compare(string, best) > 0) {
seen = true;
best = string;
}
}
return (seen ? Optional.of(best) : Optional.<String>empty()).orElse(null);
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee")));
}
}
@@ -0,0 +1,25 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
import java.util.OptionalDouble;
public class Main {
public static double test(List<String> strings) {
boolean seen = false;
double best = 0;
for (String string : strings) {
double v = string.length();
if (!seen || Double.compare(v, best) > 0) {
seen = true;
best = v;
}
}
return (seen ? OptionalDouble.of(best) : OptionalDouble.empty()).orElse(-1);
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d")));
}
}
@@ -0,0 +1,25 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
public class Main {
public static String test(List<String> strings, Comparator<String> cmp) {
boolean seen = false;
String best = null;
for (String string : strings) {
if (!seen || cmp.compare(string, best) < 0) {
seen = true;
best = string;
}
}
return (seen ? Optional.of(best) : Optional.<String>empty()).orElse(null);
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList(), Comparator.comparing(String::length)));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee"), Comparator.comparing(String::length)));
}
}
@@ -0,0 +1,26 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
public class Main {
public static String test(List<String> strings, Comparator<CharSequence> comparator) {
Comparator<CharSequence> comparator1 = comparator.reversed();
boolean seen = false;
String best = null;
for (String string : strings) {
if (!seen || comparator1.compare(string, best) < 0) {
seen = true;
best = string;
}
}
return (seen ? Optional.of(best) : Optional.<String>empty()).orElse(null);
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList(), Comparator.comparing(CharSequence::length)));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee"), Comparator.comparing(CharSequence::length)));
}
}
@@ -0,0 +1,25 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
import java.util.OptionalInt;
public class Main {
public static int test(List<String> strings) {
boolean seen = false;
int best = 0;
for (String string : strings) {
int i = string.length();
if (!seen || i < best) {
seen = true;
best = i;
}
}
return (seen ? OptionalInt.of(best) : OptionalInt.empty()).orElse(-1);
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d")));
}
}
@@ -10,13 +10,13 @@ public class Main {
long sum = 0;
for (Map.Entry<String, List<String>> e : strings.entrySet()) {
if (!e.getKey().isEmpty()) {
long l = e.getValue().stream().filter(new Predicate<String>() {
long count = e.getValue().stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return e.getKey().equals(s);
}
}).count();
sum += l;
sum += count;
}
}
return sum;
@@ -9,8 +9,8 @@ public class Main {
long sum = 0;
for (Map.Entry<String, List<String>> e : strings.entrySet()) {
if (!e.getKey().isEmpty()) {
long l = e.getValue().stream().filter(s -> e.getKey().equals(s)).count();
sum += l;
long count = e.getValue().stream().filter(s -> e.getKey().equals(s)).count();
sum += count;
}
}
return sum;
@@ -9,8 +9,8 @@ public class Main {
long sum = 0;
for (Map.Entry<String, List<String>> s : strings.entrySet()) {
if (!s.getKey().isEmpty()) {
long l = s.getValue().stream().filter(sx -> s.getKey().equals(sx)).count();
sum += l;
long count = s.getValue().stream().filter(sx -> s.getKey().equals(sx)).count();
sum += count;
}
}
return sum;
@@ -0,0 +1,23 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static Map<Boolean, List<String>> test(List<String> strings) {
Map<Boolean, List<String>> map = new HashMap<>();
map.put(false, new ArrayList<>());
map.put(true, new ArrayList<>());
for (String s : strings) {
if (!s.isEmpty()) {
map.get(s.length() > 1).add(s);
}
}
return map;
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee", "")));
}
}
@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public static void test(List<String> strings) {
Map<Boolean, Map<Character, Set<String>>> map = new HashMap<>();
map.put(false, new HashMap<>());
map.put(true, new HashMap<>());
for (String s : strings) {
map.get(s.length() > 2).computeIfAbsent(s.charAt(0), k -> new HashSet<>()).add(s);
}
System.out.println(map);
}
public static void main(String[] args) {
test(Arrays.asList("a", "bbb", "cccc", "dddd"));
}
}
@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
public class Main {
public static void test(List<String> strings) {
Map<Boolean, LinkedHashSet<String>> map = new HashMap<>();
map.put(false, new LinkedHashSet<>());
map.put(true, new LinkedHashSet<>());
for (String s : strings) {
map.get(s.length() > 2).add(s);
}
System.out.println(map);
}
public static void main(String[] args) {
test(Arrays.asList("a", "bbb", "cccc", "dddd", "ee", "e", "e"));
}
}
@@ -0,0 +1,24 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
public class Main {
public static void test(List<String> strings) {
Map<Boolean, Map<String, Integer>> map = new HashMap<>();
map.put(false, new HashMap<>());
map.put(true, new HashMap<>());
for (String string : strings) {
String s = string.trim();
if (map.get(s.length() > 2).put(((UnaryOperator<String>) x -> x).apply(s), s.length()) != null) {
throw new IllegalStateException("Duplicate key");
}
}
System.out.println(map);
}
public static void main(String[] args) {
test(Arrays.asList("a", "bbb", "cccc", "dddd", "ee", "e", "e"));
}
}
@@ -8,8 +8,8 @@ public class Main {
int sum = 0;
for (String s : list) {
System.out.println(s);
int i = s.length();
sum += i;
int length = s.length();
sum += length;
}
return sum;
}
@@ -9,8 +9,8 @@ public class Main {
LongSummaryStatistics stat = new LongSummaryStatistics();
for (String s : list) {
System.out.println(s);
long l = s.length();
stat.accept(l);
long length = s.length();
stat.accept(length);
}
return stat;
}
@@ -8,8 +8,8 @@ public class Main {
private static IntSummaryStatistics test() {
IntSummaryStatistics stat = new IntSummaryStatistics();
for (Number[] nums : Arrays.<Number[]>asList(new Integer[]{1, 2, 3})) {
int i = (int) nums[0];
stat.accept(i);
int num = (int) nums[0];
stat.accept(num);
}
return stat;
}
@@ -6,12 +6,12 @@ import java.util.stream.IntStream;
public class Main {
private static TreeSet<Integer> test() {
TreeSet<Integer> collection = new TreeSet<Integer>();
TreeSet<Integer> integers = new TreeSet<>();
for (int i : new int[]{4, 2, 1}) {
Integer integer = i;
collection.add(integer);
integers.add(integer);
}
return collection;
return integers;
}
public static void main(String[] args) {
@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static Map<Integer, String> test(List<String> strings) {
Map<Integer, String> map = new HashMap<>();
for (String str : strings) {
if (map.put(str.length(), str) != null) {
throw new IllegalStateException("Duplicate key");
}
}
return map;
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee", "")));
}
}
@@ -0,0 +1,22 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public static Map<Integer, String> test(List<String> strings) {
Map<Integer, String> map = new HashMap<>();
for (String s : strings) {
if (!s.isEmpty()) {
map.merge(s.length(), s, String::concat);
}
}
return map;
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee", "")));
}
}
@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static TreeMap<Integer, String> test(List<String> strings) {
TreeMap<Integer, String> map = new TreeMap<>();
for (String s1 : strings) {
if (!s1.isEmpty()) {
map.merge(s1.length(), s1, (s, string) -> s);
}
}
return map;
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee", "")));
}
}
@@ -0,0 +1,15 @@
// "Replace Stream API chain with loop" "true"
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
public void test(String... list) {
System.out.println(Stream.of(list).filter(Objects::nonNull).col<caret>lect(Collectors.averagingDouble(s -> 1.0/s)));
}
public static void main(String[] args) {
new Main().test("a", "bbb", null, "cc", "dd", "eedasfasdfs");
}
}
@@ -0,0 +1,15 @@
// "Replace Stream API chain with loop" "true"
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
public void test(String... list) {
System.out.println(Stream.of(list).filter(Objects::nonNull).col<caret>lect(Collectors.averagingInt(String::length)));
}
public static void main(String[] args) {
new Main().test("a", "bbb", null, "cc", "dd", "eedasfasdfs");
}
}
@@ -0,0 +1,15 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static long test(List<String> strings) {
return strings.stream().filter(s -> !s.isEmpty()).coll<caret>ect(Collectors.counting());
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee", "")));
}
}
@@ -0,0 +1,15 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static Optional<String> test(List<String> strings) {
return strings.stream().filter(s -> !s.isEmpty()).c<caret>ollect(Collectors.maxBy(Comparator.naturalOrder()));
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee", "")));
}
}
@@ -0,0 +1,10 @@
// "Replace Stream API chain with loop" "true"
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main<T> {
void test() {
Integer totalLength = Stream.of("a", "bb", "ccc").co<caret>llect(Collectors.reducing(0, String::length, Integer::sum));
}
}
@@ -0,0 +1,18 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.DoubleSummaryStatistics;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class Main {
public static DoubleSummaryStatistics test(List<String> strings) {
return strings.stream().filter(Objects::nonNull).colle<caret>ct(Collectors.summarizingDouble(str -> str.length()/2.0));
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList(null, null)));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee", "")));
}
}
@@ -0,0 +1,17 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class Main {
public static Double test(List<String> strings) {
return strings.stream().filter(Objects::nonNull).co<caret>llect(Collectors.summingDouble(String::length));
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList(null, null)));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee", "")));
}
}
@@ -0,0 +1,11 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public void test(List<String> list) {
System.out.println(list.stream().map(list.size() < 10 ? String::trim : Function.identity()).colle<caret>ct(Collectors.toList()));
}
}
@@ -0,0 +1,20 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Main {
public static void test(List<String> list) {
System.out.println(list.stream()
.filter(x -> x != null)
.flatMap(s -> IntStream.range(0, 10).boxed().filter(Predicate.isEqual(s.length())))
.co<caret>llect(Collectors.toList()));
}
public static void main(String[] args) {
test(Arrays.asList("a", "bbbb", "cccccccccc", "dd", ""));
}
}
@@ -0,0 +1,11 @@
// "Replace Stream API chain with loop" "true"
import java.util.SplittableRandom;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
int n1 = Stream.generate(() -> 500).limit(100).r<caret>educe(0, new SplittableRandom(1)::nextInt, Integer::sum);
System.out.println(n1);
}
}
@@ -0,0 +1,15 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static Map<Integer, List<String>> test(List<String> strings) {
return strings.stream().col<caret>lect(Collectors.groupingBy(str -> str.length()));
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee")));
}
}
@@ -0,0 +1,15 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static Map<Integer, List<String>> test(List<String> strings, int k) {
return strings.stream().col<caret>lect(Collectors.groupingBy(String::length));
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee")));
}
}
@@ -0,0 +1,15 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
private static TreeMap<Integer, LinkedHashSet<String>> getMap(List<String> strings) {
return strings.stream().coll<caret>ect(
Collectors.groupingBy(String::length, () -> new TreeMap<>(Comparator.reverseOrder()), Collectors.toCollection(LinkedHashSet::new)));
}
public static void main(String[] args) {
System.out.println(getMap(Arrays.asList("a", "bbb", "cccc", "dddd", "ee", "e")));
}
}
@@ -0,0 +1,15 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public static void test(List<String> strings) {
System.out.println(strings.stream().coll<caret>ect(Collectors.groupingBy(String::length, Collectors.groupingBy(s -> s.charAt(0), Collectors.toSet()))));
}
public static void main(String[] args) {
test(Arrays.asList("a", "bbb", "cccc", "dddd"));
}
}
@@ -0,0 +1,16 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
private static TreeMap<Integer, List<Integer>> getMap(List<String> strings) {
return strings.stream().colle<caret>ct(
Collectors.groupingBy(String::length, () -> new TreeMap<>(Comparator.reverseOrder()),
Collectors.mapping(String::length, Collectors.mapping(len -> len*2, Collectors.toList()))));
}
public static void main(String[] args) {
System.out.println(getMap(Arrays.asList("a", "bbb", "cccc", "dddd", "ee", "e")));
}
}
@@ -0,0 +1,17 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public static void test(List<String> strings) {
System.out.println(strings.stream()
.filter(Objects::nonNull)
.co<caret>llect(Collectors.groupingBy(String::length, Collectors.summarizingDouble(String::length))));
}
public static void main(String[] args) {
test(Arrays.asList("a", "bbb", "cccc", "dddd"));
}
}
@@ -0,0 +1,15 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public static void test(List<String> strings) {
System.out.println(strings.stream().co<caret>llect(Collectors.groupingBy(String::length, Collectors.toMap(s -> s.charAt(0), Function.identity()))));
}
public static void main(String[] args) {
test(Arrays.asList("a", "bbb", "cccc", "dddd"));
}
}
@@ -0,0 +1,15 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public static void test(List<String> strings) {
System.out.println(strings.stream().filter(Objects::nonNull).col<caret>lect(Collectors.groupingBy(String::length, Collectors.toSet())));
}
public static void main(String[] args) {
test(Arrays.asList("a", "bbb", "cccc", "dddd"));
}
}
@@ -0,0 +1,14 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
private static void getMap(List<String> strings) {
System.out.println(strings.stream().co<caret>llect(Collectors.mapping(String::length, Collectors.mapping(len -> len*2, Collectors.toList()))));
}
public static void main(String[] args) {
getMap(Arrays.asList("a", "bbb", "cccc", "dddd", "ee", "e"));
}
}
@@ -0,0 +1,16 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class Main {
public static String test(List<String> strings) {
return strings.stream().m<caret>ax(Comparator.comparing(String::length)).orElse(null);
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee")));
}
}
@@ -0,0 +1,15 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
public class Main {
public static double test(List<String> strings) {
return strings.stream().mapToDouble(String::length).m<caret>ax().orElse(-1);
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d")));
}
}
@@ -0,0 +1,16 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class Main {
public static String test(List<String> strings, Comparator<String> cmp) {
return strings.stream().m<caret>in(cmp).orElse(null);
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList(), Comparator.comparing(String::length)));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee"), Comparator.comparing(String::length)));
}
}
@@ -0,0 +1,16 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class Main {
public static String test(List<String> strings, Comparator<CharSequence> comparator) {
return strings.stream().m<caret>in(comparator.reversed()).orElse(null);
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList(), Comparator.comparing(CharSequence::length)));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee"), Comparator.comparing(CharSequence::length)));
}
}
@@ -0,0 +1,15 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
public class Main {
public static int test(List<String> strings) {
return strings.stream().mapToInt(String::length).mi<caret>n().orElse(-1);
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d")));
}
}
@@ -0,0 +1,15 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static Map<Boolean, List<String>> test(List<String> strings) {
return strings.stream().filter(s -> !s.isEmpty()).co<caret>llect(Collectors.partitioningBy(s -> s.length() > 1));
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee", "")));
}
}
@@ -0,0 +1,15 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public static void test(List<String> strings) {
System.out.println(strings.stream().c<caret>ollect(Collectors.partitioningBy((String s) -> s.length() > 2, Collectors.groupingBy(s -> s.charAt(0), Collectors.toSet()))));
}
public static void main(String[] args) {
test(Arrays.asList("a", "bbb", "cccc", "dddd"));
}
}
@@ -0,0 +1,17 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
public class Main {
public static void test(List<String> strings) {
System.out.println(strings.stream().co<caret>llect(Collectors.partitioningBy(s -> s.length() > 2, Collectors.toCollection(LinkedHashSet::new))));
}
public static void main(String[] args) {
test(Arrays.asList("a", "bbb", "cccc", "dddd", "ee", "e", "e"));
}
}
@@ -0,0 +1,18 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
public class Main {
public static void test(List<String> strings) {
System.out.println(strings.stream().map(x -> x.trim()).colle<caret>ct(Collectors.partitioningBy(s -> s.length() > 2,
Collectors.toMap(s -> ((UnaryOperator<String>) x -> x).apply(s), String::length))));
}
public static void main(String[] args) {
test(Arrays.asList("a", "bbb", "cccc", "dddd", "ee", "e", "e"));
}
}
@@ -0,0 +1,16 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static Map<Integer, String> test(List<String> strings) {
return strings.stream()
.co<caret>llect(Collectors.toMap(String::length, str -> str));
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee", "")));
}
}
@@ -0,0 +1,17 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Main {
public static Map<Integer, String> test(List<String> strings) {
return strings.stream().filter(s -> !s.isEmpty())
.coll<caret>ect(Collectors.toMap(String::length, Function.identity(), String::concat));
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee", "")));
}
}
@@ -0,0 +1,16 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
public static TreeMap<Integer, String> test(List<String> strings) {
return strings.stream().filter(s -> !s.isEmpty())
.col<caret>lect(Collectors.toMap(String::length, s -> s, (s, string) -> s, TreeMap::new));
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList()));
System.out.println(test(Arrays.asList("a", "bbb", "cc", "d", "eee", "")));
}
}
@@ -110,6 +110,7 @@ public interface CommonClassNames {
@NonNls String JAVA_UTIL_STREAM_DOUBLE_STREAM = "java.util.stream.DoubleStream";
@NonNls String JAVA_UTIL_STREAM_COLLECTORS = "java.util.stream.Collectors";
@NonNls String JAVA_UTIL_FUNCTION_PREDICATE = "java.util.function.Predicate";
@NonNls String JAVA_UTIL_FUNCTION_FUNCTION = "java.util.function.Function";
@NonNls String JAVA_LANG_INVOKE_MH_POLYMORPHIC = "java.lang.invoke.MethodHandle.PolymorphicSignature";