StreamToLoopInspection refactoring: strings replaced with actual PsiType's (as they are valid now); removed unnecessary code due to all PsiExpressions are also valid

This commit is contained in:
Tagir Valeev
2017-03-21 14:21:29 +07:00
parent 69632b09da
commit f764692965
12 changed files with 183 additions and 214 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -16,7 +16,8 @@
package com.intellij.codeInspection.streamToLoop;
import com.intellij.codeInspection.util.OptionalUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.psi.PsiPrimitiveType;
import com.intellij.psi.PsiType;
/**
* An interface representing the conditional expression to be generated in the resulting code
@@ -37,12 +38,12 @@ interface ConditionalExpression {
}
class Plain implements ConditionalExpression {
private final String myType;
private final PsiType myType;
private final String myCondition;
private final String myTrueBranch;
private final String myFalseBranch;
public Plain(String type, String condition, String trueBranch, String falseBranch) {
public Plain(PsiType type, String condition, String trueBranch, String falseBranch) {
myType = type;
myCondition = condition;
myTrueBranch = trueBranch;
@@ -50,7 +51,7 @@ interface ConditionalExpression {
}
public String getType() {
return myType;
return myType.getCanonicalText();
}
public String getCondition() {
@@ -103,7 +104,7 @@ interface ConditionalExpression {
return myInvert;
}
public Plain toPlain(String type, String trueBranch, String falseBranch) {
public Plain toPlain(PsiType type, String trueBranch, String falseBranch) {
return myInvert ? new Plain(type, myCondition, falseBranch, trueBranch) :
new Plain(type, myCondition, trueBranch, falseBranch);
}
@@ -115,21 +116,21 @@ interface ConditionalExpression {
}
class Optional implements ConditionalExpression {
private final String myType;
private final PsiType myType;
private final String myCondition;
private final String myPresentExpression;
private final String myTypeArgument;
Optional(String type, String condition, String presentExpression) {
Optional(PsiType type, String condition, String presentExpression) {
myType = type;
myCondition = condition;
myPresentExpression = presentExpression;
myTypeArgument = TypeConversionUtil.isPrimitive(type) ? "" : "<" + type + ">";
myTypeArgument = type instanceof PsiPrimitiveType ? "" : "<" + type.getCanonicalText() + ">";
}
@Override
public String getType() {
return OptionalUtil.getOptionalClass(myType) + myTypeArgument;
return OptionalUtil.getOptionalClass(myType.getCanonicalText()) + myTypeArgument;
}
@Override
@@ -139,12 +140,12 @@ interface ConditionalExpression {
@Override
public String getTrueBranch() {
return OptionalUtil.getOptionalClass(myType) + "." + myTypeArgument + "of(" + myPresentExpression + ")";
return OptionalUtil.getOptionalClass(myType.getCanonicalText()) + "." + myTypeArgument + "of(" + myPresentExpression + ")";
}
@Override
public String getFalseBranch() {
return OptionalUtil.getOptionalClass(myType) + "." + myTypeArgument + "empty()";
return OptionalUtil.getOptionalClass(myType.getCanonicalText()) + "." + myTypeArgument + "empty()";
}
public Plain unwrap(String absentExpression) {
@@ -27,6 +27,7 @@ import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.util.LambdaRefactoringUtil;
import com.intellij.util.ArrayUtil;
import com.siyeh.ig.psiutils.EquivalenceChecker;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.MethodCallUtils;
import one.util.streamex.EntryStream;
@@ -47,13 +48,13 @@ import java.util.function.Consumer;
abstract class FunctionHelper {
private static final Logger LOG = Logger.getInstance(FunctionHelper.class);
private String myResultType;
private PsiType myResultType;
FunctionHelper(PsiType resultType) {
myResultType = resultType.getCanonicalText();
myResultType = resultType;
}
String getResultType() {
PsiType getResultType() {
return myResultType;
}
@@ -72,9 +73,8 @@ abstract class FunctionHelper {
* how to name the SAM argument and returns the assigned name. After this method invocation normal transform cannot be performed.
*
* @return SAM argument name or null if function helper refused to perform a transformation.
* @param type type of the input variable (after generic substitution if applicable)
*/
String tryLightTransform(PsiType type) {
String tryLightTransform() {
return null;
}
@@ -118,8 +118,7 @@ abstract class FunctionHelper {
List<String> suggestFinalOutputNames(StreamToLoopReplacementContext context, String desiredName, String worstCaseName) {
List<String> candidates = Arrays.asList(JavaCodeStyleManager.getInstance(context.getProject())
.suggestVariableName(VariableKind.LOCAL_VARIABLE, desiredName,
context.createExpression(getText()),
context.createType(getResultType())).names);
getExpression(), getResultType()).names);
if(candidates.isEmpty() && worstCaseName != null) candidates = Collections.singletonList(worstCaseName);
return candidates;
}
@@ -127,9 +126,15 @@ abstract class FunctionHelper {
private static void suggestFromExpression(StreamVariable var, Project project, PsiExpression expression) {
SuggestedNameInfo info = JavaCodeStyleManager.getInstance(project)
.suggestVariableName(VariableKind.LOCAL_VARIABLE, null, expression, null, true);
for (String name : info.names) {
var.addOtherNameCandidate(name);
List<String> names = new ArrayList<>(Arrays.asList(info.names));
if (expression.getType() != null &&
!EquivalenceChecker.getCanonicalPsiEquivalence().typesAreEquivalent(var.getType(), expression.getType())) {
// If variable type and expression type is different, do not suggest candidates based on expression type
SuggestedNameInfo byType = JavaCodeStyleManager.getInstance(project)
.suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, expression.getType(), true);
names.removeAll(Arrays.asList(byType.names));
}
names.forEach(var::addOtherNameCandidate);
}
@Contract("null, _ -> null")
@@ -151,7 +156,6 @@ abstract class FunctionHelper {
PsiType returnType = interfaceMethod.getReturnType();
if (returnType == null) return null;
returnType = ((PsiClassType)type).resolveGenerics().getSubstitutor().substitute(returnType);
type = fixType(type, expression.getProject());
if (expression instanceof PsiLambdaExpression) {
PsiLambdaExpression lambda = (PsiLambdaExpression)expression;
PsiParameterList list = lambda.getParameterList();
@@ -203,32 +207,6 @@ abstract class FunctionHelper {
return new ComplexExpressionFunctionHelper(returnType, type, interfaceMethod.getName(), expression);
}
private static PsiType fixType(PsiType type, Project project) {
if(type instanceof PsiClassType) {
PsiClassType classType = (PsiClassType)type;
PsiClass aClass = classType.resolve();
if (aClass != null && classType.getParameterCount() != 0) {
PsiType[] parameters = classType.getParameters();
Arrays.asList(parameters).replaceAll(t -> fixType(t, project));
return JavaPsiFacade.getElementFactory(project).createType(aClass, parameters);
}
}
else if(type instanceof PsiArrayType) {
PsiType componentType = ((PsiArrayType)type).getComponentType();
PsiType fixedType = fixType(componentType, project);
if(fixedType != componentType) {
return fixedType.createArrayType();
}
}
else if(type instanceof PsiCapturedWildcardType) {
PsiCapturedWildcardType capturedWildcardType = (PsiCapturedWildcardType)type;
if(capturedWildcardType.getLowerBound().equals(PsiType.NULL)) {
return capturedWildcardType.getUpperBound();
}
}
return type;
}
@Nullable
private static String tryInlineMethodReference(int paramCount, PsiMethodReferenceExpression methodRef) {
PsiElement element = methodRef.resolve();
@@ -346,44 +324,33 @@ abstract class FunctionHelper {
}
private static class MethodReferenceFunctionHelper extends FunctionHelper {
private final String myType;
private final String myQualifierType;
private final PsiType myType;
private final PsiType myQualifierType;
private PsiMethodReferenceExpression myMethodRef;
private PsiExpression myExpression;
public MethodReferenceFunctionHelper(PsiType returnType, PsiType functionalInterfaceType, PsiMethodReferenceExpression methodRef) {
super(returnType);
myMethodRef = methodRef;
myType = functionalInterfaceType.getCanonicalText();
myType = functionalInterfaceType;
PsiExpression qualifier = methodRef.getQualifierExpression();
PsiType type = qualifier == null ? null : qualifier.getType();
myQualifierType = type == null ? null : type.getCanonicalText();
myQualifierType = qualifier == null ? null : qualifier.getType();
}
@Override
String tryLightTransform(PsiType type) {
if(myMethodRef.isConstructor()) return null;
type = GenericsUtil.getVariableTypeByExpressionType(type);
if(type == null) return null;
PsiElement element = myMethodRef.resolve();
if(!(element instanceof PsiMethod)) return null;
PsiMethod method = (PsiMethod)element;
String var = "x";
PsiLambdaExpression lambda;
PsiClass aClass = method.getContainingClass();
if(aClass == null) return null;
if(method.getModifierList().hasExplicitModifier(PsiModifier.STATIC)) {
if(method.getParameterList().getParametersCount() != 1) return null;
lambda = (PsiLambdaExpression)JavaPsiFacade.getElementFactory(myMethodRef.getProject())
.createExpressionFromText("(" + type.getCanonicalText() + " " + var + ")->" +
aClass.getQualifiedName() + "." + method.getName() + "(" + var + ")", myMethodRef);
} else {
lambda =
(PsiLambdaExpression)JavaPsiFacade.getElementFactory(myMethodRef.getProject()).createExpressionFromText(
"(" + type.getCanonicalText() + " " + var + ")->" + var + "." + myMethodRef.getReferenceName() + "()", myMethodRef);
String tryLightTransform() {
PsiLambdaExpression lambdaExpression = LambdaRefactoringUtil.createLambda(myMethodRef, true);
if(lambdaExpression == null) return null;
String typedParamList = LambdaRefactoringUtil.createLambdaParameterListWithFormalTypes(myType, lambdaExpression, false);
if(typedParamList != null && lambdaExpression.getBody() != null) {
lambdaExpression = (PsiLambdaExpression)JavaPsiFacade.getElementFactory(myMethodRef.getProject())
.createExpressionFromText(typedParamList + "->" + lambdaExpression.getBody().getText(), myMethodRef);
}
myExpression = (PsiExpression)lambda.getBody();
return var;
myExpression = LambdaUtil.extractSingleExpressionFromBody(lambdaExpression.getBody());
if(myExpression == null) return null;
PsiParameterList list = lambdaExpression.getParameterList();
if(list.getParametersCount() != 1) return null;
return list.getParameters()[0].getName();
}
@Override
@@ -399,24 +366,22 @@ abstract class FunctionHelper {
@Override
void transform(StreamToLoopReplacementContext context, String... argumentValues) {
PsiMethodReferenceExpression methodRef = fromText(context, myMethodRef.getText());
PsiMethodReferenceExpression methodRef = myMethodRef;
PsiExpression qualifier = methodRef.getQualifierExpression();
if(qualifier != null) {
String qualifierText = qualifier.getText();
if(!ExpressionUtils.isSimpleExpression(context.createExpression(qualifierText))) {
String type = myQualifierType;
if (type != null) {
if(!ExpressionUtils.isSimpleExpression(qualifier)) {
if (myQualifierType != null) {
String nameCandidate = "expr";
PsiType psiType = context.createType(myQualifierType);
SuggestedNameInfo info =
JavaCodeStyleManager
.getInstance(context.getProject()).suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, psiType, true);
SuggestedNameInfo info = JavaCodeStyleManager.getInstance(context.getProject())
.suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, myQualifierType, true);
if (info.names.length > 0) {
nameCandidate = info.names[0];
}
String expr = context.declare(nameCandidate, type, qualifierText);
PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)context
.createExpression("(" + type + " " + expr + ")->(" + myType + ")" + expr + "::" + myMethodRef.getReferenceName());
String expr = context.declare(nameCandidate, myQualifierType.getCanonicalText(), qualifierText);
PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)
context.createExpression("(" + myQualifierType.getCanonicalText() + " " + expr + ")->(" +
myType.getCanonicalText() + ")" + expr + "::" + myMethodRef.getReferenceName());
PsiTypeCastExpression castExpr = (PsiTypeCastExpression)lambdaExpression.getBody();
LOG.assertTrue(castExpr != null);
methodRef = (PsiMethodReferenceExpression)castExpr.getOperand();
@@ -424,33 +389,30 @@ abstract class FunctionHelper {
}
}
}
PsiLambdaExpression lambda = LambdaRefactoringUtil.convertMethodReferenceToLambda(methodRef, true, true);
PsiLambdaExpression lambda = LambdaRefactoringUtil.createLambda(methodRef, true);
if(lambda == null) {
throw new IllegalStateException("Unable to convert method reference to lambda: "+methodRef.getText());
}
PsiElement body = lambda.getBody();
LOG.assertTrue(body instanceof PsiExpression);
myExpression = (PsiExpression)body;
myExpression = LambdaUtil.extractSingleExpressionFromBody(lambda.getBody());
LOG.assertTrue(myExpression != null);
EntryStream.zip(lambda.getParameterList().getParameters(), argumentValues)
.forKeyValue((param, newName) -> myExpression = replaceVarReference(myExpression, param.getName(), newName, context));
}
@Override
void suggestOutputNames(StreamToLoopReplacementContext context, StreamVariable var) {
PsiTypeCastExpression castExpr = (PsiTypeCastExpression)context.createExpression("(" + myType + ")" + myMethodRef.getText());
PsiMethodReferenceExpression methodRef = (PsiMethodReferenceExpression)castExpr.getOperand();
PsiLambdaExpression lambda = LambdaRefactoringUtil.convertMethodReferenceToLambda(methodRef, true, true);
PsiLambdaExpression lambda = LambdaRefactoringUtil.createLambda(myMethodRef, true);
if(lambda != null) {
PsiElement body = lambda.getBody();
if(body instanceof PsiExpression) {
suggestFromExpression(var, context.getProject(), (PsiExpression)body);
PsiExpression body = LambdaUtil.extractSingleExpressionFromBody(lambda.getBody());
if(body != null) {
suggestFromExpression(var, context.getProject(), body);
}
}
}
@NotNull
private PsiMethodReferenceExpression fromText(StreamToLoopReplacementContext context, String text) {
PsiTypeCastExpression castExpr = (PsiTypeCastExpression)context.createExpression("(" + myType + ")" + text);
PsiTypeCastExpression castExpr = (PsiTypeCastExpression)context.createExpression("(" + myType.getCanonicalText() + ")" + text);
PsiMethodReferenceExpression methodRef = (PsiMethodReferenceExpression)castExpr.getOperand();
LOG.assertTrue(methodRef != null);
return methodRef;
@@ -590,7 +552,7 @@ abstract class FunctionHelper {
}
@Override
String tryLightTransform(PsiType type) {
String tryLightTransform() {
LOG.assertTrue(myParameters.length == 1);
return myParameters[0];
}
@@ -651,8 +613,9 @@ abstract class FunctionHelper {
@Override
void suggestOutputNames(StreamToLoopReplacementContext context, StreamVariable var) {
PsiExpression expr = context.createExpression("(" + var.getType() + ")" + getText());
suggestFromExpression(var, context.getProject(), expr);
if(myBody instanceof PsiExpression) {
suggestFromExpression(var, context.getProject(), (PsiExpression)myBody);
}
}
}
@@ -670,9 +633,6 @@ abstract class FunctionHelper {
void transform(StreamToLoopReplacementContext context, String... argumentValues) {
super.transform(context, argumentValues);
if(!myBody.isValid()) {
myBody = ((PsiLambdaExpression)context.createExpression("()->"+myBody.getText())).getBody();
}
List<PsiReturnStatement> returns = getReturns(myBody);
String continueStatement = "continue;";
returns.forEach(ret -> ret.replace(context.createStatement(continueStatement)));
@@ -94,7 +94,7 @@ abstract class Operation {
}
if ((name.equals("flatMap") || name.equals("flatMapToInt") || name.equals("flatMapToLong") || name.equals("flatMapToDouble")) &&
args.length == 1) {
return FlatMapOperation.from(outVar, args[0], inType, supportUnknownSources);
return FlatMapOperation.from(outVar, args[0], supportUnknownSources);
}
if ((name.equals("map") ||
name.equals("mapToInt") ||
@@ -289,7 +289,7 @@ abstract class Operation {
if (myCondition != null) {
String conditionText = myCondition.getText();
if (myInverted) {
conditionText = BoolUtils.getNegatedExpressionText(context.createExpression(conditionText));
conditionText = BoolUtils.getNegatedExpressionText(myCondition);
}
return "if(" + conditionText + "){\n" + replacement + "}\n";
}
@@ -297,10 +297,10 @@ abstract class Operation {
}
@Nullable
public static FlatMapOperation from(StreamVariable outVar, PsiExpression arg, PsiType inType, boolean supportUnknownSources) {
public static FlatMapOperation from(StreamVariable outVar, PsiExpression arg, boolean supportUnknownSources) {
FunctionHelper fn = FunctionHelper.create(arg, 1);
if(fn == null) return null;
String varName = fn.tryLightTransform(inType);
String varName = fn.tryLightTransform();
if(varName == null) return null;
PsiExpression body = fn.getExpression();
PsiExpression condition = null;
@@ -332,7 +332,8 @@ abstract class 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<>()");
context.declare("uniqueValues", "java.util.Set<" + PsiTypesUtil.boxIfPossible(inVar.getType().getCanonicalText()) + ">",
"new java.util.HashSet<>()");
return "if(" + set + ".add(" + inVar + ")) {\n" + code + "}\n";
}
}
@@ -407,7 +408,7 @@ abstract class Operation {
String list = context.registerVarName(Arrays.asList("toSort", "listToSort"));
context.addAfterStep(new SourceOperation.ForEachSource(context.createExpression(list)).wrap(null, outVar, code, context));
context.addAfterStep(list + ".sort(" + (myComparator == null ? "null" : myComparator.getText()) + ");\n");
String listType = CommonClassNames.JAVA_UTIL_LIST + "<" + inVar.getType() + ">";
String listType = CommonClassNames.JAVA_UTIL_LIST + "<" + inVar.getType().getCanonicalText() + ">";
String initializer = "new " + CommonClassNames.JAVA_UTIL_ARRAY_LIST + "<>()";
context.addBeforeStep(listType + " " + list + "=" + initializer + ";");
return list+".add("+inVar+");\n";
@@ -20,7 +20,6 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.StreamApiUtil;
import one.util.streamex.StreamEx;
@@ -175,19 +174,19 @@ abstract class SourceOperation extends Operation {
@Override
public String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
String type = outVar.getType();
PsiType type = outVar.getType();
String iterationParameter;
PsiExpressionList argList = myCall.getArgumentList();
if (TypeConversionUtil.isPrimitive(type)) {
if (type instanceof PsiPrimitiveType) {
// Not using argList.getExpressions() here as we want to preserve comments and formatting between the expressions
PsiElement[] children = argList.getChildren();
// first and last children are (parentheses), we need to remove them
iterationParameter = StreamEx.of(children, 1, children.length - 1)
.map(PsiElement::getText)
.joining("", "new " + type + "[] {", "}");
.joining("", "new " + type.getCanonicalText() + "[] {", "}");
}
else {
iterationParameter = "java.util.Arrays.<" + type + ">asList" + argList.getText();
iterationParameter = "java.util.Arrays.<" + type.getCanonicalText() + ">asList" + argList.getText();
}
return context.getLoopLabel() +
"for(" + outVar.getDeclaration() + ": " + iterationParameter + ") {" + code + "}\n";
@@ -304,7 +303,7 @@ abstract class SourceOperation extends Operation {
String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
String bound = myBound.getText();
if(!ExpressionUtils.isSimpleExpression(context.createExpression(bound))) {
bound = context.declare("bound", outVar.getType(), bound);
bound = context.declare("bound", outVar.getType().getCanonicalText(), bound);
}
String loopVar = outVar.getName();
String reassign = "";
@@ -313,7 +312,7 @@ abstract class SourceOperation extends Operation {
reassign = outVar.getDeclaration(loopVar);
}
return context.getLoopLabel() +
"for(" + outVar.getType() + " " + loopVar + " = " + myOrigin.getText() + ";" +
"for(" + outVar.getType().getCanonicalText() + " " + loopVar + " = " + myOrigin.getText() + ";" +
loopVar + (myInclusive ? "<=" : "<") + bound + ";" +
loopVar + "++) {\n" +
reassign +
@@ -221,7 +221,7 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
OperationRecord sourceRecord = new OperationRecord();
terminalRecord.myOperation = terminal;
sourceRecord.myOperation = source;
sourceRecord.myOutVar = terminalRecord.myInVar = new StreamVariable(elementType.getCanonicalText());
sourceRecord.myOutVar = terminalRecord.myInVar = new StreamVariable(elementType);
sourceRecord.myInVar = terminalRecord.myOutVar = StreamVariable.STUB;
return Arrays.asList(sourceRecord, terminalRecord);
}
@@ -260,7 +260,7 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
if(op.changesVariable()) {
PsiType type = StreamApiUtil.getStreamElementType(currentCall.getType());
if(type == null) return null;
lastVar = new StreamVariable(type.getCanonicalText());
lastVar = new StreamVariable(type);
}
or.myInVar = lastVar;
next = op;
@@ -397,7 +397,6 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
static class StreamToLoopReplacementContext {
private final boolean myHasNestedLoops;
private final String mySuffix;
private final PsiStatement myStatement;
private final Set<String> myUsedNames;
private final Set<String> myUsedLabels;
private final List<String> myBeforeSteps = new ArrayList<>();
@@ -412,8 +411,7 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
List<OperationRecord> records,
@NotNull PsiExpression streamExpression,
CommentTracker ct) {
myStatement = statement;
myFactory = JavaPsiFacade.getElementFactory(myStatement.getProject());
myFactory = JavaPsiFacade.getElementFactory(streamExpression.getProject());
myHasNestedLoops = records.stream().anyMatch(or -> or.myOperation instanceof FlatMapOperation);
myStreamExpression = streamExpression;
mySuffix = myHasNestedLoops ? "Outer" : "";
@@ -427,7 +425,6 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
myUsedNames = parentContext.myUsedNames;
myUsedLabels = parentContext.myUsedLabels;
myStreamExpression = parentContext.myStreamExpression;
myStatement = parentContext.myStatement;
myFactory = parentContext.myFactory;
myCommentTracker = parentContext.myCommentTracker;
myHasNestedLoops = records.stream().anyMatch(or -> or.myOperation instanceof FlatMapOperation);
@@ -484,8 +481,9 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
private boolean isUsed(String varName) {
return myUsedNames.contains(varName) || JavaLexer.isKeyword(varName, LanguageLevel.HIGHEST) ||
!varName.equals(JavaCodeStyleManager.getInstance(myStatement.getProject())
.suggestUniqueVariableName(varName, myStatement, v -> PsiTreeUtil.isAncestor(myStreamExpression, v, true)));
!varName.equals(JavaCodeStyleManager.getInstance(getProject())
.suggestUniqueVariableName(varName, myStreamExpression,
v -> PsiTreeUtil.isAncestor(myStreamExpression, v, true)));
}
public String declare(String desiredName, String type, String initializer) {
@@ -515,11 +513,11 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
return beforeSteps;
}
public String declareResult(String desiredName, String type, String initializer, @NotNull ResultKind kind) {
public String declareResult(String desiredName, PsiType type, String initializer, @NotNull ResultKind kind) {
if (kind != ResultKind.UNKNOWN && myStreamExpression.getParent() instanceof PsiVariable) {
PsiVariable var = (PsiVariable)myStreamExpression.getParent();
if(var.getType().equalsToText(type) && var.getParent() instanceof PsiDeclarationStatement
&& (kind == ResultKind.FINAL || canUseAsNonFinal(var))) {
if (EquivalenceChecker.getCanonicalPsiEquivalence().typesAreEquivalent(var.getType(), type) &&
var.getParent() instanceof PsiDeclarationStatement && (kind == ResultKind.FINAL || canUseAsNonFinal(var))) {
PsiDeclarationStatement declaration = (PsiDeclarationStatement)var.getParent();
if(declaration.getDeclaredElements().length == 1) {
myStreamExpression = declaration;
@@ -539,7 +537,7 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
}
}
String name = registerVarName(Arrays.asList(desiredName, "result"));
myBeforeSteps.add(type + " " + name + " = " + initializer + ";");
myBeforeSteps.add(type.getCanonicalText() + " " + name + " = " + initializer + ";");
if(myFinisher != null) {
throw new IllegalStateException("Finisher is already defined");
}
@@ -634,8 +632,8 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
}
}
String found =
declareResult(conditionalExpression.getCondition(), conditionalExpression.getType(), conditionalExpression.getFalseBranch(),
ResultKind.NON_FINAL);
declareResult(conditionalExpression.getCondition(), createType(conditionalExpression.getType()),
conditionalExpression.getFalseBranch(), ResultKind.NON_FINAL);
return found + " = " + conditionalExpression.getTrueBranch() + ";\n" + getBreakStatement();
}
@@ -656,10 +654,10 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
IElementType type = expression.getOperationTokenType();
if (type.equals(JavaTokenType.ANDAND)) {
candidate = condition
.toPlain("boolean", StreamEx.of(operands, 1, operands.length).map(PsiExpression::getText).joining(" && "), "false");
.toPlain(PsiType.BOOLEAN, StreamEx.of(operands, 1, operands.length).map(PsiExpression::getText).joining(" && "), "false");
} else if (type.equals(JavaTokenType.OROR)) {
candidate = condition
.toPlain("boolean", "true", StreamEx.of(operands, 1, operands.length).map(PsiExpression::getText).joining(" || "));
.toPlain(PsiType.BOOLEAN, "true", StreamEx.of(operands, 1, operands.length).map(PsiExpression::getText).joining(" || "));
}
}
} else if (parent instanceof PsiConditionalExpression) {
@@ -669,7 +667,7 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
PsiExpression thenExpression = ternary.getThenExpression();
PsiExpression elseExpression = ternary.getElseExpression();
if (type != null && thenExpression != null && elseExpression != null) {
candidate = condition.toPlain(type.getCanonicalText(), thenExpression.getText(), elseExpression.getText());
candidate = condition.toPlain(type, thenExpression.getText(), elseExpression.getText());
}
}
}
@@ -716,19 +714,19 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
}
public Project getProject() {
return myStatement.getProject();
return myStreamExpression.getProject();
}
public PsiExpression createExpression(String text) {
return myFactory.createExpressionFromText(text, myStatement);
return myFactory.createExpressionFromText(text, myStreamExpression);
}
public PsiStatement createStatement(String text) {
return myFactory.createStatementFromText(text, myStatement);
return myFactory.createStatementFromText(text, myStreamExpression);
}
public PsiType createType(String text) {
return myFactory.createTypeFromText(text, myStatement);
return myFactory.createTypeFromText(text, myStreamExpression);
}
}
@@ -17,6 +17,7 @@ package com.intellij.codeInspection.streamToLoop;
import com.intellij.codeInspection.streamToLoop.StreamToLoopInspection.StreamToLoopReplacementContext;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.PsiType;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import one.util.streamex.StreamEx;
@@ -38,7 +39,7 @@ import java.util.List;
class StreamVariable {
private static final Logger LOG = Logger.getInstance(StreamVariable.class);
static StreamVariable STUB = new StreamVariable("") {
static StreamVariable STUB = new StreamVariable(PsiType.VOID) {
@Override
public void addBestNameCandidate(String candidate) {
}
@@ -54,17 +55,17 @@ class StreamVariable {
};
String myName;
@NotNull String myType;
@NotNull PsiType myType;
boolean myFinal;
private Collection<String> myBestCandidates = new LinkedHashSet<>();
private Collection<String> myOtherCandidates = new LinkedHashSet<>();
StreamVariable(@NotNull String type) {
StreamVariable(@NotNull PsiType type) {
myType = type;
}
StreamVariable(@NotNull String type, @NotNull String name) {
StreamVariable(@NotNull PsiType type, @NotNull String name) {
myType = type;
myName = name;
}
@@ -103,7 +104,7 @@ class StreamVariable {
void register(StreamToLoopReplacementContext context) {
LOG.assertTrue(myName == null);
String[] fromType = JavaCodeStyleManager.getInstance(context.getProject())
.suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, context.createType(myType), true).names;
.suggestVariableName(VariableKind.LOCAL_VARIABLE, null, null, myType, true).names;
List<String> variants = StreamEx.of(myBestCandidates).append(myOtherCandidates).append(fromType).distinct().toList();
if (variants.isEmpty()) variants.add("val");
myName = context.registerVarName(variants);
@@ -116,16 +117,16 @@ class StreamVariable {
}
@NotNull
String getType() {
PsiType getType() {
return myType;
}
String getDeclaration() {
return getType() + " " + getName();
return getType().getCanonicalText() + " " + getName();
}
String getDeclaration(String initializer) {
return getType() + " " + getName() + "=" + initializer + ";\n";
return getType().getCanonicalText() + " " + getName() + "=" + initializer + ";\n";
}
public boolean isFinal() {
@@ -92,7 +92,7 @@ abstract class TerminalOperation extends Operation {
}
if((name.equals("findFirst") || name.equals("findAny")) && args.length == 0) {
PsiType optionalElementType = OptionalUtil.getOptionalElementType(resultType);
return optionalElementType == null ? null : new FindTerminalOperation(optionalElementType.getCanonicalText());
return optionalElementType == null ? null : new FindTerminalOperation(optionalElementType);
}
if(name.equals("toList") && args.length == 0) {
return ToCollectionTerminalOperation.toList(resultType);
@@ -108,7 +108,7 @@ abstract class TerminalOperation extends Operation {
if(args.length == 2 || args.length == 3) {
FunctionHelper fn = FunctionHelper.create(args[1], 2);
if(fn != null) {
return new ReduceTerminalOperation(args[0], fn, resultType.getCanonicalText());
return new ReduceTerminalOperation(args[0], fn, resultType);
}
}
if(args.length == 1) {
@@ -119,7 +119,7 @@ abstract class TerminalOperation extends Operation {
if(!(resultType instanceof PsiArrayType)) return null;
PsiType componentType = ((PsiArrayType)resultType).getComponentType();
if (componentType instanceof PsiPrimitiveType) {
if(args.length == 0) return new ToPrimitiveArrayTerminalOperation(componentType.getCanonicalText());
if (args.length == 0) return new ToPrimitiveArrayTerminalOperation(componentType);
}
else {
FunctionHelper fn = null;
@@ -131,7 +131,7 @@ abstract class TerminalOperation extends Operation {
}
}
if ((name.equals("max") || name.equals("min")) && args.length < 2) {
return MinMaxTerminalOperation.create(args.length == 1 ? args[0] : null, elementType.getCanonicalText(), name.equals("max"));
return MinMaxTerminalOperation.create(args.length == 1 ? args[0] : null, elementType, name.equals("max"));
}
if (name.equals("collect")) {
if (args.length == 3) {
@@ -142,7 +142,7 @@ abstract class TerminalOperation extends Operation {
return new ExplicitCollectTerminalOperation(supplier, accumulator);
}
if (args.length == 1) {
return fromCollector(elementType.getCanonicalText(), resultType, args[0]);
return fromCollector(elementType, resultType, args[0]);
}
}
return null;
@@ -150,7 +150,7 @@ abstract class TerminalOperation extends Operation {
@Contract("_, _, null -> null")
@Nullable
private static TerminalOperation fromCollector(@NotNull String elementType, @NotNull PsiType resultType, PsiExpression expr) {
private static TerminalOperation fromCollector(@NotNull PsiType elementType, @NotNull PsiType resultType, PsiExpression expr) {
if (!(expr instanceof PsiMethodCallExpression)) return null;
PsiMethodCallExpression collectorCall = (PsiMethodCallExpression)expr;
PsiExpression[] collectorArgs = collectorCall.getArgumentList().getExpressions();
@@ -164,7 +164,7 @@ abstract class TerminalOperation extends Operation {
}
@Nullable
private static TerminalOperation fromCollector(@NotNull String elementType,
private static TerminalOperation fromCollector(@NotNull PsiType elementType,
@NotNull PsiType resultType,
PsiMethod collector,
PsiExpression[] collectorArgs) {
@@ -199,13 +199,13 @@ abstract class TerminalOperation extends Operation {
return ReduceToOptionalTerminalOperation.create(collectorArgs[0], resultType);
case 2:
fn = FunctionHelper.create(collectorArgs[1], 2);
return fn == null ? null : new ReduceTerminalOperation(collectorArgs[0], fn, resultType.getCanonicalText());
return fn == null ? null : new ReduceTerminalOperation(collectorArgs[0], fn, resultType);
case 3:
FunctionHelper mapper = FunctionHelper.create(collectorArgs[1], 1);
fn = FunctionHelper.create(collectorArgs[2], 2);
return fn == null || mapper == null
? null
: new MappingTerminalOperation(mapper, new ReduceTerminalOperation(collectorArgs[0], fn, resultType.getCanonicalText()));
: new MappingTerminalOperation(mapper, new ReduceTerminalOperation(collectorArgs[0], fn, resultType));
}
return null;
case "counting":
@@ -274,9 +274,10 @@ abstract class TerminalOperation extends Operation {
if (collectorArgs.length != 1) return null;
return MinMaxTerminalOperation.create(collectorArgs[0], elementType, collectorName.equals("maxBy"));
case "joining":
PsiElementFactory factory = JavaPsiFacade.getElementFactory(collector.getProject());
switch (collectorArgs.length) {
case 0:
return new TemplateBasedOperation("sb", CommonClassNames.JAVA_LANG_STRING_BUILDER,
return new TemplateBasedOperation("sb", factory.createTypeFromText(CommonClassNames.JAVA_LANG_STRING_BUILDER, collector),
"new " + CommonClassNames.JAVA_LANG_STRING_BUILDER + "()",
"{acc}.append({item});",
"{acc}.toString()");
@@ -284,7 +285,7 @@ abstract class TerminalOperation extends Operation {
case 3:
String initializer =
"new java.util.StringJoiner(" + StreamEx.of(collectorArgs).map(PsiElement::getText).joining(",") + ")";
return new TemplateBasedOperation("joiner", "java.util.StringJoiner", initializer,
return new TemplateBasedOperation("joiner", factory.createTypeFromText("java.util.StringJoiner", collector), initializer,
"{acc}.add({item});", "{acc}.toString()");
}
return null;
@@ -340,10 +341,10 @@ abstract class TerminalOperation extends Operation {
static class ReduceTerminalOperation extends TerminalOperation {
private PsiExpression myIdentity;
private String myType;
private PsiType myType;
private FunctionHelper myUpdater;
public ReduceTerminalOperation(PsiExpression identity, FunctionHelper updater, String type) {
public ReduceTerminalOperation(PsiExpression identity, FunctionHelper updater, PsiType type) {
myIdentity = identity;
myType = type;
myUpdater = updater;
@@ -364,10 +365,10 @@ abstract class TerminalOperation extends Operation {
}
static class ReduceToOptionalTerminalOperation extends TerminalOperation {
private String myType;
private PsiType myType;
private FunctionHelper myUpdater;
public ReduceToOptionalTerminalOperation(FunctionHelper updater, String type) {
public ReduceToOptionalTerminalOperation(FunctionHelper updater, PsiType type) {
myType = type;
myUpdater = updater;
}
@@ -380,7 +381,7 @@ abstract class TerminalOperation extends Operation {
@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", ResultKind.UNKNOWN);
String accumulator = context.declareResult("acc", myType, myType instanceof PsiPrimitiveType ? "0" : "null", ResultKind.UNKNOWN);
myUpdater.transform(context, accumulator, inVar.getName());
context.setFinisher(new ConditionalExpression.Optional(myType, seen, accumulator));
String ifClause = "if(!" + seen + ") {\n" +
@@ -398,7 +399,7 @@ abstract class TerminalOperation extends Operation {
PsiType optionalElementType = OptionalUtil.getOptionalElementType(resultType);
FunctionHelper fn = FunctionHelper.create(arg, 2);
if(fn != null && optionalElementType != null) {
return new ReduceToOptionalTerminalOperation(fn, optionalElementType.getCanonicalText());
return new ReduceToOptionalTerminalOperation(fn, optionalElementType);
}
return null;
}
@@ -445,28 +446,29 @@ abstract class TerminalOperation extends Operation {
@Override
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
String sum = context.declareResult("sum", myDoubleAccumulator ? "double" : "long", "0", ResultKind.UNKNOWN);
String sum = context.declareResult("sum", myDoubleAccumulator ? PsiType.DOUBLE : PsiType.LONG, "0", ResultKind.UNKNOWN);
String count = context.declare("count", "long", "0");
String seenCheck = count + ">0";
String result = (myDoubleAccumulator ? "" : "(double)") + sum + "/" + count;
ConditionalExpression conditionalExpression = myUseOptional ?
new ConditionalExpression.Optional("double", seenCheck, result) :
new ConditionalExpression.Plain("double", seenCheck, result, "0.0");
new ConditionalExpression.Optional(PsiType.DOUBLE, seenCheck, result) :
new ConditionalExpression.Plain(PsiType.DOUBLE, seenCheck, result, "0.0");
context.setFinisher(conditionalExpression);
return sum + "+=" + inVar + ";\n" + count + "++;\n";
}
}
static class ToPrimitiveArrayTerminalOperation extends TerminalOperation {
private String myType;
private PsiType myType;
ToPrimitiveArrayTerminalOperation(String type) {
ToPrimitiveArrayTerminalOperation(PsiType type) {
myType = type;
}
@Override
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
String arr = context.declareResult("arr", myType + "[]", "new " + myType + "[10]", ResultKind.NON_FINAL);
String arr =
context.declareResult("arr", myType.createArrayType(), "new " + myType.getCanonicalText() + "[10]", ResultKind.NON_FINAL);
String count = context.declare("count", "int", "0");
context.addAfterStep(arr + "=java.util.Arrays.copyOfRange(" + arr + ",0," + count + ");\n");
return "if(" + arr + ".length==" + count + ") " + arr + "=java.util.Arrays.copyOf(" + arr + "," + count + "*2);\n" +
@@ -475,17 +477,18 @@ abstract class TerminalOperation extends Operation {
}
static class ToArrayTerminalOperation extends AccumulatedOperation {
private final String myType;
private final PsiType myType;
private final FunctionHelper mySupplier;
public ToArrayTerminalOperation(PsiType type, FunctionHelper supplier) {
myType = type.getCanonicalText();
myType = type;
mySupplier = supplier;
}
@Override
String initAccumulator(StreamVariable inVar, StreamToLoopReplacementContext context) {
String list = context.declareResult("list", CommonClassNames.JAVA_UTIL_LIST + "<" + myType + ">",
String list =
context.declareResult("list", context.createType(CommonClassNames.JAVA_UTIL_LIST + "<" + myType.getCanonicalText() + ">"),
"new " + CommonClassNames.JAVA_UTIL_ARRAY_LIST + "<>()", ResultKind.UNKNOWN);
String toArrayArg = "";
if(mySupplier != null) {
@@ -503,9 +506,9 @@ abstract class TerminalOperation extends Operation {
}
static class FindTerminalOperation extends TerminalOperation {
private String myType;
private PsiType myType;
public FindTerminalOperation(String type) {
public FindTerminalOperation(PsiType type) {
myType = type;
}
@@ -554,8 +557,7 @@ abstract class TerminalOperation extends Operation {
myFn.transform(context, inVar.getName());
String expression;
if (myNegatePredicate) {
PsiLambdaExpression lambda = (PsiLambdaExpression)context.createExpression("(" + inVar.getDeclaration() + ")->" + myFn.getText());
expression = BoolUtils.getNegatedExpressionText((PsiExpression)lambda.getBody());
expression = BoolUtils.getNegatedExpressionText(myFn.getExpression());
}
else {
expression = myFn.getText();
@@ -582,11 +584,11 @@ abstract class TerminalOperation extends Operation {
}
abstract static class CollectorBasedTerminalOperation extends AccumulatedOperation implements CollectorOperation {
final String myType;
final PsiType myType;
final Function<StreamToLoopReplacementContext, String> myAccNameSupplier;
final FunctionHelper mySupplier;
CollectorBasedTerminalOperation(String type, Function<StreamToLoopReplacementContext, String> accNameSupplier,
CollectorBasedTerminalOperation(PsiType type, Function<StreamToLoopReplacementContext, String> accNameSupplier,
FunctionHelper accSupplier) {
myType = type;
myAccNameSupplier = accNameSupplier;
@@ -596,8 +598,8 @@ abstract class TerminalOperation extends Operation {
@Override
String initAccumulator(StreamVariable inVar, StreamToLoopReplacementContext context) {
transform(context, inVar.getName());
PsiType resultType = correctReturnType(context.createType(myType));
return context.declareResult(myAccNameSupplier.apply(context), resultType.getCanonicalText(), getSupplier(), ResultKind.FINAL);
PsiType resultType = correctReturnType(myType);
return context.declareResult(myAccNameSupplier.apply(context), resultType, getSupplier(), ResultKind.FINAL);
}
@Override
@@ -623,7 +625,7 @@ abstract class TerminalOperation extends Operation {
static class TemplateBasedOperation extends AccumulatedOperation implements CollectorOperation {
private String myAccName;
private String myAccType;
private PsiType myAccType;
private String myAccInitializer;
private String myUpdateTemplate;
private String myFinisherTemplate;
@@ -637,7 +639,7 @@ abstract class TerminalOperation extends Operation {
* @param finisherTemplate template to final result. May contain {@code {acc}} - reference to accumulator variable.
* By default it's {@code "{acc}"}
*/
TemplateBasedOperation(String accName, String accType, String accInitializer, String updateTemplate, String finisherTemplate) {
TemplateBasedOperation(String accName, PsiType accType, String accInitializer, String updateTemplate, String finisherTemplate) {
myAccName = accName;
myAccType = accType;
myAccInitializer = accInitializer;
@@ -645,14 +647,14 @@ abstract class TerminalOperation extends Operation {
myFinisherTemplate = finisherTemplate;
}
TemplateBasedOperation(String accName, String accType, String accInitializer, String updateTemplate) {
TemplateBasedOperation(String accName, PsiType accType, String accInitializer, String updateTemplate) {
this(accName, accType, accInitializer, updateTemplate, "{acc}");
}
@Override
String initAccumulator(StreamVariable inVar, StreamToLoopReplacementContext context) {
ResultKind kind = myFinisherTemplate.equals("{acc}") ?
TypeConversionUtil.isPrimitive(myAccType) ? ResultKind.NON_FINAL : ResultKind.FINAL : ResultKind.UNKNOWN;
myAccType instanceof PsiPrimitiveType ? ResultKind.NON_FINAL : ResultKind.FINAL : ResultKind.UNKNOWN;
String varName = context.declareResult(myAccName, myAccType, myAccInitializer, kind);
context.setFinisher(myFinisherTemplate.replace("{acc}", varName));
return varName;
@@ -675,9 +677,9 @@ abstract class TerminalOperation extends Operation {
@Override
public String getMerger(StreamVariable inVar, String map, String key) {
String boxedType = PsiTypesUtil.boxIfPossible(myAccType);
if (boxedType.equals(myAccType)) return null;
String val = myUpdateTemplate.equals("{acc}++;") ? "1L" : "(" + myAccType + ")" + inVar;
if(!(myAccType instanceof PsiPrimitiveType)) return null;
String boxedType = PsiTypesUtil.boxIfPossible(myAccType.getCanonicalText());
String val = myUpdateTemplate.equals("{acc}++;") ? "1L" : "(" + myAccType.getCanonicalText() + ")" + inVar;
String merger = boxedType + "::sum";
return map + ".merge(" + key + "," + val + "," + merger + ");\n";
}
@@ -685,18 +687,18 @@ abstract class TerminalOperation extends Operation {
@NotNull
static TemplateBasedOperation summing(PsiType type) {
String defValue = type.equals(PsiType.DOUBLE) ? "0.0" : type.equals(PsiType.LONG) ? "0L" : "0";
return new TemplateBasedOperation("sum", type.getCanonicalText(), defValue, "{acc}+={item};");
return new TemplateBasedOperation("sum", type, defValue, "{acc}+={item};");
}
@NotNull
static TemplateBasedOperation summarizing(@NotNull PsiType resultType) {
return new TemplateBasedOperation("stat", resultType.getCanonicalText(), "new " + resultType.getCanonicalText() + "()",
return new TemplateBasedOperation("stat", resultType, "new " + resultType.getCanonicalText() + "()",
"{acc}.accept({item});");
}
@NotNull
static TemplateBasedOperation counting() {
return new TemplateBasedOperation("count", "long", "0L", "{acc}++;");
return new TemplateBasedOperation("count", PsiType.LONG, "0L", "{acc}++;");
}
}
@@ -704,7 +706,7 @@ abstract class TerminalOperation extends Operation {
private final boolean myList;
public ToCollectionTerminalOperation(PsiType resultType, FunctionHelper fn, String desiredName) {
super(resultType.getCanonicalText(), context -> fn.suggestFinalOutputNames(context, desiredName, "collection").get(0), fn);
super(resultType, context -> fn.suggestFinalOutputNames(context, desiredName, "collection").get(0), fn);
myList = InheritanceUtil.isInheritor(resultType, CommonClassNames.JAVA_UTIL_LIST);
}
@@ -736,11 +738,11 @@ abstract class TerminalOperation extends Operation {
}
static class MinMaxTerminalOperation extends TerminalOperation {
private String myType;
private PsiType myType;
private String myTemplate;
private @Nullable FunctionHelper myComparator;
public MinMaxTerminalOperation(String type, String template, @Nullable FunctionHelper comparator) {
public MinMaxTerminalOperation(PsiType type, String template, @Nullable FunctionHelper comparator) {
myType = type;
myTemplate = template;
myComparator = comparator;
@@ -756,7 +758,7 @@ abstract class TerminalOperation extends Operation {
@Override
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
String seen = context.declare("seen", "boolean", "false");
String best = context.declareResult("best", myType, TypeConversionUtil.isPrimitive(myType) ? "0" : "null", ResultKind.UNKNOWN);
String best = context.declareResult("best", myType, myType instanceof PsiPrimitiveType ? "0" : "null", ResultKind.UNKNOWN);
context.setFinisher(new ConditionalExpression.Optional(myType, seen, best));
String comparePredicate;
if(myComparator != null) {
@@ -774,13 +776,13 @@ abstract class TerminalOperation extends Operation {
}
@Nullable
static MinMaxTerminalOperation create(@Nullable PsiExpression comparator, String elementType, boolean max) {
static MinMaxTerminalOperation create(@Nullable PsiExpression comparator, PsiType elementType, boolean max) {
String sign = max ? ">" : "<";
if(comparator == null) {
if (PsiType.INT.equalsToText(elementType) || PsiType.LONG.equalsToText(elementType)) {
if (PsiType.INT.equals(elementType) || PsiType.LONG.equals(elementType)) {
return new MinMaxTerminalOperation(elementType, "{item}" + sign + "{best}", null);
}
if (PsiType.DOUBLE.equalsToText(elementType)) {
if (PsiType.DOUBLE.equals(elementType)) {
return new MinMaxTerminalOperation(elementType, "java.lang.Double.compare({item},{best})" + sign + "0", null);
}
}
@@ -803,7 +805,7 @@ abstract class TerminalOperation extends Operation {
PsiExpression merger,
FunctionHelper supplier,
PsiType resultType) {
super(resultType.getCanonicalText(), context -> "map", supplier);
super(resultType, context -> "map", supplier);
myKeyExtractor = keyExtractor;
myValueExtractor = valueExtractor;
myMerger = merger;
@@ -872,7 +874,7 @@ abstract class TerminalOperation extends Operation {
private String myKeyVar;
public GroupByTerminalOperation(FunctionHelper keyExtractor, FunctionHelper supplier, PsiType resultType, CollectorOperation collector) {
super(resultType.getCanonicalText(), context -> "map", supplier);
super(resultType, context -> "map", supplier);
myKeyExtractor = keyExtractor;
myCollector = collector;
}
@@ -941,7 +943,7 @@ abstract class TerminalOperation extends Operation {
PsiType resultType = context.createType(myResultType);
resultType = correctTypeParameters(resultType, CommonClassNames.JAVA_UTIL_MAP,
Collections.singletonMap("V", myCollector::correctReturnType));
String map = context.declareResult("map", resultType.getCanonicalText(), "new java.util.HashMap<>()", ResultKind.FINAL);
String map = context.declareResult("map", resultType, "new java.util.HashMap<>()", ResultKind.FINAL);
myPredicate.transform(context, inVar.getName());
myCollector.transform(context, inVar.getName());
context.addBeforeStep(map + ".put(false, " + myCollector.getSupplier() + ");");
@@ -1008,7 +1010,7 @@ abstract class TerminalOperation extends Operation {
myMapper.transform(context, item);
myVariable = new StreamVariable(myMapper.getResultType());
myDownstream.preprocessVariables(context, myVariable, StreamVariable.STUB);
myMapper.suggestFinalOutputNames(context, null, null).forEach(myVariable::addOtherNameCandidate);
myMapper.suggestOutputNames(context, myVariable);
myVariable.register(context);
}
@@ -58,7 +58,7 @@ public class LambdaRefactoringUtil {
public static PsiLambdaExpression convertMethodReferenceToLambda(final PsiMethodReferenceExpression referenceExpression,
final boolean ignoreCast,
final boolean simplifyToExpressionLambda) {
PsiLambdaExpression lambdaExpression = convertToLambda(referenceExpression, ignoreCast);
PsiLambdaExpression lambdaExpression = createLambda(referenceExpression, ignoreCast);
if (lambdaExpression == null) return null;
lambdaExpression = (PsiLambdaExpression)referenceExpression.replace(lambdaExpression);
@@ -70,17 +70,24 @@ public class LambdaRefactoringUtil {
}
public static boolean canConvertToLambda(PsiMethodReferenceExpression referenceExpression) {
return convertToLambda(referenceExpression, false) != null;
return createLambda(referenceExpression, false) != null;
}
private static PsiLambdaExpression convertToLambda(PsiMethodReferenceExpression referenceExpression, boolean ignoreCast) {
/**
* Convert method reference to lambda if possible and return the created lambda without replacing original method reference.
*
* @param referenceExpression a method reference to convert
* @param doNotAddParameterTypes if false, parameter types could be added to the lambda to resolve ambiguity
* @return a created lambda or null if conversion fails
*/
public static PsiLambdaExpression createLambda(PsiMethodReferenceExpression referenceExpression, boolean doNotAddParameterTypes) {
String lambda = createLambdaWithoutFormalParameters(referenceExpression);
if (lambda == null) return null;
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(referenceExpression.getProject());
PsiLambdaExpression lambdaExpression = (PsiLambdaExpression)elementFactory.createExpressionFromText(lambda, referenceExpression);
final PsiType functionalInterfaceType = referenceExpression.getFunctionalInterfaceType();
boolean needToSpecifyFormalTypes = !ignoreCast && !isInferredSameTypeAfterConversion(lambdaExpression, referenceExpression, functionalInterfaceType);
boolean needToSpecifyFormalTypes = !doNotAddParameterTypes && !isInferredSameTypeAfterConversion(lambdaExpression, referenceExpression, functionalInterfaceType);
if (needToSpecifyFormalTypes) {
PsiParameterList typedParamList = specifyLambdaParameterTypes(functionalInterfaceType, lambdaExpression);
if (typedParamList == null) {
@@ -15,10 +15,10 @@ public class Main {
boolean seen = false;
int best = 0;
for (Index index : set.asList()) {
int i = index.asInteger();
if (!seen || i < best) {
int asInteger = index.asInteger();
if (!seen || asInteger < best) {
seen = true;
best = i;
best = asInteger;
}
}
return seen ? OptionalInt.of(best) : OptionalInt.empty();
@@ -10,8 +10,8 @@ public class Main {
List<Integer> list = new ArrayList<>();
for (int x : input) {
if (x > 0) {
Integer i = x * 2;
list.add(i);
Integer integer = x * 2;
list.add(integer);
}
}
return list;
@@ -130,8 +130,8 @@ public class Main {
private static List<String> testMethodRef(List<List<String>> list) {
List<String> result = new ArrayList<>();
for (List<String> strings : list) {
for (String s : strings) {
result.add(s);
for (String string : strings) {
result.add(string);
}
}
return result;
@@ -140,8 +140,8 @@ public class Main {
private static List<String> testMethodRef2(List<String[]> list) {
List<String> result = new ArrayList<>();
for (String[] strings : list) {
for (String s : strings) {
result.add(s);
for (String t : strings) {
result.add(t);
}
}
return result;
@@ -39,7 +39,7 @@ public class Main {
public static boolean testIsPresent(List<List<String>> list) {
for (List<String> strings : list) {
if (strings != null) {
for (String s : strings) {
for (String string : strings) {
return true;
}
}