IDEA-182623 Replace with collect swallows comments

This commit is contained in:
Tagir Valeev
2017-11-27 15:22:26 +07:00
parent 5b8f3b2bfe
commit 5b36191d2c
29 changed files with 306 additions and 263 deletions
@@ -17,7 +17,7 @@ package com.intellij.codeInspection.streamMigration;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.ControlFlowUtils;
import com.siyeh.ig.psiutils.ControlFlowUtils.InitializerUsageStatus;
import org.jetbrains.annotations.NotNull;
@@ -53,74 +53,59 @@ abstract class BaseStreamApiMigration {
PsiVariable var,
String streamText,
PsiType expressionType,
OperationReductionMigration.ReductionOperation reductionOperation) {
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(loopStatement.getProject());
restoreComments(loopStatement, loopStatement instanceof PsiLoopStatement? ((PsiLoopStatement)loopStatement).getBody(): loopStatement);
OperationReductionMigration.ReductionOperation reductionOperation,
CommentTracker ct) {
InitializerUsageStatus status = ControlFlowUtils.getInitializerUsageStatus(var, loopStatement);
if (status != InitializerUsageStatus.UNKNOWN) {
PsiExpression initializer = var.getInitializer();
if (initializer != null && reductionOperation.getInitializerExpressionRestriction().test(initializer)) {
PsiType type = var.getType();
String replacement = (type.isAssignableFrom(expressionType) ? "" : "(" + type.getCanonicalText() + ") ") + streamText;
return replaceInitializer(loopStatement, var, initializer, replacement, status);
return replaceInitializer(loopStatement, var, initializer, replacement, status, ct);
}
}
return loopStatement
.replace(elementFactory.createStatementFromText(var.getName() + reductionOperation.getOperation() + "=" + streamText + ";",
loopStatement));
return ct.replaceAndRestoreComments(loopStatement, var.getName() + reductionOperation.getOperation() + "=" + streamText + ";");
}
static PsiElement replaceInitializer(PsiStatement loopStatement,
PsiVariable var,
PsiExpression initializer,
String replacement,
InitializerUsageStatus status) {
Project project = loopStatement.getProject();
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
InitializerUsageStatus status,
CommentTracker ct) {
if (status == ControlFlowUtils.InitializerUsageStatus.DECLARED_JUST_BEFORE) {
initializer.replace(elementFactory.createExpressionFromText(replacement, loopStatement));
removeLoop(loopStatement);
ct.replace(initializer, replacement);
removeLoop(ct, loopStatement);
return var;
}
else {
if (status == ControlFlowUtils.InitializerUsageStatus.AT_WANTED_PLACE_ONLY) {
initializer.delete();
ct.delete(initializer);
}
return
loopStatement.replace(elementFactory.createStatementFromText(var.getName() + " = " + replacement + ";", loopStatement));
return ct.replaceAndRestoreComments(loopStatement, var.getName() + " = " + replacement + ";");
}
}
@Nullable
static PsiElement replaceWithFindExtremum(@NotNull PsiStatement loopStatement,
static PsiElement replaceWithFindExtremum(@NotNull CommentTracker ct, @NotNull PsiStatement loopStatement,
@NotNull PsiVariable extremumHolder,
@NotNull String streamText,
@Nullable PsiVariable keyExtremum) {
restoreComments(loopStatement, loopStatement instanceof PsiLoopStatement? ((PsiLoopStatement)loopStatement).getBody(): loopStatement);
if(keyExtremum != null) {
keyExtremum.delete();
ct.delete(keyExtremum);
}
InitializerUsageStatus status = ControlFlowUtils.getInitializerUsageStatus(extremumHolder, loopStatement);
return replaceInitializer(loopStatement, extremumHolder, extremumHolder.getInitializer(), streamText, status);
return replaceInitializer(loopStatement, extremumHolder, extremumHolder.getInitializer(), streamText, status, ct);
}
static void restoreComments(PsiStatement statement, PsiElement body) {
if(statement instanceof PsiLoopStatement || statement instanceof PsiExpressionStatement) {
final PsiElement parent = statement.getParent();
for (PsiElement comment : PsiTreeUtil.findChildrenOfType(body, PsiComment.class)) {
parent.addBefore(comment, statement);
}
}
}
static void removeLoop(@NotNull PsiStatement statement) {
static void removeLoop(CommentTracker ct, @NotNull PsiStatement statement) {
PsiElement parent = statement.getParent();
if (parent instanceof PsiLabeledStatement) {
parent.delete();
ct.deleteAndRestoreComments(parent);
}
else {
statement.delete();
ct.deleteAndRestoreComments(statement);
}
}
}
@@ -79,21 +79,20 @@ class CollectMigration extends BaseStreamApiMigration {
@Override
PsiElement migrate(@NotNull Project project, @NotNull PsiElement body, @NotNull TerminalBlock tb) {
PsiStatement loopStatement = tb.getStreamSourceStatement();
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
CollectTerminal terminal = extractCollectTerminal(tb, null);
if (terminal == null) return null;
String stream = tb.generate() + terminal.generateIntermediate() + terminal.generateTerminal();
CommentTracker ct = new CommentTracker();
String stream = tb.generate(ct) + terminal.generateIntermediate(ct) + terminal.generateTerminal(ct);
PsiElement toReplace = terminal.getElementToReplace();
restoreComments(loopStatement, body);
PsiElement result;
if (toReplace != null) {
result = toReplace.replace(factory.createExpressionFromText(stream, toReplace));
removeLoop(loopStatement);
result = ct.replace(toReplace, stream);
removeLoop(ct, loopStatement);
}
else {
PsiVariable variable = terminal.getTargetVariable();
LOG.assertTrue(variable != null);
result = replaceInitializer(loopStatement, variable, variable.getInitializer(), stream, terminal.getStatus());
result = replaceInitializer(loopStatement, variable, variable.getInitializer(), stream, terminal.getStatus(), ct);
}
terminal.cleanUp();
return result;
@@ -193,7 +192,7 @@ class CollectMigration extends BaseStreamApiMigration {
@Nullable
PsiLocalVariable getTargetVariable() { return myTargetVariable; }
abstract String generateIntermediate();
abstract String generateIntermediate(CommentTracker ct);
StreamEx<? extends PsiExpression> targetReferences() {
List<PsiElement> usedElements = usedElements().toList();
@@ -215,7 +214,7 @@ class CollectMigration extends BaseStreamApiMigration {
return INTERMEDIATE_STEPS.get(aClass.getQualifiedName());
}
abstract String generateTerminal();
abstract String generateTerminal(CommentTracker ct);
StreamEx<PsiElement> usedElements() {
return StreamEx.ofNullable(myLoop);
@@ -230,7 +229,7 @@ class CollectMigration extends BaseStreamApiMigration {
void cleanUp() {}
boolean isTrivial() {
return generateIntermediate().isEmpty();
return generateIntermediate(new CommentTracker()).isEmpty();
}
}
@@ -272,20 +271,20 @@ class CollectMigration extends BaseStreamApiMigration {
}
@Override
public String generateIntermediate() {
public String generateIntermediate(CommentTracker ct) {
PsiType addedType = getAddedElementType(myAddCall);
PsiExpression mapping = getMapping();
if (addedType == null) addedType = mapping.getType();
return StreamRefactoringUtil.generateMapOperation(myElement, addedType, mapping);
return StreamRefactoringUtil.generateMapOperation(myElement, addedType, ct.markUnchanged(mapping));
}
public String generateCollector() {
return getCollectionCollector(myInitializer, myTargetType);
public String generateCollector(CommentTracker ct) {
return getCollectionCollector(ct.markUnchanged(myInitializer), myTargetType);
}
@Override
public String generateTerminal() {
return ".collect(" + generateCollector() + ")";
public String generateTerminal(CommentTracker ct) {
return ".collect(" + generateCollector(ct) + ")";
}
@Nullable
@@ -361,7 +360,7 @@ class CollectMigration extends BaseStreamApiMigration {
}
@Override
public String generateIntermediate() {
public String generateIntermediate(CommentTracker ct) {
PsiType[] typeParameters = myAddAllCall.getMethodExpression().getTypeParameters();
String generic = "";
if(typeParameters.length == 1) {
@@ -370,7 +369,7 @@ class CollectMigration extends BaseStreamApiMigration {
String method = MethodCallUtils.isVarArgCall(myAddAllCall) ? CommonClassNames.JAVA_UTIL_STREAM_STREAM + "." + generic + "of"
: CommonClassNames.JAVA_UTIL_ARRAYS + "." + generic + "stream";
String lambda = myElement.getName() + "->" + method + "(" +
StreamEx.of(myAddAllCall.getArgumentList().getExpressions()).skip(1).map(PsiExpression::getText).joining(",") + ")";
StreamEx.of(myAddAllCall.getArgumentList().getExpressions()).skip(1).map(ct::text).joining(",") + ")";
return myElement.getType() instanceof PsiPrimitiveType ?
".mapToObj(" + lambda + ").flatMap("+ CommonClassNames.JAVA_UTIL_FUNCTION_FUNCTION+".identity())" :
".flatMap(" + lambda + ")";
@@ -416,27 +415,27 @@ class CollectMigration extends BaseStreamApiMigration {
}
@Override
String generateIntermediate() {
String generateIntermediate(CommentTracker ct) {
return myDownstream.myElement.getType() instanceof PsiPrimitiveType ? ".boxed()" : "";
}
@Override
public String generateTerminal() {
String downstreamCollector = myDownstream.generateCollector();
public String generateTerminal(CommentTracker ct) {
String downstreamCollector = myDownstream.generateCollector(ct);
PsiVariable elementVariable = myDownstream.getElementVariable();
if (!ExpressionUtils.isReferenceTo(myDownstream.getMapping(), myDownstream.getElementVariable())) {
downstreamCollector = CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + ".mapping(" +
myDownstream.getElementVariable().getName() + "->" + myDownstream.getMapping().getText() + "," +
myDownstream.getElementVariable().getName() + "->" + ct.text(myDownstream.getMapping()) + "," +
downstreamCollector + ")";
}
StringBuilder builder = new StringBuilder();
builder.append(".collect(" + CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + ".groupingBy(")
.append(LambdaUtil.createLambda(elementVariable, myKeyExpression));
.append(ct.lambdaText(elementVariable, myKeyExpression));
PsiLocalVariable variable = Objects.requireNonNull(getTargetVariable());
PsiExpression initializer = variable.getInitializer();
LOG.assertTrue(initializer != null);
if (!isHashMap(variable)) {
builder.append(",()->").append(initializer.getText()).append(",").append(downstreamCollector);
builder.append(",()->").append(ct.text(initializer)).append(",").append(downstreamCollector);
}
else if (!(CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + "." + "toList()").equals(downstreamCollector)) {
builder.append(",").append(downstreamCollector);
@@ -592,12 +591,12 @@ class CollectMigration extends BaseStreamApiMigration {
}
@Override
String generateIntermediate() {
String generateIntermediate(CommentTracker ct) {
return myElementVariable.getType() instanceof PsiPrimitiveType ? ".boxed()" : "";
}
@Override
public String generateTerminal() {
public String generateTerminal(CommentTracker ct) {
PsiExpression[] args = myMapUpdateCall.getArgumentList().getExpressions();
LOG.assertTrue(args.length >= 2);
String methodName = myMapUpdateCall.getMethodExpression().getReferenceName();
@@ -616,20 +615,20 @@ class CollectMigration extends BaseStreamApiMigration {
break;
case "merge":
LOG.assertTrue(args.length == 3);
merger = args[2].getText();
merger = ct.text(args[2]);
break;
default:
return null;
}
StringBuilder collector = new StringBuilder(".collect(" + CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + ".toMap(");
collector.append(LambdaUtil.createLambda(myElementVariable, args[0])).append(',')
.append(LambdaUtil.createLambda(myElementVariable, args[1])).append(',')
collector.append(ct.lambdaText(myElementVariable, args[0])).append(',')
.append(ct.lambdaText(myElementVariable, args[1])).append(',')
.append(merger);
PsiLocalVariable variable = Objects.requireNonNull(getTargetVariable());
PsiExpression initializer = variable.getInitializer();
LOG.assertTrue(initializer != null);
if (!isHashMap(variable)) {
collector.append(",()->").append(initializer.getText());
collector.append(",()->").append(ct.text(initializer));
}
collector.append("))");
return collector.toString();
@@ -665,14 +664,14 @@ class CollectMigration extends BaseStreamApiMigration {
}
@Override
public String generateIntermediate() {
return myDownstream.generateIntermediate() + ".sorted("
+ (myComparator == null ? "" : myComparator.getText()) + ")";
public String generateIntermediate(CommentTracker ct) {
return myDownstream.generateIntermediate(ct) + ".sorted("
+ (myComparator == null ? "" : ct.text(myComparator)) + ")";
}
@Override
public String generateTerminal() {
return myDownstream.generateTerminal();
public String generateTerminal(CommentTracker ct) {
return myDownstream.generateTerminal(ct);
}
@Override
@@ -760,8 +759,8 @@ class CollectMigration extends BaseStreamApiMigration {
}
@Override
public String generateIntermediate() {
return myUpstream.generateIntermediate() + myIntermediate;
public String generateIntermediate(CommentTracker ct) {
return myUpstream.generateIntermediate(ct) + myIntermediate;
}
@Override
@@ -792,7 +791,7 @@ class CollectMigration extends BaseStreamApiMigration {
}
@Override
public String generateTerminal() {
public String generateTerminal(CommentTracker ct) {
return ".toArray(" + mySupplier + ")";
}
@@ -865,8 +864,8 @@ class CollectMigration extends BaseStreamApiMigration {
}
@Override
public String generateTerminal() {
return ".collect(" + getCollectionCollector(myCreateExpression, myResultType) + ")";
public String generateTerminal(CommentTracker ct) {
return ".collect(" + getCollectionCollector(ct.markUnchanged(myCreateExpression), myResultType) + ")";
}
@Override
@@ -17,6 +17,7 @@ package com.intellij.codeInspection.streamMigration;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.siyeh.ig.psiutils.CommentTracker;
import org.jetbrains.annotations.NotNull;
import static com.intellij.codeInspection.streamMigration.OperationReductionMigration.SUM_OPERATION;
@@ -41,6 +42,7 @@ class CountMigration extends BaseStreamApiMigration {
PsiElement element = ((PsiReferenceExpression)operand).resolve();
if (!(element instanceof PsiLocalVariable)) return null;
PsiLocalVariable var = (PsiLocalVariable)element;
return replaceWithOperation(tb.getStreamSourceStatement(), var, tb.generate() + ".count()", PsiType.LONG, SUM_OPERATION);
CommentTracker ct = new CommentTracker();
return replaceWithOperation(tb.getStreamSourceStatement(), var, tb.generate(ct) + ".count()", PsiType.LONG, SUM_OPERATION, ct);
}
}
@@ -22,10 +22,7 @@ import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.InheritanceUtil;
import com.siyeh.ig.psiutils.ControlFlowUtils;
import com.siyeh.ig.psiutils.EquivalenceChecker;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.VariableAccessUtils;
import com.siyeh.ig.psiutils.*;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -288,13 +285,14 @@ class FindExtremumMigration extends BaseStreamApiMigration {
String inFilterOperation = myMax ? ">=" : "<=";
PsiStatement loop = myTerminalBlock.getStreamSourceStatement();
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(loop.getProject());
String extremumInitializer = myExtremumKeyInitializer.getText();
CommentTracker ct = new CommentTracker();
String extremumInitializer = ct.text(myExtremumKeyInitializer);
PsiExpression condition =
elementFactory.createExpressionFromText(myExtremumKeyExpr.getText() + inFilterOperation + extremumInitializer, loop);
elementFactory.createExpressionFromText(ct.text(myExtremumKeyExpr) + inFilterOperation + extremumInitializer, loop);
TerminalBlock blockWithFilter =
myTerminalBlock.add(new StreamApiMigrationInspection.FilterOp(condition, myTerminalBlock.getVariable(), false));
String lambdaText = LambdaUtil.createLambda(myTerminalBlock.getVariable(), myExtremumKeyExpr);
String lambdaText = ct.lambdaText(myTerminalBlock.getVariable(), myExtremumKeyExpr);
String comparator;
if(myComparator == null) {
comparator = CommonClassNames.JAVA_UTIL_COMPARATOR + "." + method + "(" + lambdaText + ")";
@@ -303,8 +301,8 @@ class FindExtremumMigration extends BaseStreamApiMigration {
if(comparatorName == null) return null;
comparator = comparatorName;
}
String stream = blockWithFilter.generate() + "." + getOperation(myMax) + "(" + comparator + ").orElse(null)";
return replaceWithFindExtremum(myTerminalBlock.getStreamSourceStatement(), myExtremum, stream, myExtremumKey);
String stream = blockWithFilter.generate(ct) + "." + getOperation(myMax) + "(" + comparator + ").orElse(null)";
return replaceWithFindExtremum(ct, myTerminalBlock.getStreamSourceStatement(), myExtremum, stream, myExtremumKey);
}
@Override
@@ -472,10 +470,11 @@ class FindExtremumMigration extends BaseStreamApiMigration {
} else {
terminalBlock = blockWithMap;
}
CommentTracker ct = new CommentTracker();
String inFilterOperation = myMax ? ">=" : "<=";
PsiStatement loop = terminalBlock.getStreamSourceStatement();
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(loop.getProject());
String extremumInitializer = myExtremumInitializer.getText();
String extremumInitializer = ct.text(myExtremumInitializer);
Object nonFilterableInitialValue = getNonFilterableInitialValue(type, myMax);
final TerminalBlock filteredTerminalBlock;
if (nonFilterableInitialValue != null && !nonFilterableInitialValue.equals(initializerValue)) {
@@ -488,9 +487,8 @@ class FindExtremumMigration extends BaseStreamApiMigration {
filteredTerminalBlock = terminalBlock;
}
String stream = filteredTerminalBlock.generate() + "." + getOperation(myMax) + "().orElse(" + extremumInitializer + ")";
return replaceWithFindExtremum(loop, myExtremum, stream, null);
String stream = filteredTerminalBlock.generate(ct) + "." + getOperation(myMax) + "().orElse(" + extremumInitializer + ")";
return replaceWithFindExtremum(ct, loop, myExtremum, stream, null);
}
@@ -558,13 +556,14 @@ class FindExtremumMigration extends BaseStreamApiMigration {
PsiType loopVarExpressionType = myLoopVarExpression.getType();
if (loopVarExpressionType == null) return null;
final String comparator;
CommentTracker ct = new CommentTracker();
if(myComparator == null) {
if(ExpressionUtils.isReferenceTo(myLoopVarExpression, myTerminalBlock.getVariable())) {
comparator = CommonClassNames.JAVA_UTIL_COMPARATOR + ".naturalOrder()";
} else {
String method = getComparingMethod(loopVarExpressionType);
if (method == null) return null;
String lambdaText = LambdaUtil.createLambda(myTerminalBlock.getVariable(), myLoopVarExpression);
String lambdaText = ct.lambdaText(myTerminalBlock.getVariable(), myLoopVarExpression);
comparator = CommonClassNames.JAVA_UTIL_COMPARATOR + "." + method + "(" + lambdaText + ")";
}
} else {
@@ -572,8 +571,8 @@ class FindExtremumMigration extends BaseStreamApiMigration {
if(comparatorName == null) return null;
comparator = comparatorName;
}
String stream = myTerminalBlock.generate() + "." + getOperation(myMax) + "(" + comparator + ").orElse(null)";
return replaceWithFindExtremum(myTerminalBlock.getStreamSourceStatement(), myExtremum, stream, null);
String stream = myTerminalBlock.generate(ct) + "." + getOperation(myMax) + "(" + comparator + ").orElse(null)";
return replaceWithFindExtremum(ct, myTerminalBlock.getStreamSourceStatement(), myExtremum, stream, null);
}
@Override
@@ -20,6 +20,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiTypesUtil;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.ControlFlowUtils;
import com.siyeh.ig.psiutils.ControlFlowUtils.InitializerUsageStatus;
import com.siyeh.ig.psiutils.ExpressionUtils;
@@ -36,8 +37,8 @@ class FindFirstMigration extends BaseStreamApiMigration {
@Override
PsiElement migrate(@NotNull Project project, @NotNull PsiElement body, @NotNull TerminalBlock tb) {
PsiStatement statement = tb.getSingleStatement();
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
PsiStatement loopStatement = tb.getStreamSourceStatement();
CommentTracker ct = new CommentTracker();
if (statement instanceof PsiReturnStatement) {
PsiReturnStatement returnStatement = (PsiReturnStatement)statement;
PsiExpression value = returnStatement.getReturnValue();
@@ -46,12 +47,11 @@ class FindFirstMigration extends BaseStreamApiMigration {
if (nextReturnStatement == null) return null;
PsiExpression orElseExpression = nextReturnStatement.getReturnValue();
if (!ExpressionUtils.isSimpleExpression(orElseExpression)) return null;
String stream = generateOptionalUnwrap(tb, value, orElseExpression, PsiTypesUtil.getMethodReturnType(returnStatement));
restoreComments(loopStatement, body);
String stream = generateOptionalUnwrap(ct, tb, value, orElseExpression, PsiTypesUtil.getMethodReturnType(returnStatement));
boolean sibling = nextReturnStatement.getParent() == loopStatement.getParent();
PsiElement replacement = loopStatement.replace(elementFactory.createStatementFromText("return " + stream + ";", loopStatement));
PsiElement replacement = ct.replaceAndRestoreComments(loopStatement, "return " + stream + ";");
if(sibling || !ControlFlowUtils.isReachable(nextReturnStatement)) {
nextReturnStatement.delete();
new CommentTracker().deleteAndRestoreComments(nextReturnStatement);
}
return replacement;
}
@@ -62,9 +62,8 @@ class FindFirstMigration extends BaseStreamApiMigration {
if (assignment == null) {
if(!(statements[0] instanceof PsiExpressionStatement)) return null;
PsiExpression expression = ((PsiExpressionStatement)statements[0]).getExpression();
restoreComments(loopStatement, body);
return loopStatement.replace(elementFactory.createStatementFromText(
tb.generate() + ".findFirst().ifPresent(" + LambdaUtil.createLambda(tb.getVariable(), expression) + ");", loopStatement));
return ct.replaceAndRestoreComments(
loopStatement, tb.generate(ct) + ".findFirst().ifPresent(" + ct.lambdaText(tb.getVariable(), expression) + ");");
}
PsiReferenceExpression lValue = tryCast(assignment.getLExpression(), PsiReferenceExpression.class);
if (lValue == null) return null;
@@ -72,7 +71,6 @@ class FindFirstMigration extends BaseStreamApiMigration {
if (var == null) return null;
PsiExpression value = assignment.getRExpression();
if (value == null) return null;
restoreComments(loopStatement, body);
InitializerUsageStatus status = ControlFlowUtils.getInitializerUsageStatus(var, loopStatement);
PsiExpression initializer = var.getInitializer();
PsiExpression falseExpression = lValue;
@@ -83,19 +81,20 @@ class FindFirstMigration extends BaseStreamApiMigration {
PsiElement maybeAssignment = PsiTreeUtil.skipWhitespacesAndCommentsBackward(loopStatement);
PsiExpression prevRValue = ExpressionUtils.getAssignmentTo(maybeAssignment, var);
if (prevRValue != null) {
maybeAssignment.delete();
ct.delete(maybeAssignment);
falseExpression = prevRValue;
}
}
String replacementText = generateOptionalUnwrap(tb, value, falseExpression, var.getType());
return replaceInitializer(loopStatement, var, initializer, replacementText, status);
String replacementText = generateOptionalUnwrap(ct, tb, value, falseExpression, var.getType());
return replaceInitializer(loopStatement, var, initializer, replacementText, status, ct);
}
}
private static String generateOptionalUnwrap(TerminalBlock tb,
private static String generateOptionalUnwrap(CommentTracker ct, TerminalBlock tb,
PsiExpression trueExpression, PsiExpression falseExpression,
PsiType targetType) {
String qualifier = tb.generate() + ".findFirst()";
return OptionalUtil.generateOptionalUnwrap(qualifier, tb.getVariable(), trueExpression, falseExpression, targetType, false);
String qualifier = tb.generate(ct) + ".findFirst()";
return OptionalUtil.generateOptionalUnwrap(
qualifier, tb.getVariable(), ct.markUnchanged(trueExpression), ct.markUnchanged(falseExpression), targetType, false);
}
}
@@ -22,6 +22,7 @@ import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.SuggestedNameInfo;
import com.intellij.psi.codeStyle.VariableKind;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.VariableAccessUtils;
import org.jetbrains.annotations.Contract;
@@ -59,7 +60,7 @@ class ForEachMigration extends BaseStreamApiMigration {
@Override
PsiElement migrate(@NotNull Project project, @NotNull PsiElement body, @NotNull TerminalBlock tb) {
PsiStatement loopStatement = tb.getStreamSourceStatement();
restoreComments(loopStatement, body);
CommentTracker ct = new CommentTracker();
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
@@ -78,21 +79,20 @@ class ForEachMigration extends BaseStreamApiMigration {
}
String varName = codeStyleManager.suggestUniqueVariableName(suggestedNameInfo, call, false).names[0];
String streamText = tb.add(new StreamApiMigrationInspection.MapOp(mapExpression, tb.getVariable(), addedType)).generate();
String forEachBody = varName + "->" + call.getMethodExpression().getText() + "(" + varName + ")";
String streamText = tb.add(new StreamApiMigrationInspection.MapOp(mapExpression, tb.getVariable(), addedType)).generate(ct);
String forEachBody = varName + "->" + ct.text(call.getMethodExpression()) + "(" + varName + ")";
String callText = streamText + "." + getReplacement() + "(" + forEachBody + ");";
return loopStatement.replace(factory.createStatementFromText(callText, loopStatement));
return ct.replaceAndRestoreComments(loopStatement, callText);
}
tb.replaceContinueWithReturn(factory);
String stream = tb.generate(true) + "." + getReplacement() + "(";
PsiElement block = tb.convertToElement(factory);
String stream = tb.generate(ct, true) + "." + getReplacement() + "(";
PsiElement block = tb.convertToElement(ct, factory);
final String functionalExpressionText = tb.getVariable().getName() + " -> " + wrapInBlock(block);
PsiExpressionStatement callStatement = (PsiExpressionStatement)factory
.createStatementFromText(stream + functionalExpressionText + ");", loopStatement);
callStatement = (PsiExpressionStatement)loopStatement.replace(callStatement);
PsiExpressionStatement callStatement =
(PsiExpressionStatement)ct.replaceAndRestoreComments(loopStatement, stream + functionalExpressionText + ");");
final PsiExpressionList argumentList = ((PsiCallExpression)callStatement.getExpression()).getArgumentList();
LOG.assertTrue(argumentList != null, callStatement.getText());
@@ -10,6 +10,7 @@ import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.ArrayUtil;
import com.siyeh.ig.callMatcher.CallMatcher;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.ControlFlowUtils;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.Nls;
@@ -42,14 +43,14 @@ public class FuseStreamOperationsInspection extends AbstractBaseJavaLocalInspect
}
@Override
String generateIntermediate() {
String generateIntermediate(CommentTracker ct) {
PsiExpression qualifier = myChain.getMethodExpression().getQualifierExpression();
return Objects.requireNonNull(qualifier).getText();
return ct.text(Objects.requireNonNull(qualifier));
}
@Override
String generateTerminal() {
return ".collect(" + myCollector.getText() + ")";
String generateTerminal(CommentTracker ct) {
return ".collect(" + ct.text(myCollector) + ")";
}
private static PsiClass resolveClassCreatedByFunction(PsiExpression function) {
@@ -184,19 +185,19 @@ public class FuseStreamOperationsInspection extends AbstractBaseJavaLocalInspect
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiMethodCallExpression chain = PsiTreeUtil.getParentOfType(descriptor.getStartElement(), PsiMethodCallExpression.class);
if (chain == null) return;
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
CollectTerminal terminal = extractTerminal(chain);
if (terminal == null) return;
String stream = terminal.generateIntermediate() + terminal.generateTerminal();
CommentTracker ct = new CommentTracker();
String stream = terminal.generateIntermediate(ct) + terminal.generateTerminal(ct);
PsiElement toReplace = terminal.getElementToReplace();
PsiElement result;
if (toReplace != null) {
result = toReplace.replace(factory.createExpressionFromText(stream, toReplace));
result = ct.replaceAndRestoreComments(toReplace, stream);
}
else {
PsiVariable variable = Objects.requireNonNull(terminal.getTargetVariable());
PsiExpression initializer = Objects.requireNonNull(variable.getInitializer());
result = initializer.replace(factory.createExpressionFromText(stream, initializer));
result = ct.replaceAndRestoreComments(initializer, stream);
}
terminal.cleanUp();
LambdaCanBeMethodReferenceInspection.replaceAllLambdasWithMethodReferences(result);
@@ -61,13 +61,13 @@ public class JoiningMigration extends BaseStreamApiMigration {
TerminalBlock block = terminal.getTerminalBlock();
PsiStatement loopStatement = block.getStreamSourceStatement();
String stream = terminal.generateStreamCode();
restoreComments(loopStatement, body);
CommentTracker ct = new CommentTracker();
String stream = terminal.generateStreamCode(ct);
PsiVariable builder = terminal.getBuilder();
terminal.preCleanUp();
terminal.preCleanUp(ct);
ControlFlowUtils.InitializerUsageStatus status = getInitializerUsageStatus(builder, loopStatement);
if(builder instanceof PsiLocalVariable) {
PsiElement result = replaceInitializer(loopStatement, builder, builder.getInitializer(), stream, status);
PsiElement result = replaceInitializer(loopStatement, builder, builder.getInitializer(), stream, status, ct);
terminal.cleanUp((PsiLocalVariable)builder);
JoiningTerminal.replaceUsages((PsiLocalVariable)terminal.getBuilder());
return result;
@@ -157,14 +157,14 @@ public class JoiningMigration extends BaseStreamApiMigration {
replaceUsages(target);
}
void preCleanUp() {
cleanUpCall(myBeforeLoopAppend);
cleanUpCall(myAfterLoopAppend);
void preCleanUp(CommentTracker ct) {
cleanUpCall(ct, myBeforeLoopAppend);
cleanUpCall(ct, myAfterLoopAppend);
}
@NotNull
String generateStreamCode() {
return myTerminalBlock.generate() + generateIntermediate() + generateTerminal();
String generateStreamCode(CommentTracker ct) {
return myTerminalBlock.generate(ct) + generateIntermediate(ct) + generateTerminal(ct);
}
private static void replaceInitializer(@NotNull PsiLocalVariable target) {
@@ -195,33 +195,33 @@ public class JoiningMigration extends BaseStreamApiMigration {
FinalUtils.canBeFinal(variable);
}
String generateTerminal() {
String generateTerminal(CommentTracker ct) {
final String collectArguments;
if (myDelimiterJoinParts.isEmpty() && myPrefixJoinParts.isEmpty() && mySuffixJoinParts.isEmpty()) {
collectArguments = "";
}
else {
String delimiter = myDelimiterJoinParts.isEmpty() ? "\"\"" : getExpressionText(myDelimiterJoinParts);
String delimiter = myDelimiterJoinParts.isEmpty() ? "\"\"" : getExpressionText(ct, myDelimiterJoinParts);
if (mySuffixJoinParts.isEmpty() && myPrefixJoinParts.isEmpty()) {
collectArguments = delimiter;
}
else {
String suffix = mySuffixJoinParts.isEmpty() ? "\"\"" : getExpressionText(mySuffixJoinParts);
String prefix = myPrefixJoinParts.isEmpty() ? "\"\"" : getExpressionText(myPrefixJoinParts);
String suffix = mySuffixJoinParts.isEmpty() ? "\"\"" : getExpressionText(ct, mySuffixJoinParts);
String prefix = myPrefixJoinParts.isEmpty() ? "\"\"" : getExpressionText(ct, myPrefixJoinParts);
collectArguments = delimiter + "," + prefix + "," + suffix;
}
}
return ".collect(" + CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + ".joining(" + collectArguments + "))";
}
String generateIntermediate() {
String generateIntermediate(CommentTracker ct) {
if (TypeUtils.isJavaLangString(myLoopVariable.getType()) &&
myMainJoinParts.size() == 1 &&
myMainJoinParts.get(0) instanceof PsiReferenceExpression) {
return "";
}
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(myLoopVariable.getProject());
String joinTransformation = getExpressionText(myMainJoinParts);
String joinTransformation = getExpressionText(ct, myMainJoinParts);
PsiExpression mapping = elementFactory.createExpressionFromText(joinTransformation, myLoopVariable);
return StreamRefactoringUtil.generateMapOperation(myLoopVariable, null, mapping);
}
@@ -240,23 +240,23 @@ public class JoiningMigration extends BaseStreamApiMigration {
}
}
private static void cleanUpCall(PsiMethodCallExpression call) {
private static void cleanUpCall(CommentTracker ct, PsiMethodCallExpression call) {
if (call != null) {
if (call.getParent() instanceof PsiExpressionStatement) {
call.delete();
ct.delete(call);
}
else {
PsiMethodCallExpression nextCall = ExpressionUtils.getCallForQualifier(call);
PsiExpression qualifier = call.getMethodExpression().getQualifierExpression();
if (nextCall != null && qualifier != null) {
nextCall.replace(qualifier);
ct.replace(nextCall, ct.markUnchanged(qualifier));
}
}
}
}
private static String getExpressionText(@NotNull List<PsiExpression> joinParts) {
private static String getExpressionText(CommentTracker ct, @NotNull List<PsiExpression> joinParts) {
StringJoiner joiner = new StringJoiner("+");
int size = joinParts.size();
for (int i = 0; i < joinParts.size(); i++) {
@@ -270,10 +270,10 @@ public class JoiningMigration extends BaseStreamApiMigration {
neighborIsString = true;
}
}
partText = expressionToCharSequence(joinPart, size, neighborIsString);
partText = expressionToCharSequence(ct, joinPart, size, neighborIsString);
}
else {
partText = expressionToCharSequence(joinPart, size, true);
partText = expressionToCharSequence(ct, joinPart, size, true);
}
joiner.add(partText);
}
@@ -310,7 +310,10 @@ public class JoiningMigration extends BaseStreamApiMigration {
}
@NotNull
private static String expressionToCharSequence(@NotNull PsiExpression expression, int expressionCount, boolean neighborIsString) {
private static String expressionToCharSequence(CommentTracker ct,
@NotNull PsiExpression expression,
int expressionCount,
boolean neighborIsString) {
PsiType type = expression.getType();
if(expression instanceof PsiMethodCallExpression) {
PsiMethodCallExpression callExpression = (PsiMethodCallExpression)expression;
@@ -324,7 +327,7 @@ public class JoiningMigration extends BaseStreamApiMigration {
Object constantExpression = ExpressionUtils.computeConstantExpression(first);
if(constantExpression instanceof Integer) {
String endIndex = String.valueOf((int)constantExpression + 1);
return qualifierExpression.getText() + ".substring(" + first.getText() + "," + endIndex + ")";
return ct.text(qualifierExpression) + ".substring(" + ct.text(first) + "," + endIndex + ")";
}
}
}
@@ -336,22 +339,22 @@ public class JoiningMigration extends BaseStreamApiMigration {
if (literalExpression != null) {
Object value = literalExpression.getValue();
if (value instanceof Character) {
String text = literalExpression.getText();
String text = ct.text(literalExpression);
if ("'\"'".equals(text)) return "\"\\\"\"";
return "\"" + text.substring(1, text.length() - 1) + "\"";
}
}
return CommonClassNames.JAVA_LANG_STRING + ".valueOf(" + expression.getText() + ")";
return CommonClassNames.JAVA_LANG_STRING + ".valueOf(" + ct.text(expression) + ")";
}
if (ParenthesesUtils.getPrecedence(expression) > ParenthesesUtils.ADDITIVE_PRECEDENCE ||
(expression.getType() instanceof PsiPrimitiveType &&
ParenthesesUtils.getPrecedence(expression) == ParenthesesUtils.ADDITIVE_PRECEDENCE) ||
expressionCount == 1) {
return "(" + expression.getText() + ")";
return "(" + ct.text(expression) + ")";
}
return expression.getText();
return ct.text(expression);
}
String expressionText = expression.getText();
String expressionText = ct.text(expression);
if(ParenthesesUtils.getPrecedence(expression) > ParenthesesUtils.ADDITIVE_PRECEDENCE && expressionCount > 1) {
expressionText = "(" + expressionText + ")";
}
@@ -860,9 +863,9 @@ List<PsiExpression> builderStrInitializers = null;
}
@Override
void preCleanUp() {
super.preCleanUp();
myBoolVariable.delete();
void preCleanUp(CommentTracker ct) {
super.preCleanUp(ct);
ct.delete(myBoolVariable);
}
@Nullable
@@ -919,9 +922,9 @@ List<PsiExpression> builderStrInitializers = null;
myTruncateIfStatement = truncateIfStatement;
}
void preCleanUp() {
super.preCleanUp();
myTruncateIfStatement.delete();
void preCleanUp(CommentTracker ct) {
super.preCleanUp(ct);
ct.delete(myTruncateIfStatement);
}
@Nullable
@@ -1032,9 +1035,9 @@ List<PsiExpression> builderStrInitializers = null;
myDelimiterVariable = delimiterVariable;
}
void preCleanUp() {
super.preCleanUp();
myDelimiterVariable.delete();
void preCleanUp(CommentTracker ct) {
super.preCleanUp(ct);
ct.delete(myDelimiterVariable);
}
@Nullable
@@ -1182,9 +1185,9 @@ List<PsiExpression> builderStrInitializers = null;
}
@Override
void preCleanUp() {
super.preCleanUp();
myBeforeLoopAppend.delete();
void preCleanUp(CommentTracker ct) {
super.preCleanUp(ct);
ct.delete(myBeforeLoopAppend);
}
private static List<PsiExpression> copyReplacingVar(@NotNull List<PsiExpression> joinParts,
@@ -1201,8 +1204,8 @@ List<PsiExpression> builderStrInitializers = null;
@NotNull
@Override
String generateStreamCode() {
return mySource.createReplacement() + generateIntermediate() + generateTerminal();
String generateStreamCode(CommentTracker ct) {
return mySource.createReplacement(ct) + generateIntermediate(ct) + generateTerminal(ct);
}
@Nullable
@@ -18,11 +18,8 @@ package com.intellij.codeInspection.streamMigration;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.siyeh.ig.psiutils.BoolUtils;
import com.siyeh.ig.psiutils.ControlFlowUtils;
import com.siyeh.ig.psiutils.*;
import com.siyeh.ig.psiutils.ControlFlowUtils.InitializerUsageStatus;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import org.jetbrains.annotations.NotNull;
/**
@@ -38,7 +35,7 @@ class MatchMigration extends BaseStreamApiMigration {
@Override
PsiElement migrate(@NotNull Project project, @NotNull PsiElement body, @NotNull TerminalBlock tb) {
PsiStatement sourceStatement = tb.getStreamSourceStatement();
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
CommentTracker ct = new CommentTracker();
if(tb.getSingleStatement() instanceof PsiReturnStatement) {
PsiReturnStatement returnStatement = (PsiReturnStatement)tb.getSingleStatement();
PsiExpression value = returnStatement.getReturnValue();
@@ -49,30 +46,29 @@ class MatchMigration extends BaseStreamApiMigration {
PsiExpression returnValue = nextReturnStatement.getReturnValue();
if(returnValue == null) return null;
String methodName = foundResult ? "anyMatch" : "noneMatch";
String streamText = addTerminalOperation(methodName, sourceStatement, tb);
restoreComments(sourceStatement, body);
String streamText = addTerminalOperation(ct, methodName, sourceStatement, tb);
if (nextReturnStatement.getParent() == sourceStatement.getParent()) {
if(!ExpressionUtils.isLiteral(returnValue, !foundResult)) {
streamText+= (foundResult ? "||" : "&&") + ParenthesesUtils.getText(returnValue, ParenthesesUtils.AND_PRECEDENCE);
streamText += (foundResult ? "||" : "&&") + ct.text(returnValue, ParenthesesUtils.BINARY_OR_PRECEDENCE);
}
removeLoop(sourceStatement);
return returnValue.replace(elementFactory.createExpressionFromText(streamText, nextReturnStatement));
removeLoop(ct, sourceStatement);
return new CommentTracker().replaceAndRestoreComments(returnValue, streamText);
}
PsiElement result = sourceStatement.replace(elementFactory.createStatementFromText("return " + streamText + ";", sourceStatement));
PsiElement result = ct.replaceAndRestoreComments(sourceStatement, "return " + streamText + ";");
if(!ControlFlowUtils.isReachable(nextReturnStatement)) {
nextReturnStatement.delete();
new CommentTracker().deleteAndRestoreComments(nextReturnStatement);
}
return result;
}
}
}
PsiStatement[] statements = tb.getStatements();
if (!(statements.length == 1 || (sourceStatement instanceof PsiLoopStatement && statements.length == 2 && ControlFlowUtils.statementBreaksLoop(statements[1],
(PsiLoopStatement)sourceStatement)))) {
if (!(statements.length == 1 ||
(sourceStatement instanceof PsiLoopStatement && statements.length == 2 &&
ControlFlowUtils.statementBreaksLoop(statements[1], (PsiLoopStatement)sourceStatement)))) {
return null;
}
restoreComments(sourceStatement, body);
String streamText = addTerminalOperation("anyMatch", sourceStatement, tb);
String streamText = addTerminalOperation(ct, "anyMatch", sourceStatement, tb);
PsiStatement statement = statements[0];
PsiAssignmentExpression assignment = ExpressionUtils.getAssignment(statement);
if(assignment != null) {
@@ -98,19 +94,22 @@ class MatchMigration extends BaseStreamApiMigration {
replacement = "!" + streamText;
}
else {
replacement = streamText + "?" + rValue.getText() + ":" + initializer.getText();
replacement = streamText + "?" + ct.text(rValue) + ":" + ct.text(initializer);
}
return replaceInitializer(sourceStatement, var, initializer, replacement, status);
return replaceInitializer(sourceStatement, var, initializer, replacement, status, ct);
}
}
}
}
String replacement = "if(" + streamText + "){" + statement.getText() + "}";
return sourceStatement.replace(elementFactory.createStatementFromText(replacement, sourceStatement));
String replacement = "if(" + streamText + "){" + ct.text(statement) + "}";
return ct.replaceAndRestoreComments(sourceStatement, replacement);
}
private static String addTerminalOperation(String methodName, @NotNull PsiElement contextElement, @NotNull TerminalBlock tb) {
String origStream = tb.generate();
private static String addTerminalOperation(CommentTracker ct,
String methodName,
@NotNull PsiElement contextElement,
@NotNull TerminalBlock tb) {
String origStream = tb.generate(ct);
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(contextElement.getProject());
PsiExpression stream = elementFactory.createExpressionFromText(origStream, contextElement);
LOG.assertTrue(stream instanceof PsiMethodCallExpression);
@@ -20,6 +20,7 @@ import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.TypeConversionUtil;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import org.jetbrains.annotations.NotNull;
@@ -46,6 +47,7 @@ public class OperationReductionMigration extends BaseStreamApiMigration {
PsiVariable var = StreamApiMigrationInspection.extractAccumulator(assignment, myReductionOperation.getCompoundAssignmentOp());
if (var == null) return null;
CommentTracker ct = new CommentTracker();
PsiExpression operand = StreamApiMigrationInspection.extractOperand(assignment, myReductionOperation.getCompoundAssignmentOp());
if (operand == null) return null;
PsiType type = var.getType();
@@ -53,7 +55,7 @@ public class OperationReductionMigration extends BaseStreamApiMigration {
PsiType operandType = operand.getType();
if (operandType != null && !TypeConversionUtil.isAssignable(type, operandType)) {
operand = JavaPsiFacade.getElementFactory(project).createExpressionFromText(
"(" + type.getCanonicalText() + ")" + ParenthesesUtils.getText(operand, ParenthesesUtils.MULTIPLICATIVE_PRECEDENCE), operand);
"(" + type.getCanonicalText() + ")" + ct.text(operand, ParenthesesUtils.TYPE_CAST_PRECEDENCE), operand);
}
JavaCodeStyleManager javaStyle = JavaCodeStyleManager.getInstance(project);
String leftOperand = javaStyle.suggestUniqueVariableName("a", body, true);
@@ -65,13 +67,13 @@ public class OperationReductionMigration extends BaseStreamApiMigration {
PsiExpression initializer = var.getInitializer();
String identity = initializer != null && myReductionOperation.getInitializerExpressionRestriction().test(initializer)
? initializer.getText()
? ct.text(initializer)
: myReductionOperation.getIdentity();
String stream = tb.add(new StreamApiMigrationInspection.MapOp(operand, tb.getVariable(), type)).generate()
String stream = tb.add(new StreamApiMigrationInspection.MapOp(operand, tb.getVariable(), type)).generate(ct)
+ String.format(Locale.ENGLISH, ".reduce(%s, (%s, %s) -> %s %s %s)",
identity, leftOperand, rightOperand, leftOperand,
myReductionOperation.getOperation(), rightOperand);
return replaceWithOperation(tb.getStreamSourceStatement(), var, stream, type, myReductionOperation);
return replaceWithOperation(tb.getStreamSourceStatement(), var, stream, type, myReductionOperation, ct);
}
static class ReductionOperation {
@@ -19,6 +19,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.ig.callMatcher.CallMatcher;
import com.siyeh.ig.psiutils.CommentTracker;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -137,7 +138,7 @@ public class SimplifyForEachInspection extends AbstractBaseJavaLocalInspectionTo
}
@Override
String createReplacement() {
String createReplacement(CommentTracker ct) {
return myExpression.getText() + (myIsCollectionForEach? ".stream()" : "");
}
@@ -665,7 +665,7 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
return StreamEx.ofNullable(myExpression);
}
abstract String createReplacement();
abstract String createReplacement(CommentTracker ct);
boolean isWriteAllowed(PsiVariable variable, PsiExpression reference) {
return false;
@@ -689,11 +689,11 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
}
@Override
public String createReplacement() {
public String createReplacement(CommentTracker ct) {
PsiElementFactory factory = JavaPsiFacade.getElementFactory(myExpression.getProject());
PsiExpression intermediate = makeIntermediateExpression(factory);
PsiExpression intermediate = makeIntermediateExpression(ct, factory);
PsiExpression expression =
myNegated ? factory.createExpressionFromText(BoolUtils.getNegatedExpressionText(intermediate), myExpression) : intermediate;
myNegated ? factory.createExpressionFromText(BoolUtils.getNegatedExpressionText(intermediate, ct), myExpression) : intermediate;
return "." + getOpName() + "(" + LambdaUtil.createLambda(myVariable, expression) + ")";
}
@@ -702,8 +702,8 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
return "filter";
}
PsiExpression makeIntermediateExpression(PsiElementFactory factory) {
return myExpression;
PsiExpression makeIntermediateExpression(CommentTracker ct, PsiElementFactory factory) {
return ct.markUnchanged(myExpression);
}
}
@@ -731,9 +731,9 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
@Override
PsiExpression makeIntermediateExpression(PsiElementFactory factory) {
return factory.createExpressionFromText(mySource.createReplacement() + ".anyMatch(" +
LambdaUtil.createLambda(myMatchVariable, myExpression) + ")", myExpression);
PsiExpression makeIntermediateExpression(CommentTracker ct, PsiElementFactory factory) {
return factory.createExpressionFromText(mySource.createReplacement(ct) + ".anyMatch(" +
ct.lambdaText(myMatchVariable, myExpression) + ")", myExpression);
}
@Override
@@ -756,8 +756,8 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
}
@Override
public String createReplacement() {
return StreamRefactoringUtil.generateMapOperation(myVariable, myType, myExpression);
public String createReplacement(CommentTracker ct) {
return StreamRefactoringUtil.generateMapOperation(myVariable, myType, ct.markUnchanged(myExpression));
}
@Override
@@ -775,11 +775,11 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
}
@Override
public String createReplacement() {
public String createReplacement(CommentTracker ct) {
String operation = "flatMap";
PsiType inType = myVariable.getType();
PsiType outType = mySource.getVariable().getType();
String lambda = myVariable.getName() + " -> " + getStreamExpression();
String lambda = myVariable.getName() + " -> " + getStreamExpression(ct);
if (outType instanceof PsiPrimitiveType && !outType.equals(inType)) {
if (outType.equals(PsiType.INT)) {
operation = "flatMapToInt";
@@ -802,8 +802,8 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
}
@NotNull
String getStreamExpression() {
return mySource.createReplacement();
String getStreamExpression(CommentTracker ct) {
return mySource.createReplacement(ct);
}
@Override
@@ -839,8 +839,8 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
}
@Override
String createReplacement() {
return ".limit(" + getLimitExpression() + ")";
String createReplacement(CommentTracker ct) {
return ".limit(" + getLimitExpression(ct) + ")";
}
PsiLocalVariable getCounterVariable() {
@@ -863,9 +863,9 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
return variable == myCounterVariable && PsiTreeUtil.isAncestor(myCounter, reference, false);
}
private String getLimitExpression() {
private String getLimitExpression(CommentTracker ct) {
if (myDelta == 0) {
return myExpression.getText();
return ct.text(myExpression);
}
if (myExpression instanceof PsiLiteralExpression) {
Object value = ((PsiLiteralExpression)myExpression).getValue();
@@ -873,7 +873,7 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
return String.valueOf(((Number)value).longValue() + myDelta);
}
}
return ParenthesesUtils.getText(myExpression, ParenthesesUtils.ADDITIVE_PRECEDENCE) + "+" + myDelta;
return ct.text(myExpression, ParenthesesUtils.ADDITIVE_PRECEDENCE) + "+" + myDelta;
}
}
@@ -883,7 +883,7 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
}
@Override
String createReplacement() {
String createReplacement(CommentTracker ct) {
return ".distinct()";
}
}
@@ -930,8 +930,8 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
}
@Override
String createReplacement() {
return myExpression.getText() + ".lines()";
String createReplacement(CommentTracker ct) {
return ct.text(myExpression) + ".lines()";
}
@Override
@@ -1068,13 +1068,13 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
}
@Override
String createReplacement() {
String createReplacement(CommentTracker ct) {
if (myExpression instanceof PsiNewExpression) {
PsiArrayInitializerExpression initializer = ((PsiNewExpression)myExpression).getArrayInitializer();
if (initializer != null) {
PsiElement[] children = initializer.getChildren();
if (children.length > 2) {
String initializerText = StreamEx.of(children, 1, children.length - 1).map(PsiElement::getText).joining();
String initializerText = StreamEx.of(children, 1, children.length - 1).map(ct::text).joining();
PsiType type = myExpression.getType();
if (type instanceof PsiArrayType) {
PsiType componentType = ((PsiArrayType)type).getComponentType();
@@ -1094,7 +1094,7 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
}
}
}
return CommonClassNames.JAVA_UTIL_ARRAYS + ".stream(" + myExpression.getText() + ")";
return CommonClassNames.JAVA_UTIL_ARRAYS + ".stream(" + ct.text(myExpression) + ")";
}
@Nullable
@@ -1120,8 +1120,8 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
}
@Override
String createReplacement() {
return ParenthesesUtils.getText(myExpression, ParenthesesUtils.POSTFIX_PRECEDENCE) + ".stream()" + tryUnbox(myVariable);
String createReplacement(CommentTracker ct) {
return ct.text(myExpression, ParenthesesUtils.METHOD_CALL_PRECEDENCE) + ".stream()" + tryUnbox(myVariable);
}
@Contract("null, _ -> false")
@@ -1170,10 +1170,10 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
}
@Override
public String createReplacement() {
public String createReplacement(CommentTracker ct) {
String className = myVariable.getType().equals(PsiType.LONG) ? "java.util.stream.LongStream" : "java.util.stream.IntStream";
String methodName = myIncluding ? "rangeClosed" : "range";
return className + "." + methodName + "(" + myExpression.getText() + ", " + myBound.getText() + ")";
return className + "." + methodName + "(" + ct.text(myExpression) + ", " + ct.text(myBound) + ")";
}
CountingLoopSource withBound(PsiExpression bound) {
@@ -1277,12 +1277,12 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
}
@Override
String createReplacement() {
String createReplacement(CommentTracker ct) {
String lambda;
if (myOpType != null) {
PsiElementFactory factory = JavaPsiFacade.getElementFactory(myVariable.getProject());
PsiExpression expression = myUnaryExpression == null ? myExpression : factory.createExpressionFromText("1", null);
String expressionText = ParenthesesUtils.getText(expression, ParenthesesUtils.getPrecedenceForOperator(myOpType));
String expressionText = ParenthesesUtils.getText(ct.markUnchanged(expression), ParenthesesUtils.getPrecedenceForOperator(myOpType));
String lambdaBody = myVariable.getName() + getOperationSign(myOpType) + expressionText;
if (!myVariable.getType().equals(expression.getType())) {
lambdaBody = ("(" + myVariable.getType().getCanonicalText() + ")") + "(" + lambdaBody + ")";
@@ -1290,11 +1290,11 @@ public class StreamApiMigrationInspection extends AbstractBaseJavaLocalInspectio
lambda = myVariable.getName() + "->" + lambdaBody;
}
else {
lambda = LambdaUtil.createLambda(myVariable, myExpression);
lambda = ct.lambdaText(myVariable, myExpression);
}
String maybeCondition = myCondition != null ? LambdaUtil.createLambda(myVariable, myCondition) + "," : "";
String maybeCondition = myCondition != null ? ct.lambdaText(myVariable, myCondition) + "," : "";
return getStreamClass(myVariable.getType()) + ".iterate(" + myInitializer.getText() + "," + maybeCondition + lambda + ")";
return getStreamClass(myVariable.getType()) + ".iterate(" + ct.text(myInitializer) + "," + maybeCondition + lambda + ")";
}
@Contract(value = "null -> null", pure = true)
@@ -19,6 +19,7 @@ import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.util.TypeConversionUtil;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import org.jetbrains.annotations.NotNull;
@@ -46,11 +47,12 @@ class SumMigration extends BaseStreamApiMigration {
type = PsiType.INT;
}
PsiType addendType = addend.getType();
CommentTracker ct = new CommentTracker();
if(addendType != null && !TypeConversionUtil.isAssignable(type, addendType)) {
addend = JavaPsiFacade.getElementFactory(project).createExpressionFromText(
"(" + type.getCanonicalText() + ")" + ParenthesesUtils.getText(addend, ParenthesesUtils.MULTIPLICATIVE_PRECEDENCE), addend);
"(" + type.getCanonicalText() + ")" + ct.text(addend, ParenthesesUtils.TYPE_CAST_PRECEDENCE), addend);
}
String stream = tb.add(new MapOp(addend, tb.getVariable(), type)).generate()+".sum()";
return replaceWithOperation(tb.getStreamSourceStatement(), var, stream, type, SUM_OPERATION);
String stream = tb.add(new MapOp(addend, tb.getVariable(), type)).generate(ct)+".sum()";
return replaceWithOperation(tb.getStreamSourceStatement(), var, stream, type, SUM_OPERATION, ct);
}
}
@@ -29,7 +29,10 @@ import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.NoSuchElementException;
import java.util.Objects;
import static com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.*;
import static com.intellij.util.ObjectUtils.tryCast;
@@ -460,16 +463,17 @@ class TerminalBlock {
/**
* Converts this TerminalBlock to PsiElement (either PsiStatement or PsiCodeBlock)
*
* @param ct CommentTracker to mark statements as unchanged
* @param factory factory to use to create new element if necessary
* @return the PsiElement
*/
PsiElement convertToElement(PsiElementFactory factory) {
PsiElement convertToElement(CommentTracker ct, PsiElementFactory factory) {
if (myStatements.length == 1) {
return myStatements[0];
return ct.markUnchanged(myStatements[0]);
}
PsiCodeBlock block = factory.createCodeBlock();
for (PsiStatement statement : myStatements) {
block.add(statement);
block.add(ct.markUnchanged(statement));
}
return block;
}
@@ -492,15 +496,15 @@ class TerminalBlock {
}
}
String generate() {
return generate(false);
String generate(CommentTracker ct) {
return generate(ct, false);
}
String generate(boolean noStreamForEmpty) {
String generate(CommentTracker ct, boolean noStreamForEmpty) {
if(noStreamForEmpty && myOperations.length == 1 && myOperations[0] instanceof CollectionStream) {
return ParenthesesUtils.getText(myOperations[0].getExpression(), ParenthesesUtils.POSTFIX_PRECEDENCE);
}
return StreamEx.of(myOperations).map(Operation::createReplacement).joining();
return StreamEx.of(myOperations).map(operation -> operation.createReplacement(ct)).joining();
}
@NotNull
@@ -20,6 +20,7 @@ import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.util.ArrayUtil;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.ControlFlowUtils;
import com.siyeh.ig.psiutils.ControlFlowUtils.InitializerUsageStatus;
import org.jetbrains.annotations.NotNull;
@@ -59,8 +60,9 @@ public class ToArrayMigration extends BaseStreamApiMigration {
} else {
supplier = arrayType.getCanonicalText()+"::new";
}
CommentTracker ct = new CommentTracker();
MapOp mapping = new MapOp(rValue, tb.getVariable(), assignment.getType());
String replacementText = loop.withBound(dimension).createReplacement() + mapping.createReplacement() + ".toArray(" + supplier + ")";
return replaceInitializer(tb.getStreamSourceStatement(), arrayVariable, initializer, replacementText, status);
String replacementText = loop.withBound(dimension).createReplacement(ct) + mapping.createReplacement(ct) + ".toArray(" + supplier + ")";
return replaceInitializer(tb.getStreamSourceStatement(), arrayVariable, initializer, replacementText, status, ct);
}
}
@@ -4,6 +4,12 @@ import java.util.stream.Collectors;
public class Main {
public void test(List<Set<String>> nested) {
List<String> result = nested.stream().filter(Objects::nonNull).flatMap(Collection::stream).filter(str -> str.startsWith("xyz")).map(String::trim).collect(Collectors.toList());
/*non-equal*//*empty*/
List<String> result = nested.stream().filter(Objects::nonNull).flatMap(Collection::stream).filter(str -> str./*startswith*/startsWith("xyz")).map(String::trim).collect(Collectors.toList());
// 1
/*target is here*/
// 2
// 3
// 4
}
}
@@ -6,15 +6,15 @@ import java.util.Set;
public class Main {
public void test(List<Set<String>> nested) {
List<String> result = new ArrayList<>();
for (Set<String> element : nes<caret>ted) {
if (element != null) {
for (Set<String> element : nes<caret>ted) { // 1
if (element /*non-equal*/!= null) {
for (String str : element) {
if (str.startsWith("xyz")) {
String target = str.trim();
result.add(target);
}
}
}
if (str./*startswith*/startsWith("xyz")) {
String target = str.trim(/*empty*/);
result.add(/*target is here*/target);
} // 2
} // 3
} // 4
}
}
}
@@ -6,8 +6,8 @@ import java.util.List;
public class Main {
public static String find(List<List<String>> list) {
/*
Block comment
*/
Block comment
*/
return list.stream().flatMap(Collection::stream).filter(string -> string.startsWith("ABC")).findFirst().map(string -> string.substring(3)).orElse("");
}
}
@@ -6,8 +6,8 @@ import java.util.List;
public class Main {
public static boolean find(List<List<String>> list) {
/*
Block comment
*/
Block comment
*/
return list.stream().flatMap(Collection::stream).filter(string -> string.startsWith("ABC")).findFirst().filter(string -> string.substring(3).equals("xyz")).isPresent();
}
}
@@ -8,6 +8,7 @@ import java.util.Optional;
public class Main {
private static String test(List<String> list) {
Optional<String> found = list.stream().filter(Objects::nonNull).findFirst();
// optional!
return found.orElse(null);
}
@@ -4,6 +4,7 @@ import java.util.List;
public class Main {
public int testPrimitiveMap(List<String> data) {
return data.stream().filter(str -> str.startsWith("xyz")).mapToInt(String::length).filter(len -> len > 10).findFirst().orElse(0);
/*ten*/
return data.stream().filter(str -> str.startsWith("xyz")).mapToInt(String::length).filter(len -> len /*bigger*/ > 10).findFirst().orElse(0);
}
}
@@ -5,10 +5,10 @@ import java.util.Map;
public class Main {
public void testMap(Map<String, List<String>> map) throws Exception {
int bigSize = 0;
int bigSize = 0; // initial
for(List<String> list : map.valu<caret>es()) {
int size = list.size();
if(size > 10) {
int size = list.size(); // size
if(size/*bigger*/ > 10) {
bigSize = size*2;
break;
}
@@ -10,7 +10,7 @@ public class Main {
Optional<String> found = Optional.empty();
for (String s : li<caret>st) {
if (Objects.nonNull(s)) {
found = Optional.of(s);
found = Optional.of(s); // optional!
break;
}
}
@@ -7,7 +7,7 @@ public class Main {
for(String str : dat<caret>a) {
if(str.startsWith("xyz")) {
int len = str.length();
if(len > 10) {
if(len /*bigger*/> 10 /*ten*/) {
return len;
}
}
@@ -6,6 +6,9 @@ import java.util.stream.Collectors;
public class Test {
private static String work2(List<String> strs) {
String sb = strs.stream().collect(Collectors.joining(",", "{", "}"));
// before
// after
// inside
return sb;
}
}
@@ -5,14 +5,14 @@ import java.util.List;
public class Test {
private static String work2(List<String> strs) {
StringBuilder sb = new StringBuilder();
sb.append("{");
sb.append("{"); // before
String separator = "";
for <caret> (String str : strs) {
sb.append(separator);
sb.append(separator); // inside
sb.append(str);
separator = ",";
}
sb.append("}");
sb.append("}"); // after
return sb.toString();
}
}
@@ -27,7 +27,9 @@ public class Main {
new Person("James", 25),
new Person("Kelly", 12)
);
Person maxPerson = personList.stream().filter(p -> p.getAge() > 13).max(Comparator.comparingInt(Person::getAge)).orElse(null);
/*age*/
Person maxPerson = personList.stream().filter(p -> p.getAge() > /*thirteen*/ 13).max(Comparator.comparingInt(Person::getAge)).orElse(null);
// max!
return maxPerson;
}
@@ -29,9 +29,9 @@ public class Main {
);
Person maxPerson = null;
for <caret>(Person p : personList) {
if(p.getAge() > 13) {
if (maxPerson == null || p.getAge() > maxPerson.getAge()) {
maxPerson = p;
if(p.getAge() > /*thirteen*/ 13) {
if (maxPerson == null || p./*age*/getAge() > maxPerson.getAge()) {
maxPerson = p; // max!
}
}
}
@@ -51,6 +51,35 @@ public class CommentTracker {
return element.getText();
}
/**
* Marks the expression as unchanged and returns its text, adding parentheses if necessary.
* The unchanged elements are assumed to be preserved in the resulting code as is,
* so the comments from them will not be extracted.
*
* @param element expression to return the text
* @param precedence precedence of surrounding operation
* @return a text to be inserted into refactored code
* @see ParenthesesUtils#getText(PsiExpression, int)
*/
@NotNull
public String text(@NotNull PsiExpression element, int precedence) {
checkState();
addIgnored(element);
return ParenthesesUtils.getText(element, precedence + 1);
}
/**
* Marks the expression as unchanged and returns a single-parameter lambda text which parameter
* is the name of supplied variable and body is the supplied expression
*
* @param variable a variable to use as lambda parameter
* @param expression an expression to use as lambda body
* @return a string representation of lambda
*/
public String lambdaText(@NotNull PsiVariable variable, @NotNull PsiExpression expression) {
return variable.getName() + " -> " + text(expression);
}
/**
* Marks the element as unchanged and returns it. The unchanged elements are assumed to be preserved
* in the resulting code as is, so the comments from them will not be extracted.
@@ -71,6 +100,9 @@ public class CommentTracker {
* @param element element to delete
*/
public void delete(@NotNull PsiElement element) {
if (element instanceof PsiExpression && element.getParent() instanceof PsiExpressionStatement) {
element = element.getParent();
}
grabComments(element);
element.delete();
}