mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-13 21:55:01 +07:00
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:
+13
-12
@@ -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 +
|
||||
|
||||
+19
-21
@@ -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() {
|
||||
|
||||
+57
-55
@@ -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) {
|
||||
|
||||
+3
-3
@@ -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();
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
+4
-4
@@ -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;
|
||||
|
||||
+1
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user