IDEA-161198 Migration from Stream API back to for loops

This commit is contained in:
Tagir Valeev
2016-11-01 15:31:34 +07:00
parent 994b0f84a9
commit 9caa6d5ede
181 changed files with 5276 additions and 0 deletions

View File

@@ -0,0 +1,322 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection.streamToLoop;
import com.intellij.codeInspection.streamToLoop.StreamToLoopInspection.StreamToLoopReplacementContext;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.SuggestedNameInfo;
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.siyeh.ig.psiutils.ExpressionUtils;
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.function.Consumer;
/**
* A helper class which represents an expression mapped to the functional interface (like lambda, method reference or normal reference)
*
* @author Tagir Valeev
*/
@SuppressWarnings("SameParameterValue")
abstract class FunctionHelper {
private static final Logger LOG = Logger.getInstance(FunctionHelper.class);
String getText() {
return getExpression().getText();
}
abstract PsiExpression getExpression();
/**
* Perform an adaptation of current function helper to the replacement context with given parameter names.
* Must be called exactly once prior using getExpression() or getText()
*
* @param context a context for which transformation should be performed
* @param newNames names of the SAM parameters (the length must be exactly the same as number of SAM parameters)
*/
abstract void transform(StreamToLoopReplacementContext context, String... newNames);
/**
* Rename the references of some variable if it's used inside this function
*
* @param oldName old variable name
* @param newName new variable name
* @param context a context
*/
abstract void rename(String oldName, String newName, StreamToLoopReplacementContext context);
abstract void registerUsedNames(Consumer<String> consumer);
@Nullable
abstract String getParameterName(int index);
void suggestVariableName(StreamVariable var, int index) {
String name = getParameterName(index);
if (name != null) {
var.addBestNameCandidate(name);
}
}
@Contract("null, _, _ -> null")
@Nullable
static FunctionHelper create(PsiExpression expression, int paramCount, boolean singleExpression) {
if (expression instanceof PsiLambdaExpression) {
PsiLambdaExpression lambda = (PsiLambdaExpression)expression;
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 && singleExpression) return null;
return new LambdaFunctionHelper(body, parameters);
}
if (expression instanceof PsiMethodReferenceExpression) {
PsiType functionalInterfaceType = ((PsiMethodReferenceExpression)expression).getFunctionalInterfaceType();
PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
if (interfaceMethod == null) return null;
if (interfaceMethod.getParameterList().getParametersCount() != paramCount) return null;
return new MethodReferenceFunctionHelper(functionalInterfaceType, (PsiMethodReferenceExpression)expression);
}
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 null;
}
/**
* Renames references to the variable oldName in given expression into newName
* @param expression
* @param oldName old name
* @param newName new name
* @param context context
* @return resulting expression (might be the same as input expression) or null if expression already had references to newName,
* so rename may merge two variables
*/
@NotNull
static PsiExpression renameVarReference(@NotNull PsiExpression expression,
String oldName,
String newName,
StreamToLoopReplacementContext context) {
if(oldName.equals(newName)) return expression;
PsiLambdaExpression lambda = (PsiLambdaExpression)context.createExpression("("+oldName+","+newName+")-> "+expression.getText());
PsiParameter[] parameters = lambda.getParameterList().getParameters();
PsiParameter oldVar = parameters[0];
PsiParameter newVar = parameters[1];
PsiElement body = lambda.getBody();
LOG.assertTrue(body != null);
if(ReferencesSearch.search(newVar, new LocalSearchScope(body)).findFirst() != null) {
throw new IllegalStateException("Reference with name "+newVar+" already exists in "+lambda.getText());
}
for (PsiReference ref : ReferencesSearch.search(oldVar, new LocalSearchScope(body)).findAll()) {
ref.handleElementRename(newName);
}
return (PsiExpression)lambda.getBody();
}
static void processUsedNames(PsiElement start, Consumer<String> action) {
start.accept(new JavaRecursiveElementVisitor() {
@Override
public void visitVariable(PsiVariable variable) {
super.visitVariable(variable);
action.accept(variable.getName());
}
});
}
private static class MethodReferenceFunctionHelper extends FunctionHelper {
private final String myType;
private final String myQualifierType;
private PsiMethodReferenceExpression myMethodRef;
private PsiExpression myExpression;
public MethodReferenceFunctionHelper(PsiType functionalInterfaceType, PsiMethodReferenceExpression methodRef) {
myMethodRef = methodRef;
myType = functionalInterfaceType.getCanonicalText();
PsiExpression qualifier = methodRef.getQualifierExpression();
PsiType type = qualifier == null ? null : qualifier.getType();
myQualifierType = type == null ? null : type.getCanonicalText();
}
@Override
PsiExpression getExpression() {
LOG.assertTrue(myExpression != null);
return myExpression;
}
@Override
void registerUsedNames(Consumer<String> consumer) {
processUsedNames(myMethodRef, consumer);
}
@Override
void transform(StreamToLoopReplacementContext context, String... newNames) {
PsiMethodReferenceExpression methodRef = fromText(context, myMethodRef.getText());
PsiExpression qualifier = methodRef.getQualifierExpression();
if(qualifier != null && !ExpressionUtils.isSimpleExpression(qualifier)) {
String type = myQualifierType;
if(type != null) {
String nameCandidate = "expr";
PsiType psiType = context.createType(myQualifierType);
SuggestedNameInfo info =
JavaCodeStyleManager
.getInstance(context.getProject()).suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, psiType, true);
if(info.names.length > 0) {
nameCandidate = info.names[0];
}
String expr = context.declare(nameCandidate, type, qualifier.getText());
PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)context
.createExpression("(" + type + " " + expr + ")->(" + myType + ")" + expr + "::" + myMethodRef.getReferenceName());
PsiTypeCastExpression castExpr = (PsiTypeCastExpression)lambdaExpression.getBody();
LOG.assertTrue(castExpr != null);
methodRef = (PsiMethodReferenceExpression)castExpr.getOperand();
}
}
PsiLambdaExpression lambda = LambdaRefactoringUtil.convertMethodReferenceToLambda(methodRef, true, true);
LOG.assertTrue(lambda != null);
PsiElement body = lambda.getBody();
LOG.assertTrue(body instanceof PsiExpression);
myExpression = (PsiExpression)body;
EntryStream.zip(lambda.getParameterList().getParameters(), newNames)
.forKeyValue((param, newName) -> myExpression = renameVarReference(myExpression, param.getName(), newName, context));
}
@NotNull
private PsiMethodReferenceExpression fromText(StreamToLoopReplacementContext context, String text) {
PsiTypeCastExpression castExpr = (PsiTypeCastExpression)context.createExpression("(" + myType + ")" + text);
PsiMethodReferenceExpression methodRef = (PsiMethodReferenceExpression)castExpr.getOperand();
LOG.assertTrue(methodRef != null);
return methodRef;
}
@Override
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
if(oldName.equals(newName)) return;
PsiExpression qualifier = myMethodRef.getQualifierExpression();
if(qualifier == null) return;
qualifier = renameVarReference(qualifier, oldName, newName, context);
myMethodRef = fromText(context, qualifier.getText()+"::"+myMethodRef.getReferenceName());
}
@Override
@Nullable
String getParameterName(int index) {
return null;
}
}
private static class SimpleReferenceFunctionHelper extends FunctionHelper {
private PsiExpression myReference;
private final String myName;
private PsiExpression myExpression;
public SimpleReferenceFunctionHelper(PsiExpression reference, String methodName) {
myReference = reference;
myName = methodName;
}
@Override
PsiExpression getExpression() {
LOG.assertTrue(myExpression != null);
return myExpression;
}
@Override
void transform(StreamToLoopReplacementContext context, String... newNames) {
myExpression = context.createExpression(myReference.getText() + "." + myName + "(" + String.join(",", newNames) + ")");
}
@Override
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
myReference = renameVarReference(myReference, oldName, newName, context);
}
@Override
void registerUsedNames(Consumer<String> consumer) {
processUsedNames(myReference, consumer);
}
@Nullable
@Override
String getParameterName(int index) {
return null;
}
}
private static class LambdaFunctionHelper extends FunctionHelper {
private String[] myParameters;
private PsiExpression myBody;
LambdaFunctionHelper(PsiExpression body, String[] parameters) {
myParameters = parameters;
myBody = body;
}
PsiExpression getExpression() {
return myBody;
}
void transform(StreamToLoopReplacementContext context, String... newNames) {
LOG.assertTrue(newNames.length == myParameters.length);
EntryStream.zip(myParameters, newNames).forKeyValue(
(oldName, newName) -> myBody = renameVarReference(myBody, oldName, newName, context));
}
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
OptionalLong idx = StreamEx.of(myParameters).indexOf(newName);
if(idx.isPresent()) {
for(int i = 1;; i++) {
String paramName = newName+'$'+i;
if (!paramName.equals(oldName) &&
!StreamEx.of(myParameters).has(paramName)) {
try {
myBody = renameVarReference(myBody, newName, paramName, context);
myParameters[(int)idx.getAsLong()] = paramName;
break;
}
catch(IllegalStateException ise) {
// something is really wrong if we already have references to all newName$1, newName$2, ... newName$50
// or probably IllegalStateException was thrown by something else: at least we don't stuck in endless loop
if(i > 50) throw ise;
}
}
}
}
myBody = renameVarReference(myBody, oldName, newName, context);
}
@Override
void registerUsedNames(Consumer<String> consumer) {
processUsedNames(myBody, consumer);
}
String getParameterName(int index) {
return myParameters[index];
}
}
}

View File

@@ -0,0 +1,293 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection.streamToLoop;
import com.intellij.codeInspection.streamToLoop.StreamToLoopInspection.StreamToLoopReplacementContext;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiMethodCallExpression;
import com.intellij.psi.util.PsiTypesUtil;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Consumer;
/**
* @author Tagir Valeev
*/
abstract class Operation {
boolean changesVariable() {
return false;
}
StreamEx<StreamToLoopInspection.OperationRecord> nestedOperations() {
return StreamEx.empty();
}
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {}
abstract String wrap(StreamVariable inVar,
StreamVariable outVar,
String code,
StreamToLoopReplacementContext context);
void registerUsedNames(Consumer<String> usedNameConsumer) {
}
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {}
@Nullable
static Operation createIntermediate(String name, PsiExpression[] args, StreamVariable outVar) {
if(name.equals("distinct") && args.length == 0) {
return new DistinctOperation();
}
if(name.equals("skip") && args.length == 1) {
return new SkipOperation(args[0]);
}
if(name.equals("limit") && args.length == 1) {
return new LimitOperation(args[0]);
}
if(name.equals("filter") && args.length == 1) {
FunctionHelper fn = FunctionHelper.create(args[0], 1, true);
return fn == null ? null : new FilterOperation(fn);
}
if(name.equals("peek") && args.length == 1) {
FunctionHelper fn = FunctionHelper.create(args[0], 1, true);
return fn == null ? null : new PeekOperation(fn);
}
if((name.equals("boxed") || name.equals("asLongStream") || name.equals("asDoubleStream")) && args.length == 0) {
return new WideningOperation();
}
if ((name.equals("flatMap") || name.equals("flatMapToInt") || name.equals("flatMapToLong") || name.equals("flatMapToDouble")) &&
args.length == 1) {
return FlatMapOperation.from(outVar, args[0]);
}
if ((name.equals("map") ||
name.equals("mapToInt") ||
name.equals("mapToLong") ||
name.equals("mapToDouble") ||
name.equals("mapToObj")) && args.length == 1) {
FunctionHelper fn = FunctionHelper.create(args[0], 1, true);
return fn == null ? null : new MapOperation(fn);
}
return null;
}
static abstract class LambdaIntermediateOperation extends Operation {
final FunctionHelper myFn;
LambdaIntermediateOperation(FunctionHelper fn) {
myFn = fn;
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
myFn.registerUsedNames(usedNameConsumer);
}
@Override
final String wrap(StreamVariable inVar, StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
myFn.transform(context, inVar.getName());
return wrap(outVar, code, context);
}
abstract String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context);
@Override
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
myFn.rename(oldName, newName, context);
}
@Override
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
myFn.suggestVariableName(inVar, 0);
}
}
static class FilterOperation extends LambdaIntermediateOperation {
public FilterOperation(FunctionHelper fn) {
super(fn);
}
@Override
String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
return "if(" + myFn.getText() + ") {\n" + code + "}\n";
}
}
static class PeekOperation extends LambdaIntermediateOperation {
public PeekOperation(FunctionHelper fn) {
super(fn);
}
@Override
String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
return myFn.getText() + ";\n" + code;
}
}
static class MapOperation extends LambdaIntermediateOperation {
public MapOperation(FunctionHelper fn) {
super(fn);
}
@Override
String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
return outVar.getDeclaration() + " = " + myFn.getText() + ";\n" + code;
}
@Override
boolean changesVariable() {
return true;
}
}
static class WideningOperation extends Operation {
@Override
String wrap(StreamVariable inVar,
StreamVariable outVar,
String code,
StreamToLoopReplacementContext context) {
return outVar.getDeclaration() + " = " + inVar + ";\n" + code;
}
@Override
boolean changesVariable() {
return true;
}
}
static class FlatMapOperation extends Operation {
private final @Nullable String myOriginalName;
private final List<StreamToLoopInspection.OperationRecord> myRecords;
private FlatMapOperation(@Nullable String name, List<StreamToLoopInspection.OperationRecord> records) {
myOriginalName = name;
myRecords = records;
}
@Override
StreamEx<StreamToLoopInspection.OperationRecord> nestedOperations() {
return StreamEx.of(myRecords).flatMap(or -> StreamEx.of(or).append(or.myOperation.nestedOperations()));
}
@Override
boolean changesVariable() {
return true;
}
@Override
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
if(myOriginalName != null) {
inVar.addBestNameCandidate(myOriginalName);
}
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
myRecords.forEach(or -> or.myOperation.registerUsedNames(usedNameConsumer));
}
@Override
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
myRecords.forEach(or -> or.myOperation.rename(oldName, newName, context));
}
@Override
String wrap(StreamVariable inVar, StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
if(myOriginalName != null && !myOriginalName.equals(inVar.getName())) {
rename(myOriginalName, inVar.getName(), context);
}
StreamToLoopReplacementContext
innerContext = new StreamToLoopReplacementContext(context, myRecords);
String replacement = code;
for(StreamToLoopInspection.OperationRecord or : StreamEx.ofReversed(myRecords)) {
replacement = or.myOperation.wrap(or.myInVar, or.myOutVar, replacement, innerContext);
}
return StreamEx.of(innerContext.getDeclarations()).map(str -> str + "\n").joining()+replacement;
}
@Nullable
public static FlatMapOperation from(StreamVariable outVar, PsiExpression arg) {
FunctionHelper fn = FunctionHelper.create(arg, 1, true);
if(fn == null) return null;
PsiExpression body = fn.getExpression();
if(!(body instanceof PsiMethodCallExpression)) return null;
PsiMethodCallExpression terminalCall = (PsiMethodCallExpression)body;
List<StreamToLoopInspection.OperationRecord> records = StreamToLoopInspection.extractOperations(outVar, terminalCall);
if(records == null || StreamToLoopInspection.getTerminal(records) != null) return null;
return new FlatMapOperation(fn.getParameterName(0), records);
}
}
static class DistinctOperation extends Operation {
@Override
String wrap(StreamVariable inVar, StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
String set =
context.declare("uniqueValues", "java.util.Set<" + PsiTypesUtil.boxIfPossible(inVar.getType()) + ">", "new java.util.HashSet<>()");
return "if(" + set + ".add(" + inVar + ")) {\n" + code + "}\n";
}
}
static class SkipOperation extends Operation {
PsiExpression myExpression;
SkipOperation(PsiExpression expression) {
myExpression = expression;
}
@Override
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
myExpression = FunctionHelper.renameVarReference(myExpression, oldName, newName, context);
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
FunctionHelper.processUsedNames(myExpression, usedNameConsumer);
}
@Override
String wrap(StreamVariable inVar, StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
String toSkip = context.declare("toSkip", "long", myExpression.getText());
return "if(" + toSkip + ">0) {\n" + toSkip + "--;\ncontinue;\n}\n" + code;
}
}
static class LimitOperation extends Operation {
PsiExpression myExpression;
LimitOperation(PsiExpression expression) {
myExpression = expression;
}
@Override
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
myExpression = FunctionHelper.renameVarReference(myExpression, oldName, newName, context);
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
FunctionHelper.processUsedNames(myExpression, usedNameConsumer);
}
@Override
String wrap(StreamVariable inVar, StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
String limit = context.declare("limit", "long", myExpression.getText());
return "if(" + limit + "--==0) " + context.getBreakStatement() + code;
}
}
}

View File

@@ -0,0 +1,270 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection.streamToLoop;
import com.intellij.codeInspection.streamToLoop.StreamToLoopInspection.StreamToLoopReplacementContext;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.siyeh.ig.psiutils.ExpressionUtils;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.function.Consumer;
import static com.intellij.codeInspection.streamToLoop.FunctionHelper.processUsedNames;
import static com.intellij.codeInspection.streamToLoop.FunctionHelper.renameVarReference;
/**
* @author Tagir Valeev
*/
abstract class SourceOperation extends Operation {
@Contract(value = " -> true", pure = true)
@Override
final boolean changesVariable() {
return true;
}
@Override
final String wrap(StreamVariable inVar, StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
return wrap(outVar, code, context);
}
abstract String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context);
@Nullable
static SourceOperation createSource(PsiMethodCallExpression call) {
PsiExpression[] args = call.getArgumentList().getExpressions();
PsiType callType = call.getType();
if(callType == null) return null;
PsiMethod method = call.resolveMethod();
if(method == null) return null;
String name = method.getName();
PsiClass aClass = method.getContainingClass();
if(aClass == null) return null;
String className = aClass.getQualifiedName();
if(className == null) return null;
if ((name.equals("range") || name.equals("rangeClosed")) && args.length == 2 && method.getModifierList().hasExplicitModifier(
PsiModifier.STATIC) && (className.equals(CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM) ||
className.equals(CommonClassNames.JAVA_UTIL_STREAM_LONG_STREAM))) {
return new RangeSource(args[0], args[1], name.equals("rangeClosed"));
}
if (name.equals("of") && method.getModifierList().hasExplicitModifier(
PsiModifier.STATIC) && className.startsWith("java.util.stream.")) {
if(args.length == 1) {
PsiType type = args[0].getType();
if(type instanceof PsiArrayType) {
PsiType componentType = ((PsiArrayType)type).getComponentType();
if(StreamToLoopInspection.getStreamElementType(callType).isAssignableFrom(componentType)) {
return new ForEachSource(args[0]);
}
}
}
return new ExplicitSource(args);
}
if (name.equals("generate") && args.length == 1 && method.getModifierList().hasExplicitModifier(
PsiModifier.STATIC) && className.startsWith("java.util.stream.")) {
FunctionHelper fn = FunctionHelper.create(args[0], 0, true);
return fn == null ? null : new GenerateSource(fn);
}
if (name.equals("iterate") && args.length == 2 && method.getModifierList().hasExplicitModifier(
PsiModifier.STATIC) && className.startsWith("java.util.stream.")) {
FunctionHelper fn = FunctionHelper.create(args[1], 1, true);
return fn == null ? null : new IterateSource(args[0], fn);
}
if (name.equals("stream") && args.length == 0 &&
InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_UTIL_COLLECTION)) {
return new ForEachSource(call.getMethodExpression().getQualifierExpression());
}
if (name.equals("stream") && args.length == 1 &&
CommonClassNames.JAVA_UTIL_ARRAYS.equals(className)) {
return new ForEachSource(args[0]);
}
return null;
}
static class ForEachSource extends SourceOperation {
private PsiExpression myQualifier;
ForEachSource(PsiExpression qualifier) {
myQualifier = qualifier;
}
@Override
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
myQualifier = renameVarReference(myQualifier, oldName, newName, context);
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
processUsedNames(myQualifier, usedNameConsumer);
}
@Override
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
if(myQualifier instanceof PsiReferenceExpression) {
String name = ((PsiReferenceExpression)myQualifier).getReferenceName();
if(name != null) {
name = StringUtil.unpluralize(name);
if(name != null) {
outVar.addOtherNameCandidate(name);
}
}
}
}
@Override
public String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
return context.getLoopLabel() +
"for(" + outVar.getDeclaration() + ": " + (myQualifier == null ? "this" : myQualifier.getText()) + ") {" + code + "}\n";
}
}
static class ExplicitSource extends SourceOperation {
private PsiExpression[] myArgList;
ExplicitSource(PsiExpression[] argList) {
myArgList = argList;
}
@Override
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
Arrays.asList(myArgList).replaceAll(arg -> renameVarReference(arg, oldName, newName, context));
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
for(PsiExpression arg : myArgList) {
processUsedNames(arg, usedNameConsumer);
}
}
@Override
public String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
String type = outVar.getType();
String args = StreamEx.of(myArgList).map(PsiExpression::getText).joining(", ");
// TODO: remove type argument if redundant
String collection =
TypeConversionUtil.isPrimitive(type) ? "new " + type + "[] {" + args + "}" : "java.util.Arrays.<" + type + ">asList(" + args + ")";
return context.getLoopLabel() +
"for(" + outVar.getDeclaration() + ": " + collection + ") {" + code + "}\n";
}
}
static class GenerateSource extends SourceOperation {
private FunctionHelper myFn;
GenerateSource(FunctionHelper fn) {
myFn = fn;
}
@Override
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
myFn.rename(oldName, newName, context);
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
myFn.registerUsedNames(usedNameConsumer);
}
@Override
String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
myFn.transform(context);
return context.getLoopLabel() +
"while(true) {\n" +
outVar.getDeclaration() + "=" + myFn.getText() + ";\n" + code +
"}\n";
}
}
static class IterateSource extends SourceOperation {
private PsiExpression myInitializer;
private FunctionHelper myFn;
IterateSource(PsiExpression initializer, FunctionHelper fn) {
myInitializer = initializer;
myFn = fn;
}
@Override
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
myInitializer = renameVarReference(myInitializer, oldName, newName, context);
myFn.rename(oldName, newName, context);
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
processUsedNames(myInitializer, usedNameConsumer);
myFn.registerUsedNames(usedNameConsumer);
}
@Override
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
myFn.suggestVariableName(outVar, 0);
}
@Override
String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
myFn.transform(context, outVar.getName());
return context.getLoopLabel() +
"for(" + outVar.getDeclaration() + "=" + myInitializer.getText() + ";;" +
outVar + "=" + myFn.getText() + ") {\n" + code + "}\n";
}
}
static class RangeSource extends SourceOperation {
private PsiExpression myOrigin;
private PsiExpression myBound;
private boolean myInclusive;
RangeSource(PsiExpression origin, PsiExpression bound, boolean inclusive) {
myOrigin = origin;
myBound = bound;
myInclusive = inclusive;
}
@Override
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
myOrigin = renameVarReference(myOrigin, oldName, newName, context);
myBound = renameVarReference(myBound, oldName, newName, context);
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
processUsedNames(myOrigin, usedNameConsumer);
processUsedNames(myBound, usedNameConsumer);
}
@Override
String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
String bound;
if(!ExpressionUtils.isSimpleExpression(myBound)) {
bound = context.declare("bound", outVar.getType(), myBound.getText());
} else {
bound = myBound.getText();
}
return context.getLoopLabel() +
"for(" + outVar.getDeclaration() + " = " + myOrigin.getText() + ";" +
outVar + (myInclusive ? "<=" : "<") + bound + ";" +
outVar + "++) {\n" +
code + "}\n";
}
}
}

View File

@@ -0,0 +1,437 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection.streamToLoop;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
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 one.util.streamex.IntStreamEx;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.codeInspection.streamToLoop.Operation.FlatMapOperation;
/**
* @author Tagir Valeev
*/
public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
private static final Logger LOG = Logger.getInstance(StreamToLoopInspection.class);
// 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();
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
if (!PsiUtil.isLanguageLevel8OrHigher(holder.getFile())) {
return PsiElementVisitor.EMPTY_VISITOR;
}
return new JavaElementVisitor() {
@Override
public void visitMethodCallExpression(PsiMethodCallExpression call) {
super.visitMethodCallExpression(call);
PsiReferenceExpression expression = call.getMethodExpression();
if (!SUPPORTED_TERMINALS.contains(expression.getReferenceName()) || !isSupportedCodeLocation(call)) return;
PsiMethod method = call.resolveMethod();
if(method == null) return;
PsiClass aClass = method.getContainingClass();
if(!InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM)) return;
PsiMethodCallExpression currentCall = call;
while(true) {
Operation op = createOperationFromCall(StreamVariable.STUB, currentCall);
if(op == null) return;
if(op instanceof SourceOperation) {
holder.registerProblem(call, "Replace stream API chain with loop", new ReplaceStreamWithLoopFix());
return;
}
PsiExpression qualifier = currentCall.getMethodExpression().getQualifierExpression();
if(!(qualifier instanceof PsiMethodCallExpression)) return;
currentCall = (PsiMethodCallExpression)qualifier;
}
}
};
}
private static boolean isSupportedCodeLocation(PsiMethodCallExpression call) {
PsiElement cur = call;
PsiElement parent = cur.getParent();
while(parent instanceof PsiExpression) {
// TODO: support in single expression lambdas
if(parent instanceof PsiLambdaExpression) {
return false;
}
if(parent instanceof PsiPolyadicExpression) {
PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)parent;
IElementType type = polyadicExpression.getOperationTokenType();
if ((type.equals(JavaTokenType.ANDAND) || type.equals(JavaTokenType.OROR)) && polyadicExpression.getOperands()[0] != cur) {
// not the first in the &&/|| chain: we cannot properly generate code which would short-circuit as well
return false;
}
}
if(parent instanceof PsiConditionalExpression && ((PsiConditionalExpression)parent).getCondition() != cur) {
return false;
}
cur = parent;
parent = cur.getParent();
}
if(parent instanceof PsiExportsStatement || parent instanceof PsiReturnStatement || parent instanceof PsiExpressionStatement) return true;
if(parent instanceof PsiLocalVariable) {
PsiElement grandParent = parent.getParent();
if(grandParent instanceof PsiDeclarationStatement && ((PsiDeclarationStatement)grandParent).getDeclaredElements().length == 1) {
return true;
}
}
if(parent instanceof PsiForeachStatement && ((PsiForeachStatement)parent).getIteratedValue() == cur) return true;
if(parent instanceof PsiIfStatement && ((PsiIfStatement)parent).getCondition() == cur) return true;
return false;
}
@Nullable
static Operation createOperationFromCall(StreamVariable outVar, PsiMethodCallExpression call) {
PsiMethod method = call.resolveMethod();
if(method == null) return null;
PsiClass aClass = method.getContainingClass();
if(aClass == null) return null;
PsiExpression[] args = call.getArgumentList().getExpressions();
String name = method.getName();
String className = aClass.getQualifiedName();
if(className == null) return null;
PsiType callType = call.getType();
if(callType == null) return null;
if(InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM)) {
Operation op = Operation.createIntermediate(name, args, outVar);
if (op != null) return op;
op = TerminalOperation.createTerminal(name, args, callType, className, call.getParent() instanceof PsiExpressionStatement);
if(op != null) return op;
}
return SourceOperation.createSource(call);
}
@Nullable
static List<OperationRecord> extractOperations(StreamVariable outVar, PsiMethodCallExpression terminalCall) {
List<OperationRecord> operations = new ArrayList<>();
PsiMethodCallExpression currentCall = terminalCall;
StreamVariable lastVar = outVar;
while(true) {
Operation op = createOperationFromCall(lastVar, currentCall);
if(op == null) return null;
OperationRecord or = new OperationRecord();
or.myOperation = op;
or.myOutVar = lastVar;
operations.add(or);
if(op instanceof SourceOperation) {
or.myInVar = StreamVariable.STUB;
Collections.reverse(operations);
return operations;
}
PsiExpression qualifier = currentCall.getMethodExpression().getQualifierExpression();
if(!(qualifier instanceof PsiMethodCallExpression)) return null;
currentCall = (PsiMethodCallExpression)qualifier;
if(op.changesVariable()) {
PsiType type = getStreamElementType(currentCall.getType());
if(type == null) return null;
lastVar = new StreamVariable(type);
}
or.myInVar = lastVar;
}
}
@Contract("null -> null")
@Nullable
static TerminalOperation getTerminal(List<OperationRecord> operations) {
if (operations == null || operations.isEmpty()) return null;
OperationRecord record = operations.get(operations.size()-1);
if(record.myOperation instanceof TerminalOperation) {
return (TerminalOperation)record.myOperation;
}
return null;
}
static class ReplaceStreamWithLoopFix implements LocalQuickFix {
@Nls
@NotNull
@Override
public String getFamilyName() {
return "Replace Stream API chain with loop";
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getStartElement();
if(!(element instanceof PsiMethodCallExpression)) return;
PsiMethodCallExpression terminalCall = (PsiMethodCallExpression)element;
if(!isSupportedCodeLocation(terminalCall)) return;
boolean inReturn = terminalCall.getParent() instanceof PsiReturnStatement;
List<OperationRecord> operations = extractOperations(StreamVariable.STUB, terminalCall);
TerminalOperation terminal = getTerminal(operations);
if (terminal == null) return;
annotateOperations(project, operations);
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
if(!FileModificationService.getInstance().preparePsiElementForWrite(element)) return;
terminalCall = ensureCodeBlock(terminalCall, factory);
if(terminalCall == null) return;
PsiStatement statement = PsiTreeUtil.getParentOfType(terminalCall, PsiStatement.class);
LOG.assertTrue(statement != null);
PsiElement temporaryStreamPlaceholder = terminalCall.replace(factory.createExpressionFromText("$streamReplacement$", terminalCall));
try {
StreamToLoopReplacementContext context = new StreamToLoopReplacementContext(statement, operations, inReturn);
registerVariables(operations, context);
String replacement = "";
for (OperationRecord or : StreamEx.ofReversed(operations)) {
replacement = or.myOperation.wrap(or.myInVar, or.myOutVar, replacement, context);
}
for (String declaration : context.getDeclarations()) {
addStatement(project, statement, factory.createStatementFromText(declaration, statement));
}
for (PsiStatement addedStatement : ((PsiBlockStatement)factory.createStatementFromText("{" + replacement + "}", statement))
.getCodeBlock().getStatements()) {
addStatement(project, statement, addedStatement);
}
String finisher = context.getFinisher();
if (finisher == null) {
temporaryStreamPlaceholder.delete();
}
else {
temporaryStreamPlaceholder.replace(factory.createExpressionFromText(finisher, temporaryStreamPlaceholder));
}
}
catch (Exception ex) {
if(temporaryStreamPlaceholder.isPhysical()) {
// Just in case if something went wrong: at least try to restore the original stream code
temporaryStreamPlaceholder.replace(factory.createExpressionFromText(terminalCall.getText(), temporaryStreamPlaceholder));
}
throw ex;
}
}
void addStatement(@NotNull Project project, PsiStatement statement, PsiStatement context) {
CodeStyleManager.getInstance(project)
.reformat(JavaCodeStyleManager.getInstance(project).shortenClassReferences(statement.getParent().addBefore(context, statement)));
}
@Nullable
private static PsiMethodCallExpression ensureCodeBlock(PsiMethodCallExpression expression, PsiElementFactory factory) {
PsiStatement statement = PsiTreeUtil.getParentOfType(expression, PsiStatement.class);
if(statement == null) return null;
if(!(statement.getParent() instanceof PsiCodeBlock)) {
PsiElement nameElement = expression.getMethodExpression().getReferenceNameElement();
if(nameElement == null) return null;
int delta = nameElement.getTextOffset() - statement.getTextOffset();
PsiElement blockStatement = statement.replace(factory.createStatementFromText("{" + statement.getText() + "}", statement));
PsiStatement newStatement = ((PsiBlockStatement)blockStatement).getCodeBlock().getStatements()[0];
int targetOffset = newStatement.getTextOffset() + delta;
PsiElement element = PsiUtilCore.getElementAtOffset(newStatement.getContainingFile(), targetOffset);
PsiMethodCallExpression newExpression = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class);
LOG.assertTrue(newExpression != null);
return newExpression;
}
return expression;
}
private static StreamEx<OperationRecord> allOperations(List<OperationRecord> operations) {
return StreamEx.of(operations)
.flatMap(or -> or.myOperation.nestedOperations().append(or));
}
private static void annotateOperations(@NotNull Project project, List<OperationRecord> operations) {
JavaCodeStyleManager javaCodeStyleManager = JavaCodeStyleManager.getInstance(project);
allOperations(operations)
.peek(or -> or.myOperation.suggestNames(or.myInVar, or.myOutVar))
.flatMap(or -> StreamEx.of(or.myInVar, or.myOutVar)).distinct()
.forEach(var -> var.addCandidatesFromType(javaCodeStyleManager));
}
private static void registerVariables(List<OperationRecord> operations, StreamToLoopReplacementContext context) {
allOperations(operations).map(or -> or.myOperation).forEach(op -> op.registerUsedNames(context::addUsedVar));
allOperations(operations).map(or -> or.myInVar).distinct().forEach(var -> var.register(context));
}
}
@Contract("null -> null")
static PsiType getStreamElementType(PsiType type) {
if(!(type instanceof PsiClassType)) return null;
PsiClass aClass = ((PsiClassType)type).resolve();
if(InheritanceUtil.isInheritor(aClass, false, CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM)) {
return PsiType.INT;
}
if(InheritanceUtil.isInheritor(aClass, false, CommonClassNames.JAVA_UTIL_STREAM_LONG_STREAM)) {
return PsiType.LONG;
}
if(InheritanceUtil.isInheritor(aClass, false, CommonClassNames.JAVA_UTIL_STREAM_DOUBLE_STREAM)) {
return PsiType.DOUBLE;
}
PsiType[] parameters = ((PsiClassType)type).getParameters();
if(parameters.length != 1) return null;
PsiType streamType = parameters[0];
if(streamType instanceof PsiCapturedWildcardType) {
streamType = ((PsiCapturedWildcardType)streamType).getUpperBound();
}
return streamType;
}
static class StreamToLoopReplacementContext {
private final boolean myHasNestedLoops;
private final boolean myInReturn;
private final String mySuffix;
private final PsiStatement myStatement;
private final Set<String> myUsedNames;
private final Set<String> myUsedLabels;
private final List<String> myDeclarations = new ArrayList<>();
private String myLabel;
private String myFinisher;
StreamToLoopReplacementContext(PsiStatement statement, List<OperationRecord> records, boolean inReturn) {
myStatement = statement;
myHasNestedLoops = records.stream().anyMatch(or -> or.myOperation instanceof FlatMapOperation);
myInReturn = inReturn;
mySuffix = myHasNestedLoops ? "Outer" : "";
myUsedNames = new HashSet<>();
myUsedLabels = StreamEx.iterate(statement, Objects::nonNull, PsiElement::getParent).select(PsiLabeledStatement.class)
.map(PsiLabeledStatement::getName).toSet();
}
StreamToLoopReplacementContext(StreamToLoopReplacementContext parentContext, List<OperationRecord> records) {
myUsedNames = parentContext.myUsedNames;
myUsedLabels = parentContext.myUsedLabels;
myInReturn = false;
myStatement = parentContext.myStatement;
myHasNestedLoops = records.stream().anyMatch(or -> or.myOperation instanceof FlatMapOperation);
mySuffix = "Inner";
}
public void addUsedVar(String name) {
myUsedNames.add(name);
}
@Nullable
private String allocateLabel() {
if(!myHasNestedLoops) return null;
if(myLabel == null) {
String base = mySuffix.toUpperCase(Locale.ENGLISH);
myLabel = IntStreamEx.ints().mapToObj(i -> i == 0 ? base : base + i)
.remove(myUsedLabels::contains).findFirst().orElseThrow(IllegalArgumentException::new);
myUsedLabels.add(myLabel);
}
return myLabel;
}
public String getLoopLabel() {
return myLabel == null ? "" : myLabel + ":\n";
}
public String getBreakStatement() {
String label = allocateLabel();
return label == null ? "break;\n" : "break "+label+";\n";
}
public List<String> getDeclarations() {
return myDeclarations;
}
public String registerVarName(Collection<String> variants) {
// TODO: avoid introducing conflicts with nested scopes (variables declared in lambdas, nested lambdas)
if(variants.isEmpty()) {
return registerVarName(Collections.singleton("val"));
}
for(int idx = 0; ; idx++) {
for(String variant : variants) {
String name = idx == 0 ? variant : variant + idx;
if(!isUsed(name)) {
myUsedNames.add(name);
return name;
}
}
}
}
private boolean isUsed(String varName) {
if(myUsedNames.contains(varName)) return true;
// TODO: cleaner solution
return !varName.equals(JavaCodeStyleManager.getInstance(myStatement.getProject()).suggestUniqueVariableName(varName, myStatement, true));
}
public String declare(String desiredName, String type, String initializer) {
String name = registerVarName(
mySuffix.isEmpty() ? Collections.singleton(desiredName) : Arrays.asList(desiredName, desiredName + mySuffix));
myDeclarations.add(type + " " + name + " = " + initializer + ";");
return name;
}
public String assignAndBreak(String desiredName, String type, String foundValue, String notFoundValue) {
if(myInReturn) {
setFinisher(notFoundValue);
return "return "+foundValue+";";
}
String found = declareResult(desiredName, type, notFoundValue);
return found + " = " +foundValue+";\n" + getBreakStatement();
}
public String declareResult(String desiredName, String type, String initializer) {
String name = registerVarName(Arrays.asList(desiredName, "result"));
myDeclarations.add(type + " " + name + " = " + initializer + ";");
if(myFinisher != null) {
throw new IllegalStateException("Finisher is already defined");
}
setFinisher(name);
return name;
}
public String getFinisher() {
return myFinisher;
}
public void setFinisher(String finisher) {
myFinisher = finisher;
}
public Project getProject() {
return myStatement.getProject();
}
public PsiExpression createExpression(String text) {
return JavaPsiFacade.getElementFactory(myStatement.getProject()).createExpressionFromText(text, myStatement);
}
public PsiType createType(String text) {
return JavaPsiFacade.getElementFactory(myStatement.getProject()).createTypeFromText(text, myStatement);
}
}
static class OperationRecord {
Operation myOperation;
StreamVariable myInVar, myOutVar;
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection.streamToLoop;
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;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
/**
* This class represents a variable which holds stream element. Its lifecycle is the following:
* 1. Construction: fast, in case you don't need to perform a fix actually
* 2. Gather name candidates (addBestNameCandidate/addOtherNameCandidate can be called).
* 3. addCandidatesFromType called which adds name candidates based on type
* (until this stage no changes in original file should be performed)
* 4. Register variable in {@code StreamToLoopReplacementContext}: actual variable name is assigned here
* 5. Usage in code generation: getName()/getType() could be called.
*
* @author Tagir Valeev
*/
class StreamVariable {
private static final Logger LOG = Logger.getInstance(StreamVariable.class);
static StreamVariable STUB = new StreamVariable(PsiType.VOID) {
@Override
public void addBestNameCandidate(String candidate) {
}
@Override
public void addCandidatesFromType(JavaCodeStyleManager manager) {
}
@Override
void register(StreamToLoopInspection.StreamToLoopReplacementContext context) {
}
@Override
public String toString() {
return "###STUB###";
}
};
String myName;
String myType;
PsiType myPsiType;
private Collection<String> myBestCandidates = new LinkedHashSet<>();
private Collection<String> myOtherCandidates = new LinkedHashSet<>();
StreamVariable(@NotNull PsiType type) {
myPsiType = type;
}
/**
* Register best name candidate for this variable (like lambda argument which was explicitly present in the original code).
*
* @param candidate name candidate
*/
void addBestNameCandidate(String candidate) {
myBestCandidates.add(candidate);
}
/**
* Register normal name candidate for this variable (for example, derived using unpluralize from collection name, etc.)
*
* @param candidate name candidate
*/
void addOtherNameCandidate(String candidate) {
myOtherCandidates.add(candidate);
}
/**
* Register name candidates based on variable type.
* This must be called only once and only after addBestNameCandidate/addOtherNameCandidate.
*
* @param manager project-specific {@link JavaCodeStyleManager}
*/
void addCandidatesFromType(JavaCodeStyleManager manager) {
LOG.assertTrue(myPsiType != null);
LOG.assertTrue(myType == null);
myOtherCandidates.addAll(Arrays.asList(manager.suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, myPsiType, true).names));
myType = myPsiType.getCanonicalText();
myPsiType = null;
}
/**
* Register variable within {@link com.intellij.codeInspection.streamToLoop.StreamToLoopInspection.StreamToLoopReplacementContext}.
* Must be called once after all name candidates are registered. Now variable gets an actual name.
*
* @param context context to use
*/
void register(StreamToLoopInspection.StreamToLoopReplacementContext context) {
LOG.assertTrue(myName == null);
List<String> variants = StreamEx.of(myBestCandidates).append(myOtherCandidates).distinct().toList();
if (variants.isEmpty()) variants.add("val");
myName = context.registerVarName(variants);
myBestCandidates = myOtherCandidates = null;
}
String getName() {
LOG.assertTrue(myName != null);
return myName;
}
String getType() {
LOG.assertTrue(myType != null);
return myType;
}
String getDeclaration() {
return getType() + " " + getName();
}
@Override
public String toString() {
if (myName == null) {
return "###(unregistered: " + myBestCandidates + "|" + myOtherCandidates + ")###";
}
return myName;
}
}

View File

@@ -0,0 +1,447 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInspection.streamToLoop;
import com.intellij.codeInspection.streamToLoop.StreamToLoopInspection.StreamToLoopReplacementContext;
import com.intellij.psi.*;
import com.intellij.psi.util.TypeConversionUtil;
import com.siyeh.ig.psiutils.BoolUtils;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Consumer;
/**
* @author Tagir Valeev
*/
abstract class TerminalOperation extends Operation {
@Override
final String wrap(StreamVariable inVar, StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
return generate(inVar, context);
}
@Override
final void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
throw new IllegalStateException("Should not be called for terminal operation (tried to rename " + oldName + " -> " + newName + ")");
}
@Override
final boolean changesVariable() {
return true;
}
abstract String generate(StreamVariable inVar, StreamToLoopReplacementContext context);
@NotNull
@Contract(pure = true)
private static String getOptionalClass(String type) {
switch (type) {
case "int":
return "java.util.OptionalInt";
case "long":
return "java.util.OptionalLong";
case "double":
return "java.util.OptionalDouble";
default:
return "java.util.Optional";
}
}
@Nullable
static TerminalOperation createTerminal(String name, PsiExpression[] args, PsiType callType, String className, boolean isVoid) {
if(isVoid) {
if ((name.equals("forEach") || name.equals("forEachOrdered")) && args.length == 1) {
FunctionHelper fn = FunctionHelper.create(args[0], 1, true);
return fn == null ? null : new ForEachTerminalOperation(fn);
}
return null;
}
if(name.equals("count") && args.length == 0) {
return new AccumulatedTerminalOperation("count", "long", "0", "{acc}++;");
}
if(name.equals("sum") && args.length == 0) {
return new AccumulatedTerminalOperation("sum", callType.getCanonicalText(), "0", "{acc}+={item};");
}
if(name.equals("average") && args.length == 0) {
if(className.equals(CommonClassNames.JAVA_UTIL_STREAM_DOUBLE_STREAM)) {
return new AverageTerminalOperation(true);
}
else if(className.equals(CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM) ||
className.equals(CommonClassNames.JAVA_UTIL_STREAM_LONG_STREAM)) {
return new AverageTerminalOperation(false);
}
}
if(name.equals("summaryStatistics") && args.length == 0) {
return new AccumulatedTerminalOperation("stat", callType.getCanonicalText(), "new " + callType.getCanonicalText() + "()",
"{acc}.accept({item});");
}
if((name.equals("findFirst") || name.equals("findAny")) && args.length == 0) {
return new FindTerminalOperation(callType.getCanonicalText());
}
if((name.equals("anyMatch") || name.equals("allMatch") || name.equals("noneMatch")) && args.length == 1) {
FunctionHelper fn = FunctionHelper.create(args[0], 1, true);
return fn == null ? null : new MatchTerminalOperation(fn, name);
}
if(name.equals("reduce")) {
if(args.length == 2 || args.length == 3) {
FunctionHelper fn = FunctionHelper.create(args[1], 2, true);
if(fn != null) {
return new ReduceTerminalOperation(args[0], fn, callType.getCanonicalText());
}
}
if(args.length == 1) {
PsiType optionalElementType = getOptionalElementType(callType);
FunctionHelper fn = FunctionHelper.create(args[0], 2, true);
if(fn != null && optionalElementType != null) {
return new ReduceToOptionalTerminalOperation(fn, optionalElementType.getCanonicalText());
}
}
}
if(name.equals("toArray") && args.length < 2) {
if(!(callType instanceof PsiArrayType)) return null;
PsiType componentType = ((PsiArrayType)callType).getComponentType();
if (componentType instanceof PsiPrimitiveType) {
if(args.length == 0) return new ToPrimitiveArrayTerminalOperation(componentType.getCanonicalText());
}
else {
String arr = "";
if(args.length == 1) {
if(!(args[0] instanceof PsiMethodReferenceExpression)) return null;
PsiMethodReferenceExpression arrCtor = (PsiMethodReferenceExpression)args[0];
if(!arrCtor.isConstructor()) return null;
PsiTypeElement typeElement = arrCtor.getQualifierType();
if(typeElement == null) return null;
PsiType type = typeElement.getType();
if(!(type instanceof PsiArrayType)) return null;
arr = "new "+type.getCanonicalText().replaceFirst("\\[]", "[0]");
}
return new AccumulatedTerminalOperation("list", CommonClassNames.JAVA_UTIL_LIST + "<" + componentType.getCanonicalText() + ">",
"new "+ CommonClassNames.JAVA_UTIL_ARRAY_LIST+"<>()", "{acc}.add({item});",
"{acc}.toArray("+arr+")");
}
}
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(callType, CommonClassNames.JAVA_UTIL_ARRAY_LIST, "list");
}
if(collector.getName().equals("toSet") && collectorArgs.length == 0) {
return AccumulatedTerminalOperation.toCollection(callType, CommonClassNames.JAVA_UTIL_HASH_SET, "set");
}
if(collector.getName().equals("toCollection") && collectorArgs.length == 1) {
FunctionHelper fn = FunctionHelper.create(collectorArgs[0], 0, true);
if(fn != null) {
return new ToCollectionTerminalOperation(fn, callType);
}
}
if(collector.getName().equals("reducing") && collectorArgs.length == 2) {
FunctionHelper fn = FunctionHelper.create(collectorArgs[1], 2, true);
if(fn != null) {
return new ReduceTerminalOperation(collectorArgs[0], fn, callType.getCanonicalText());
}
}
if(collector.getName().equals("reducing") && collectorArgs.length == 1) {
PsiType optionalElementType = getOptionalElementType(callType);
FunctionHelper fn = FunctionHelper.create(collectorArgs[0], 2, true);
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()");
}
}
}
}
}
return null;
}
@Contract("null -> null")
static PsiType getOptionalElementType(PsiType type) {
if(!(type instanceof PsiClassType)) return null;
PsiClass aClass = ((PsiClassType)type).resolve();
if(aClass == null) return null;
if("java.util.OptionalInt".equals(aClass.getQualifiedName())) {
return PsiType.INT;
}
if("java.util.OptionalLong".equals(aClass.getQualifiedName())) {
return PsiType.LONG;
}
if("java.util.OptionalDouble".equals(aClass.getQualifiedName())) {
return PsiType.DOUBLE;
}
if(!CommonClassNames.JAVA_UTIL_OPTIONAL.equals(aClass.getQualifiedName())) return null;
PsiType[] parameters = ((PsiClassType)type).getParameters();
if(parameters.length != 1) return null;
PsiType streamType = parameters[0];
if(streamType instanceof PsiCapturedWildcardType) {
streamType = ((PsiCapturedWildcardType)streamType).getUpperBound();
}
return streamType;
}
static class ReduceTerminalOperation extends TerminalOperation {
private PsiExpression myIdentity;
private String myType;
private FunctionHelper myUpdater;
public ReduceTerminalOperation(PsiExpression identity, FunctionHelper updater, String type) {
myIdentity = identity;
myType = type;
myUpdater = updater;
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
FunctionHelper.processUsedNames(myIdentity, usedNameConsumer);
myUpdater.registerUsedNames(usedNameConsumer);
}
@Override
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
String accumulator = context.declareResult("acc", myType, myIdentity.getText());
myUpdater.transform(context, accumulator, inVar.getName());
return accumulator + "=" + myUpdater.getText() + ";";
}
}
static class ReduceToOptionalTerminalOperation extends TerminalOperation {
private String myType;
private FunctionHelper myUpdater;
public ReduceToOptionalTerminalOperation(FunctionHelper updater, String type) {
myType = type;
myUpdater = updater;
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
myUpdater.registerUsedNames(usedNameConsumer);
}
@Override
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
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 = getOptionalClass(myType);
context.setFinisher("(" + seen + "?" + optionalClass + ".of(" + accumulator + "):" + optionalClass + ".empty())");
return "if(!" + seen + ") {\n" +
seen + "=true;\n" +
accumulator + "=" + inVar + ";\n" +
"} else {\n" +
accumulator + "=" + myUpdater.getText() + ";\n" +
"}\n";
}
}
static class AverageTerminalOperation extends TerminalOperation {
private boolean myDoubleAccumulator;
public AverageTerminalOperation(boolean doubleAccumulator) {
myDoubleAccumulator = doubleAccumulator;
}
@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+"))");
return sum + "+=" + inVar + ";\n" + count + "++;\n";
}
}
static class ToPrimitiveArrayTerminalOperation extends TerminalOperation {
private String myType;
ToPrimitiveArrayTerminalOperation(String type) {
myType = type;
}
@Override
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
String arr = context.declareResult("arr", myType + "[]", "new " + myType + "[10]");
String count = context.declare("count", "int", "0");
context.setFinisher("java.util.Arrays.copyOfRange("+arr+",0,"+count+")");
return "if(" + arr + ".length==" + count + ") " + arr + "=java.util.Arrays.copyOf(" + arr + "," + count + "*2);\n" +
arr + "[" + count + "++]=" + inVar + ";\n";
}
}
static class FindTerminalOperation extends TerminalOperation {
private String myType;
public FindTerminalOperation(String type) {
myType = type;
}
@Override
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
int pos = myType.indexOf('<');
String optType = pos == -1 ? myType : myType.substring(0, pos);
return context.assignAndBreak("found", myType, optType + ".of(" + inVar + ")", optType + ".empty()");
}
}
static class MatchTerminalOperation extends TerminalOperation {
private final FunctionHelper myFn;
private final String myName;
private final boolean myDefaultValue, myNegatePredicate;
public MatchTerminalOperation(FunctionHelper fn, String name) {
myFn = fn;
switch(name) {
case "anyMatch":
myName = "found";
myDefaultValue = false;
myNegatePredicate = false;
break;
case "allMatch":
myName = "allMatch";
myDefaultValue = true;
myNegatePredicate = true;
break;
case "noneMatch":
myName = "noneMatch";
myDefaultValue = true;
myNegatePredicate = false;
break;
default:
throw new IllegalArgumentException(name);
}
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
myFn.registerUsedNames(usedNameConsumer);
}
@Override
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
myFn.suggestVariableName(inVar, 0);
}
@Override
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
myFn.transform(context, inVar.getName());
String expression = myNegatePredicate ? BoolUtils.getNegatedExpressionText(myFn.getExpression()) : myFn.getText();
return "if(" + expression + ") {\n" +
context.assignAndBreak(myName, PsiType.BOOLEAN.getCanonicalText(), String.valueOf(!myDefaultValue), String.valueOf(myDefaultValue)) +
"}\n";
}
}
static class AccumulatedTerminalOperation extends TerminalOperation {
private String myAccName;
private String myAccType;
private String myAccInitializer;
private String myUpdateTemplate;
private String myFinisherTemplate;
/**
* @param accName desired name for accumulator variable
* @param accType type of accumulator variable
* @param accInitializer initializer for accumulator variable
* @param updateTemplate template to update accumulator. May contain {@code {acc}} - reference to accumulator variable
* and {@code {item}} - reference to stream element.
* @param finisherTemplate template to final result. May contain {@code {acc}} - reference to accumulator variable.
* By default it's {@code "{acc}"}
*/
AccumulatedTerminalOperation(String accName, String accType, String accInitializer, String updateTemplate, String finisherTemplate) {
myAccName = accName;
myAccType = accType;
myAccInitializer = accInitializer;
myUpdateTemplate = updateTemplate;
myFinisherTemplate = finisherTemplate;
}
AccumulatedTerminalOperation(String accName, String accType, String accInitializer, String updateTemplate) {
this(accName, accType, accInitializer, updateTemplate, "{acc}");
}
@Override
public String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
String varName = context.declareResult(myAccName, myAccType, myAccInitializer);
context.setFinisher(myFinisherTemplate.replace("{acc}", varName));
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
void registerUsedNames(Consumer<String> usedNameConsumer) {
myFn.registerUsedNames(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";
}
}
static class ForEachTerminalOperation extends TerminalOperation {
private FunctionHelper myFn;
public ForEachTerminalOperation(FunctionHelper fn) {
myFn = fn;
}
@Override
void registerUsedNames(Consumer<String> usedNameConsumer) {
myFn.registerUsedNames(usedNameConsumer);
}
@Override
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
myFn.transform(context, inVar.getName());
return myFn.getText()+";\n";
}
}
}

View File

@@ -0,0 +1,25 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import static java.util.Arrays.asList;
public class Main {
public static boolean test(List<List<String>> list) {
for (List<String> x : list) {
if (x != null) {
for (String s : x) {
if (!s.startsWith("a")) {
return false;
}
}
}
}
return true;
}
public static void main(String[] args) {
System.out.println(test(asList(asList(), asList("a"), asList("b", "c"))));
System.out.println(test(asList(asList(), asList("d"), asList("b", "c"))));
}
}

View File

@@ -0,0 +1,30 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import static java.util.Arrays.asList;
public class Main {
public static void test(List<List<String>> list) {
boolean allMatch = true;
OUTER:
for (List<String> x : list) {
if (x != null) {
for (String s : x) {
if (!s.startsWith("a")) {
allMatch = false;
break OUTER;
}
}
}
}
if(allMatch) {
System.out.println("ok");
}
}
public static void main(String[] args) {
System.out.println(test(asList(asList(), asList("a"), asList("b", "c"))));
System.out.println(test(asList(asList(), asList("d"), asList("b", "c"))));
}
}

View File

@@ -0,0 +1,25 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import static java.util.Arrays.asList;
public class Main {
public static boolean test(List<List<String>> list) {
for (List<String> x : list) {
if (x != null) {
for (String s : x) {
if (s.startsWith("a")) {
return true;
}
}
}
}
return false;
}
public static void main(String[] args) {
System.out.println(test(asList(asList(), asList("a"), asList("b", "c"))));
System.out.println(test(asList(asList(), asList("d"), asList("b", "c"))));
}
}

View File

@@ -0,0 +1,27 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class Main {
private static long test() {
long count = 0;
Set<Integer> uniqueValues = new HashSet<>();
long toSkip = 1;
for (Integer integer : new Integer[]{1, 2, 3, 2, 3}) {
if (toSkip > 0) {
toSkip--;
continue;
}
if (uniqueValues.add(integer)) {
count++;
}
}
return count;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,23 @@
// "Replace Stream API chain with loop" "true"
import java.util.OptionalDouble;
import java.util.stream.LongStream;
public class Main {
private static OptionalDouble test(long... numbers) {
double sum = 0;
long count = 0;
for (long x : numbers) {
if (x > 0) {
double v = x;
sum += v;
count++;
}
}
return (count == 0 ? OptionalDouble.empty() : OptionalDouble.of(sum / count));
}
public static void main(String[] args) {
System.out.println(test(-1,-2,-3, Long.MAX_VALUE, Long.MAX_VALUE));
}
}

View File

@@ -0,0 +1,22 @@
// "Replace Stream API chain with loop" "true"
import java.util.OptionalDouble;
import java.util.stream.IntStream;
public class Main {
private static OptionalDouble test(int... numbers) {
long sum = 0;
long count = 0;
for (int x : numbers) {
if (x > 0) {
sum += x;
count++;
}
}
return (count == 0 ? OptionalDouble.empty() : OptionalDouble.of((double) sum / count));
}
public static void main(String[] args) {
System.out.println(test(-1,-2,-3));
}
}

View File

@@ -0,0 +1,22 @@
// "Replace Stream API chain with loop" "true"
import java.util.OptionalDouble;
import java.util.stream.LongStream;
public class Main {
private static OptionalDouble test(long... numbers) {
long sum = 0;
long count = 0;
for (long x : numbers) {
if (x > 0) {
sum += x;
count++;
}
}
return (count == 0 ? OptionalDouble.empty() : OptionalDouble.of((double) sum / count));
}
public static void main(String[] args) {
System.out.println(test(-1,-2,-3, Long.MAX_VALUE, Long.MAX_VALUE));
}
}

View File

@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
private static List<Integer> test(int[] numbers) {
List<Integer> list = new ArrayList<>();
for (int number : numbers) {
Integer integer = number;
list.add(integer);
}
return list;
}
public static void main(String[] args) {
System.out.println(test(new int[] {1,2,3}));
}
}

View File

@@ -0,0 +1,27 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static java.util.Arrays.asList;
public class Main {
public static Optional<String> test(List<String> list) {
boolean seen = false;
String acc = null;
for (String s : list) {
if (!seen) {
seen = true;
acc = s;
} else {
acc = acc + s;
}
}
return (seen ? Optional.of(acc) : Optional.empty());
}
public static void main(String[] args) {
System.out.println(test(asList("a", "b", "c")));
}
}

View File

@@ -0,0 +1,20 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import java.util.stream.Collectors;
import static java.util.Arrays.asList;
public class Main {
public static String test(List<String> list) {
String acc = "";
for (String s : list) {
acc = acc + s;
}
return acc;
}
public static void main(String[] args) {
System.out.println(test(asList("a", "b", "c")));
}
}

View File

@@ -0,0 +1,14 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
public class Main {
public void test(List<String> list) {
long count = 0;
for (String s : list) {
count++;
}
long x = count;
System.out.println(x);
}
}

View File

@@ -0,0 +1,16 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
public class Main {
static class Count {
}
public long test(List<Count> count) {
long result = 0;
for (Count count1 : count) {
result++;
}
return result;
}
}

View File

@@ -0,0 +1,16 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
public class Main {
public long test(List<String> list) {
if(!list.isEmpty()) {
long count = 0;
for (String s : list) {
count++;
}
return count;
}
return -1;
}
}

View File

@@ -0,0 +1,13 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
public class Main {
public long test(List<String> list) {
long count = 0;
for (String s : list) {
count++;
}
return count;
}
}

View File

@@ -0,0 +1,18 @@
// "Replace Stream API chain with loop" "true"
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Main {
public long test(List<String> list) {
long count = 0;
Set<String> uniqueValues = new HashSet<>();
for (String s : list) {
if (uniqueValues.add(s)) {
count++;
}
}
return count;
}
}

View File

@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
private static List<Object> test(List<? extends Number> numbers) {
List<Object> list = new ArrayList<>();
Set<Number> uniqueValues = new HashSet<>();
for (Number number : numbers) {
if (uniqueValues.add(number)) {
list.add(number);
}
}
return list;
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList(1,2,3,5,3,2,2,2,1,1,4,3)));
}
}

View File

@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
public class Main {
private static long countInRange(int... input) {
long count = 0;
for (int x : input) {
if (x > 0) {
if (x < 10) {
count++;
}
}
}
return count;
}
public static void main(String[] args) {
System.out.println(countInRange(1, 2, 3, -1, -2));
}
}

View File

@@ -0,0 +1,22 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
public class Main {
private static long countInRange(int... input) {
int x = 1;
long count = 0;
for (int y : input) {
if (y > 0) {
if (y < 10) {
count++;
}
}
}
return count;
}
public static void main(String[] args) {
System.out.println(countInRange(1, 2, 3, -1, -2));
}
}

View File

@@ -0,0 +1,23 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
public class Main {
private static long countInRange(int... input) {
int x = 1;
int y = 2;
long count = 0;
for (int i : input) {
if (i > 0) {
if (i < 10) {
count++;
}
}
}
return count;
}
public static void main(String[] args) {
System.out.println(countInRange(1, 2, 3, -1, -2));
}
}

View File

@@ -0,0 +1,24 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
public class Main {
private static long countInRange(int... input) {
int x = 1;
int y = 2;
int i = 3;
long count = 0;
for (int x1 : input) {
if (x1 > 0) {
if (x1 < 10) {
count++;
}
}
}
return count;
}
public static void main(String[] args) {
System.out.println(countInRange(1, 2, 3, -1, -2));
}
}

View File

@@ -0,0 +1,23 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
public class Main {
private static long countInRange(int... input) {
int x = 1;
int i = 3;
long result = 0;
for (int count : input) {
if (count > 0) {
if (count < 10) {
result++;
}
}
}
return result;
}
public static void main(String[] args) {
System.out.println(countInRange(1, 2, 3, -1, -2));
}
}

View File

@@ -0,0 +1,24 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
import java.util.LongSummaryStatistics;
public class Main {
public static LongSummaryStatistics test(List<List<String>> list) {
LongSummaryStatistics stat = new LongSummaryStatistics();
for (List<String> a : list) {
if (a != null) {
for (String s : a) {
long l = s.length();
stat.accept(l);
}
}
}
return stat;
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList(null, Arrays.asList("aaa", "b", "cc", "dddd"), Arrays.asList("gggg"))));
}
}

View File

@@ -0,0 +1,23 @@
// "Replace Stream API chain with loop" "true"
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
private static List<Integer> getPositiveDoubled(int... input) {
List<Integer> list = new ArrayList<>();
for (int x : input) {
if (x > 0) {
Integer integer = x * 2;
list.add(integer);
}
}
return list;
}
public static void main(String[] args) {
System.out.println(getPositiveDoubled(1, 2, 3, -1, -2));
}
}

View File

@@ -0,0 +1,23 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import java.util.Optional;
import static java.util.Arrays.asList;
public class Main {
public static Optional<String> test(List<List<String>> list) {
for (List<String> x : list) {
if (x != null) {
for (String s : x) {
return Optional.of(s);
}
}
}
return Optional.empty();
}
public static void main(String[] args) {
System.out.println(test(asList(asList(), asList("a"), asList("b", "c"))));
}
}

View File

@@ -0,0 +1,30 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import java.util.Optional;
import static java.util.Arrays.asList;
public class Main {
public static Optional<String> test(List<List<String>> list) {
OUTER:
for(int i=0; i<10; i++) {
Optional<String> found = Optional.empty();
OUTER1:
for (List<String> x : list) {
if (x != null) {
for (String s : x) {
found = Optional.of(s);
break OUTER1;
}
}
}
return found.orElse("");
}
return null;
}
public static void main(String[] args) {
System.out.println(test(asList(asList(), asList("a"), asList("b", "c"))));
}
}

View File

@@ -0,0 +1,20 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.*;
public class Main {
private static OptionalInt test() {
for (int x = 0; x < 100; x++) {
if (x > 50) {
return OptionalInt.of(x);
}
}
return OptionalInt.empty();
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,22 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.*;
public class Main {
private static int test() {
OptionalInt found = OptionalInt.empty();
for (int x = 0; x < 100; x++) {
if (x > 50) {
found = OptionalInt.of(x);
break;
}
}
return found.orElse(0);
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,19 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import java.util.Optional;
import static java.util.Arrays.asList;
public class Main {
public static Optional<String> test(List<String> list) {
for (String s : list) {
return Optional.of(s);
}
return Optional.empty();
}
public static void main(String[] args) {
System.out.println(test(asList("a", "b", "c")));
}
}

View File

@@ -0,0 +1,26 @@
// "Replace Stream API chain with loop" "true"
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static java.util.Arrays.asList;
public class Main {
private static long test(List<List<String>> nested) {
long count = 0;
for (List<String> names : nested) {
Set<String> uniqueValues = new HashSet<>();
for (String name : names) {
if (uniqueValues.add(name)) {
count++;
}
}
}
return count;
}
public static void main(String[] args) {
System.out.println(test(asList(asList("a"), asList(null, "bb", "ccc"))));
}
}

View File

@@ -0,0 +1,25 @@
// "Replace Stream API chain with loop" "true"
import java.util.IntSummaryStatistics;
import java.util.stream.IntStream;
public class Main {
public static IntSummaryStatistics test() {
IntSummaryStatistics stat = new IntSummaryStatistics();
long limit = 50;
OUTER:
for (int x = 0; x < 100; x++) {
long limitInner = x / 2;
for (int i = 0; i < x; i++) {
if (limitInner-- == 0) break;
if (limit-- == 0) break OUTER;
stat.accept(i);
}
}
return stat;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,31 @@
// "Replace Stream API chain with loop" "true"
import java.util.IntSummaryStatistics;
import java.util.stream.IntStream;
public class Main {
public static IntSummaryStatistics test() {
IntSummaryStatistics stat = new IntSummaryStatistics();
long limit = 500;
OUTER:
for (int x = 0; x < 100; x++) {
long limitInner = x / 2;
INNER:
for (int y = 0; y < x; y++) {
long limit1 = 10;
int bound = y + 100;
for (int i = y; i < bound; i++) {
if (limit1-- == 0) break;
if (limitInner-- == 0) break INNER;
if (limit-- == 0) break OUTER;
stat.accept(i);
}
}
}
return stat;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,35 @@
// "Replace Stream API chain with loop" "true"
import java.util.IntSummaryStatistics;
import java.util.stream.IntStream;
public class Main {
public static IntSummaryStatistics test() {
IntSummaryStatistics stat = new IntSummaryStatistics();
long limit = 500;
OUTER:
for (int x = 0; x < 100; x++) {
long limitInner = x / 2;
INNER:
for (int y = 0; y < x; y++) {
long limit1 = 10;
int boundInner = y + 100;
INNER1:
for (int z = y; z < boundInner; z++) {
int bound = z + 2;
for (int i = z; i < bound; i++) {
if (limit1-- == 0) break INNER1;
if (limitInner-- == 0) break INNER;
if (limit-- == 0) break OUTER;
stat.accept(i);
}
}
}
}
return stat;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,30 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
public class Main {
private static long test(Map<String, List<String>> strings) {
long count = 0;
for (Map.Entry<String, List<String>> e : strings.entrySet()) {
if (!e.getKey().isEmpty()) {
String sInner = e.getKey();
for (String s : e.getValue()) {
if (sInner.equals(s)) {
count++;
}
}
}
}
return count;
}
public static void main(String[] args) {
Map<String, List<String>> map = new HashMap<>();
map.put("", Arrays.asList("", "a", "b"));
map.put("a", Arrays.asList("", "a", "b", "a"));
map.put("b", Arrays.asList("", "a", "b"));
map.put("c", Arrays.asList("", "a", "b"));
System.out.println(test(map));
}
}

View File

@@ -0,0 +1,15 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
import java.util.LongSummaryStatistics;
public class Main {
public static LongSummaryStatistics test(List<List<String>> list) {
return list.stream().filter(a -> a != null).flatMapToLong(lst -> lst.stream().distinct().mapToLong(a -> a.length())).summa<caret>ryStatistics();
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList(null, Arrays.asList("aaa", "b", "cc", "cc", "dddd"), Arrays.asList("cc", "b", "gggg"))));
}
}

View File

@@ -0,0 +1,29 @@
// "Replace Stream API chain with loop" "true"
import java.util.IntSummaryStatistics;
import java.util.List;
import static java.util.Arrays.asList;
public class Main {
public static IntSummaryStatistics test(List<List<List<String>>> list) {
IntSummaryStatistics stat = new IntSummaryStatistics();
for (List<List<String>> l : list) {
if (l != null) {
for (List<String> lst : l) {
if (lst != null) {
for (String str : lst) {
int i = str.length();
stat.accept(i);
}
}
}
}
}
return stat;
}
public static void main(String[] args) {
System.out.println(test(asList(asList(asList("a", "bbb", "ccc")), asList(), null, asList(asList("z")))));
}
}

View File

@@ -0,0 +1,24 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
import java.util.LongSummaryStatistics;
public class Main {
public static LongSummaryStatistics test(List<List<String>> list) {
LongSummaryStatistics stat = new LongSummaryStatistics();
for (List<String> a : list) {
if (a != null) {
for (String s : a) {
long l = s.length();
stat.accept(l);
}
}
}
return stat;
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList(null, Arrays.asList("aaa", "b", "cc", "dddd"), Arrays.asList("gggg"))));
}
}

View File

@@ -0,0 +1,33 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.stream.IntStream;
public class Main {
public static IntSummaryStatistics test(int... values) {
IntSummaryStatistics stat = new IntSummaryStatistics();
long toSkipOuter = 1;
for (int x : values) {
if (toSkipOuter > 0) {
toSkipOuter--;
continue;
}
if (x > 0) {
long toSkip = x;
for (int i = 0; i < 100; i++) {
if (toSkip > 0) {
toSkip--;
continue;
}
stat.accept(i);
}
}
}
return stat;
}
public static void main(String[] args) {
System.out.println(test(1, 95, -2, 0, 97, 90));
}
}

View File

@@ -0,0 +1,33 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.stream.IntStream;
public class Main {
public static IntSummaryStatistics test(int... values) {
IntSummaryStatistics stat = new IntSummaryStatistics();
long toSkip = 1;
for (int x : values) {
if (x > 0) {
long toSkipInner = x;
for (int i = 0; i < 100; i++) {
if (toSkipInner > 0) {
toSkipInner--;
continue;
}
if (toSkip > 0) {
toSkip--;
continue;
}
stat.accept(i);
}
}
}
return stat;
}
public static void main(String[] args) {
System.out.println(test(1, 95, -2, 0, 97, 90));
}
}

View File

@@ -0,0 +1,18 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
public class Main {
private static void test(List<String> list) {
for (String x : list) {
if (x != null) {
System.out.println(x);
}
}
}
public static void main(String[] args) {
test(Arrays.asList("a", "b", "xyz"));
}
}

View File

@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import java.util.function.Predicate;
public class Main {
static Predicate<String> nonEmpty = s -> s != null && !s.isEmpty();
private static long test(List<String> strings) {
long count = 0;
for (String string : strings) {
if (nonEmpty.test(string)) {
count++;
}
}
return count;
}
public static void main(String[] args) {
}
}

View File

@@ -0,0 +1,28 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class Main {
private static long test(List<Predicate<String>> predicates, List<String> strings) {
long count = 0;
for (Predicate<String> pred : predicates) {
if (pred != null) {
for (String string : strings) {
if (pred.test(string)) {
count++;
}
}
}
}
return count;
}
public static void main(String[] args) {
System.out.println(test(
Arrays.asList(String::isEmpty, s -> s.length() > 3),
Arrays.asList("", "a", "abcd", "xyz")
));
}
}

View File

@@ -0,0 +1,25 @@
// "Replace Stream API chain with loop" "true"
import java.util.IntSummaryStatistics;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class Main {
public static IntSummaryStatistics test() {
IntSummaryStatistics stat = new IntSummaryStatistics();
long limit = 33;
OUTER:
while (true) {
Integer x = 10;
for (int i = 0; i < x; i++) {
if (limit-- == 0) break OUTER;
stat.accept(i);
}
}
return stat;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,29 @@
// "Replace Stream API chain with loop" "true"
import java.util.IntSummaryStatistics;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class Main {
public static Integer getInt() {
return 10;
}
public static IntSummaryStatistics test() {
IntSummaryStatistics stat = new IntSummaryStatistics();
long limit = 33;
OUTER:
while (true) {
Integer x = Main.getInt();
for (int i = 0; i < x; i++) {
if (limit-- == 0) break OUTER;
stat.accept(i);
}
}
return stat;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,27 @@
// "Replace Stream API chain with loop" "true"
import java.util.DoubleSummaryStatistics;
import java.util.Random;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
public class Main {
public static DoubleSummaryStatistics test() {
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);
}
}
return stat;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,25 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
public class Main {
private static void test(List<String> list) {
boolean found = false;
for (String x : list) {
if (x != null) {
if (x.startsWith("x")) {
found = true;
break;
}
}
}
if(found) {
System.out.println("Ok!");
}
}
public static void main(String[] args) {
test(Arrays.asList("a", "b", "xyz"));
}
}

View File

@@ -0,0 +1,18 @@
// "Replace Stream API chain with loop" "true"
import java.util.IntSummaryStatistics;
import java.util.stream.IntStream;
public class Main {
private static IntSummaryStatistics test() {
IntSummaryStatistics stat = new IntSummaryStatistics();
for (int i : new int[]{1, 2, 3}) {
stat.accept(i);
}
return stat;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,18 @@
// "Replace Stream API chain with loop" "true"
import java.util.IntSummaryStatistics;
import java.util.stream.IntStream;
public class Main {
private static IntSummaryStatistics test() {
IntSummaryStatistics stat = new IntSummaryStatistics();
for (int i : new int[]{1, 2, 3}) {
stat.accept(i);
}
return stat;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.IntSummaryStatistics;
import java.util.stream.Stream;
public class Main {
public static IntSummaryStatistics test() {
IntSummaryStatistics stat = new IntSummaryStatistics();
long limit = 20;
for (String x = ""; ; x = x + "a") {
if (limit-- == 0) break;
int i = x.length();
stat.accept(i);
}
return stat;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,22 @@
// "Replace Stream API chain with loop" "true"
import java.util.ArrayList;
import java.util.stream.Stream;
import java.util.stream.Collectors;
import java.util.List;
public class Main {
public static List<String> test() {
List<String> list = new ArrayList<>();
long limit = 20;
for (String x = ""; ; x = x + "a") {
if (limit-- == 0) break;
list.add(x);
}
return list;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,24 @@
// "Replace Stream API chain with loop" "true"
import java.util.IntSummaryStatistics;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class Main {
public static IntSummaryStatistics test() {
IntSummaryStatistics stat = new IntSummaryStatistics();
for (int limit = 0; limit < 20; limit++) {
long limitInner = limit;
for (String x = ""; ; x = x + limit) {
if (limitInner-- == 0) break;
int i = x.length();
stat.accept(i);
}
}
return stat;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,26 @@
// "Replace Stream API chain with loop" "true"
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class Main {
private static List<String> test() {
List<String> list = new ArrayList<>();
for (int x = 0; x < 20; x++) {
Integer integer = x;
long limit = integer;
for (String str = ""; ; str = "a" + str) {
if (limit-- == 0) break;
list.add(str);
}
}
return list;
}
public static void main(String[] args) {
System.out.println(String.join("|", test()).length());
}
}

View File

@@ -0,0 +1,26 @@
// "Replace Stream API chain with loop" "true"
import java.util.IntSummaryStatistics;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class Main {
public static IntSummaryStatistics test() {
IntSummaryStatistics stat = new IntSummaryStatistics();
for (int x = 0; x < 20; x++) {
if (x > 2) {
long limit = x;
for (String s = String.valueOf(x); ; s = s + x) {
if (limit-- == 0) break;
int i = s.length();
stat.accept(i);
}
}
}
return stat;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,20 @@
// "Replace Stream API chain with loop" "true"
import java.util.IntSummaryStatistics;
import java.util.stream.IntStream;
public class Main {
public static IntSummaryStatistics test() {
IntSummaryStatistics stat = new IntSummaryStatistics();
long limit = 50;
for (int i = 0; i < 100; i++) {
if (limit-- == 0) break;
stat.accept(i);
}
return stat;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
public class Main {
private static long countNonEmpty(List<String> input) {
long count = 0;
for (String str : input) {
String s = str.trim();
if (!s.isEmpty()) {
count++;
}
}
return count;
}
public static void main(String[] args) {
System.out.println(countNonEmpty(Arrays.asList("a", "", "b", "", "")));
}
}

View File

@@ -0,0 +1,24 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
public class Main {
private static String getString() {
return "abc";
}
private static boolean test(List<String> strings) {
String s = getString();
for (String string : strings) {
if (s.equals(string)) {
return true;
}
}
return false;
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList("a", "b", "c")));
}
}

View File

@@ -0,0 +1,19 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
public class Main {
private static void test(List<String> names) {
for (String name : names) {
if (Objects.nonNull(name)) {
System.out.println(name);
}
}
}
public static void main(String[] args) {
test(Arrays.asList("a", "b", "xyz"));
}
}

View File

@@ -0,0 +1,34 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Main {
private static long test(Map<String, List<String>> strings) {
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>() {
@Override
public boolean test(String s) {
return e.getKey().equals(s);
}
}).count();
sum += l;
}
}
return sum;
}
public static void main(String[] args) {
boolean x = Stream.of(1, 2, 3).anyMatch(Objects::nonNull);
Map<String, List<String>> map = new HashMap<>();
map.put("", Arrays.asList("", "a", "b"));
map.put("a", Arrays.asList("", "a", "b", "a"));
map.put("b", Arrays.asList("", "a", "b"));
map.put("c", Arrays.asList("", "a", "b"));
System.out.println(test(map));
}
}

View File

@@ -0,0 +1,28 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Stream;
public class Main {
private static long test(Map<String, List<String>> strings) {
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;
}
}
return sum;
}
public static void main(String[] args) {
boolean x = Stream.of(1, 2, 3).anyMatch(Objects::nonNull);
Map<String, List<String>> map = new HashMap<>();
map.put("", Arrays.asList("", "a", "b"));
map.put("a", Arrays.asList("", "a", "b", "a"));
map.put("b", Arrays.asList("", "a", "b"));
map.put("c", Arrays.asList("", "a", "b"));
System.out.println(test(map));
}
}

View File

@@ -0,0 +1,28 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Stream;
public class Main {
private static long test(Map<String, List<String>> strings) {
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;
}
}
return sum;
}
public static void main(String[] args) {
boolean x = Stream.of(1, 2, 3).anyMatch(Objects::nonNull);
Map<String, List<String>> map = new HashMap<>();
map.put("", Arrays.asList("", "a", "b"));
map.put("a", Arrays.asList("", "a", "b", "a"));
map.put("b", Arrays.asList("", "a", "b"));
map.put("c", Arrays.asList("", "a", "b"));
System.out.println(test(map));
}
}

View File

@@ -0,0 +1,29 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import java.util.function.Consumer;
import static java.util.Arrays.asList;
public class Main {
public static long test(List<String> list) {
long count = 0;
for (String l : list) {
if (l != null) {
(new Consumer<String>() {
String lst = "hello";
public void accept(String lst) {
System.out.println(this.lst + lst);
}
}).accept(l);
count++;
}
}
return count;
}
public static void main(String[] args) {
System.out.println(test(asList("a", "b", "c")));
}
}

View File

@@ -0,0 +1,29 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import java.util.function.Consumer;
import static java.util.Arrays.asList;
public class Main {
public static long test(List<String> list) {
long count = 0;
for (String l : list) {
if (l != null) {
(new Consumer<String>() {
String list = "hello";
public void accept(String list) {
System.out.println(this.list + l);
}
}).accept(l);
count++;
}
}
return count;
}
public static void main(String[] args) {
System.out.println(test(asList("a", "b", "c")));
}
}

View File

@@ -0,0 +1,25 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import static java.util.Arrays.asList;
public class Main {
public static boolean test(List<List<String>> list) {
for (List<String> x : list) {
if (x != null) {
for (String str : x) {
if (str.startsWith("a")) {
return false;
}
}
}
}
return true;
}
public static void main(String[] args) {
System.out.println(test(asList(asList(), asList("a"), asList("b", "c"))));
System.out.println(test(asList(asList(), asList("d"), asList("b", "c"))));
}
}

View File

@@ -0,0 +1,25 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class Main {
private static String test(List<String> list) {
if (list == null) return null;
else {
Optional<String> found = Optional.empty();
for (String str : list) {
if (str.contains("x")) {
found = Optional.of(str);
break;
}
}
return found.orElse(null);
}
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList("a", "b", "syz")));
}
}

View File

@@ -0,0 +1,20 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
public class Main {
public static int test(List<String> list) {
int sum = 0;
for (String s : list) {
System.out.println(s);
int i = s.length();
sum += i;
}
return sum;
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList("aaa", "b", "cc", "dddd")));
}
}

View File

@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
import java.util.LongSummaryStatistics;
public class Main {
public static LongSummaryStatistics test(List<String> list) {
LongSummaryStatistics stat = new LongSummaryStatistics();
for (String s : list) {
System.out.println(s);
long l = s.length();
stat.accept(l);
}
return stat;
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList("aaa", "b", "cc", "dddd")));
}
}

View File

@@ -0,0 +1,20 @@
// "Replace Stream API chain with loop" "true"
import java.util.stream.IntStream;
public class Main {
private static long check(int start, int stop, double v) {
long count = 0;
for (int x = start; x < stop; x++) {
double x1 = 1.0 / x;
if (x1 < v) {
count++;
}
}
return count;
}
public static void main(String[] args) {
System.out.println(check(1, 100, 0.04));
}
}

View File

@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.stream.IntStream;
public class Main {
private static long check(int start, double val) {
long count = 0;
int bound = start * 200;
for (int x = start; x <= bound; x++) {
double v = 1.0 / x;
if (v < val) {
count++;
}
}
return count;
}
public static void main(String[] args) {
System.out.println(check(2, 0.04));
}
}

View File

@@ -0,0 +1,19 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import static java.util.Arrays.asList;
public class Main {
public static String test(List<String> list) {
String acc = "";
for (String s : list) {
acc = acc + s;
}
return acc;
}
public static void main(String[] args) {
System.out.println(test(asList("a", "b", "c")));
}
}

View File

@@ -0,0 +1,19 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import static java.util.Arrays.asList;
public class Main {
public static int test(List<String> list) {
Integer acc = 0;
for (String s : list) {
acc = acc + s.length();
}
return acc;
}
public static void main(String[] args) {
System.out.println(test(asList("a", "b", "c")));
}
}

View File

@@ -0,0 +1,18 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
public class Main {
private static Integer test(List<Integer> numbers) {
Integer acc = 0;
for (Integer number : numbers) {
acc = Math.max(acc, number);
}
return acc;
}
public static void main(String[] args) {
test(Arrays.asList("a", "b", "xyz"));
}
}

View File

@@ -0,0 +1,24 @@
// "Replace Stream API chain with loop" "true"
import java.util.OptionalInt;
import java.util.stream.IntStream;
public class Main {
private static OptionalInt test() {
boolean seen = false;
int acc = 0;
for (int i : new int[]{}) {
if (!seen) {
seen = true;
acc = i;
} else {
acc = acc * i;
}
}
return (seen ? OptionalInt.of(acc) : OptionalInt.empty());
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,24 @@
// "Replace Stream API chain with loop" "true"
import java.util.Optional;
import java.util.stream.Stream;
public class Main {
private static Optional<Integer> test(Integer... numbers) {
boolean seen = false;
Integer acc = null;
for (Integer number : numbers) {
if (!seen) {
seen = true;
acc = number;
} else {
acc = acc * number;
}
}
return (seen ? Optional.of(acc) : Optional.empty());
}
public static void main(String[] args) {
System.out.println(test(1,2,3,4));
}
}

View File

@@ -0,0 +1,25 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
public class Main {
private static long test(List<?> list) {
long count = 0;
Set<Object> uniqueValues = new HashSet<>();
long toSkip = list.size() / 2;
for (Object o : list) {
if (toSkip > 0) {
toSkip--;
continue;
}
if (uniqueValues.add(o)) {
count++;
}
}
return count;
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList(1,2,3,3,2,1,1,2,3)));
}
}

View File

@@ -0,0 +1,29 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class Main {
private static String test(List<CharSequence> list) {
StringBuilder sb = new StringBuilder();
Set<CharSequence> uniqueValues = new HashSet<>();
long toSkip = 1;
for (CharSequence charSequence : list) {
if (toSkip > 0) {
toSkip--;
continue;
}
if (uniqueValues.add(charSequence)) {
sb.append(charSequence);
}
}
return sb.toString();
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList("a", "b", "e", "c", "d", "e", "a")));
}
}

View File

@@ -0,0 +1,31 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
private static String test(List<CharSequence> list, String delimiter) {
StringJoiner joiner = new StringJoiner(delimiter);
long toSkip = 2;
Set<CharSequence> uniqueValues = new HashSet<>();
long toSkip1 = 1;
for (CharSequence charSequence : list) {
if (toSkip1 > 0) {
toSkip1--;
continue;
}
if (uniqueValues.add(charSequence)) {
if (toSkip > 0) {
toSkip--;
continue;
}
joiner.add(charSequence);
}
}
return joiner.toString();
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList("a", "b", "e", "c", "d", "e", "a"), ";"));
}
}

View File

@@ -0,0 +1,26 @@
// "Replace Stream API chain with loop" "true"
import java.util.*;
import java.util.stream.Collectors;
public class Main {
private static String test(List<CharSequence> list, String delimiter) {
StringJoiner joiner = new StringJoiner(delimiter, "<", ">");
Set<CharSequence> uniqueValues = new HashSet<>();
long toSkip = 1;
for (CharSequence charSequence : list) {
if (toSkip > 0) {
toSkip--;
continue;
}
if (uniqueValues.add(charSequence)) {
joiner.add(charSequence);
}
}
return joiner.toString();
}
public static void main(String[] args) {
System.out.println(test(Arrays.asList("a", "b", "e", "c", "d", "e", "a"), ";"));
}
}

View File

@@ -0,0 +1,19 @@
// "Replace Stream API chain with loop" "true"
import java.util.IntSummaryStatistics;
import java.util.stream.Stream;
public class Main {
private static IntSummaryStatistics test() {
IntSummaryStatistics stat = new IntSummaryStatistics();
for (Integer i : new Integer[]{1, 2, 3}) {
int i1 = i;
stat.accept(i1);
}
return stat;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,20 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.stream.Stream;
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);
}
return stat;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class Main {
private static IntSummaryStatistics test() {
IntSummaryStatistics stat = new IntSummaryStatistics();
for (Supplier<Integer> sup : Arrays.<Supplier<Integer>>asList(() -> 1, () -> 2, () -> 3)) {
int i = sup.get();
stat.accept(i);
}
return stat;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,21 @@
// "Replace Stream API chain with loop" "true"
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
private static Integer[][] test(int[] numbers) {
List<Integer[]> list = new ArrayList<>();
for (int number : numbers) {
Integer n = number;
Integer[] integers = new Integer[]{n};
list.add(integers);
}
return list.toArray(new Integer[0][]);
}
public static void main(String[] args) {
System.out.println(Arrays.asList(test(new int[] {1,2,3})));
}
}

View File

@@ -0,0 +1,20 @@
// "Replace Stream API chain with loop" "true"
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
private static Number[] test(int[] numbers) {
List<Integer> list = new ArrayList<>();
for (int number : numbers) {
Integer integer = number;
list.add(integer);
}
return list.toArray(new Integer[0]);
}
public static void main(String[] args) {
System.out.println(Arrays.asList(test(new int[] {1,2,3})));
}
}

View File

@@ -0,0 +1,22 @@
// "Replace Stream API chain with loop" "true"
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Main {
private static List<?>[] test(int[] numbers) {
List<List<?>> list = new ArrayList<>();
for (int number : numbers) {
Integer n = number;
List<Integer> integers = Collections.singletonList(n);
list.add(integers);
}
return list.toArray(new List<?>[0]);
}
public static void main(String[] args) {
System.out.println(Arrays.asList(test(new int[] {1,2,3})));
}
}

View File

@@ -0,0 +1,20 @@
// "Replace Stream API chain with loop" "true"
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
private static Object[] test(int[] numbers) {
List<Object> list = new ArrayList<>();
for (int number : numbers) {
Integer integer = number;
list.add(integer);
}
return list.toArray();
}
public static void main(String[] args) {
System.out.println(Arrays.asList(test(new int[] {1,2,3})));
}
}

View File

@@ -0,0 +1,20 @@
// "Replace Stream API chain with loop" "true"
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Main {
private static TreeSet<Integer> test() {
TreeSet<Integer> collection = new TreeSet<Integer>();
for (int i : new int[]{4, 2, 1}) {
Integer integer = i;
collection.add(integer);
}
return collection;
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,22 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.stream.IntStream;
public class Main {
private static int[] test() {
int[] arr = new int[10];
int count = 0;
for (int x = -100; x < 100; x++) {
if (x > 0) {
if (arr.length == count) arr = Arrays.copyOf(arr, count * 2);
arr[count++] = x;
}
}
return Arrays.copyOfRange(arr, 0, count);
}
public static void main(String[] args) {
System.out.println(Arrays.toString(test()));
}
}

View File

@@ -0,0 +1,16 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import static java.util.Arrays.asList;
public class Main {
public static boolean test(List<List<String>> list) {
return list.stream().filter(x -> x != null).flatMap(x -> x.stream()).allMat<caret>ch(x -> x.startsWith("a"));
}
public static void main(String[] args) {
System.out.println(test(asList(asList(), asList("a"), asList("b", "c"))));
System.out.println(test(asList(asList(), asList("d"), asList("b", "c"))));
}
}

View File

@@ -0,0 +1,18 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import static java.util.Arrays.asList;
public class Main {
public static void test(List<List<String>> list) {
if(list.stream().filter(x -> x != null).flatMap(x -> x.stream()).allMat<caret>ch(x -> x.startsWith("a"))) {
System.out.println("ok");
}
}
public static void main(String[] args) {
System.out.println(test(asList(asList(), asList("a"), asList("b", "c"))));
System.out.println(test(asList(asList(), asList("d"), asList("b", "c"))));
}
}

View File

@@ -0,0 +1,16 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import static java.util.Arrays.asList;
public class Main {
public static boolean test(List<List<String>> list) {
return list.stream().filter(x -> x != null).flatMap(x -> x.stream()).anyMat<caret>ch(x -> x.startsWith("a"));
}
public static void main(String[] args) {
System.out.println(test(asList(asList(), asList("a"), asList("b", "c"))));
System.out.println(test(asList(asList(), asList("d"), asList("b", "c"))));
}
}

View File

@@ -0,0 +1,13 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
public class Main {
private static long test() {
return Arrays.stream(new Integer[] {1,2,3,2,3}).skip(1).distinct().cou<caret>nt();
}
public static void main(String[] args) {
System.out.println(test());
}
}

View File

@@ -0,0 +1,14 @@
// "Replace Stream API chain with loop" "true"
import java.util.OptionalDouble;
import java.util.stream.LongStream;
public class Main {
private static OptionalDouble test(long... numbers) {
return LongStream.of(numbers).filter(x -> x > 0).asDoubleStream().avera<caret>ge();
}
public static void main(String[] args) {
System.out.println(test(-1,-2,-3, Long.MAX_VALUE, Long.MAX_VALUE));
}
}

View File

@@ -0,0 +1,14 @@
// "Replace Stream API chain with loop" "true"
import java.util.OptionalDouble;
import java.util.stream.IntStream;
public class Main {
private static OptionalDouble test(int... numbers) {
return IntStream.of(numbers).filter(x -> x > 0).aver<caret>age();
}
public static void main(String[] args) {
System.out.println(test(-1,-2,-3));
}
}

View File

@@ -0,0 +1,14 @@
// "Replace Stream API chain with loop" "true"
import java.util.OptionalDouble;
import java.util.stream.LongStream;
public class Main {
private static OptionalDouble test(long... numbers) {
return LongStream.of(numbers).filter(x -> x > 0).avera<caret>ge();
}
public static void main(String[] args) {
System.out.println(test(-1,-2,-3, Long.MAX_VALUE, Long.MAX_VALUE));
}
}

View File

@@ -0,0 +1,15 @@
// "Replace Stream API chain with loop" "true"
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class Main {
private static List<Integer> test(int[] numbers) {
return Arrays.stream(numbers).boxed().c<caret>ollect(Collectors.toList());
}
public static void main(String[] args) {
System.out.println(test(new int[] {1,2,3}));
}
}

View File

@@ -0,0 +1,17 @@
// "Replace Stream API chain with loop" "true"
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static java.util.Arrays.asList;
public class Main {
public static Optional<String> test(List<String> list) {
return list.stream().coll<caret>ect(Collectors.reducing((a, b) -> a+b));
}
public static void main(String[] args) {
System.out.println(test(asList("a", "b", "c")));
}
}

Some files were not shown because too many files have changed in this diff Show More