mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-160707 Stream API migration: support object arrays
This commit is contained in:
+100
-69
@@ -101,51 +101,62 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
if (iteratedValue != null && body != null) {
|
||||
final PsiType iteratedValueType = iteratedValue.getType();
|
||||
final PsiClass iteratorClass = PsiUtil.resolveClassInClassTypeOnly(iteratedValueType);
|
||||
final PsiClass collectionClass = JavaPsiFacade.getInstance(body.getProject()).findClass(CommonClassNames.JAVA_UTIL_COLLECTION, statement.getResolveScope());
|
||||
if (collectionClass != null && InheritanceUtil.isInheritorOrSelf(iteratorClass, collectionClass, true)) {
|
||||
try {
|
||||
final ControlFlow controlFlow = ControlFlowFactory.getInstance(holder.getProject())
|
||||
.getControlFlow(body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance());
|
||||
int startOffset = controlFlow.getStartOffset(body);
|
||||
int endOffset = controlFlow.getEndOffset(body);
|
||||
final Collection<PsiStatement> exitPoints = ControlFlowUtil
|
||||
.findExitPointsAndStatements(controlFlow, startOffset, endOffset, new IntArrayList(), PsiContinueStatement.class,
|
||||
PsiBreakStatement.class, PsiReturnStatement.class, PsiThrowStatement.class);
|
||||
if (exitPoints.isEmpty()) {
|
||||
|
||||
final List<PsiVariable> usedVariables = ControlFlowUtil.getUsedVariables(controlFlow, startOffset, endOffset);
|
||||
for (PsiVariable variable : usedVariables) {
|
||||
if (!HighlightControlFlowUtil.isEffectivelyFinal(variable, body, null)) {
|
||||
return;
|
||||
}
|
||||
PsiClass collectionClass = null;
|
||||
final boolean isArray;
|
||||
if(iteratedValueType instanceof PsiArrayType) {
|
||||
// Do not handle primitive types now
|
||||
if(((PsiArrayType)iteratedValueType).getComponentType() instanceof PsiPrimitiveType) return;
|
||||
isArray = true;
|
||||
} else {
|
||||
collectionClass = JavaPsiFacade.getInstance(body.getProject()).findClass(CommonClassNames.JAVA_UTIL_COLLECTION, statement.getResolveScope());
|
||||
if (collectionClass != null && InheritanceUtil.isInheritorOrSelf(iteratorClass, collectionClass, true)) {
|
||||
isArray = false;
|
||||
} else return;
|
||||
}
|
||||
try {
|
||||
final ControlFlow controlFlow = ControlFlowFactory.getInstance(holder.getProject())
|
||||
.getControlFlow(body, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance());
|
||||
int startOffset = controlFlow.getStartOffset(body);
|
||||
int endOffset = controlFlow.getEndOffset(body);
|
||||
final Collection<PsiStatement> exitPoints = ControlFlowUtil
|
||||
.findExitPointsAndStatements(controlFlow, startOffset, endOffset, new IntArrayList(), PsiContinueStatement.class,
|
||||
PsiBreakStatement.class, PsiReturnStatement.class, PsiThrowStatement.class);
|
||||
if (exitPoints.isEmpty()) {
|
||||
|
||||
final List<PsiVariable> usedVariables = ControlFlowUtil.getUsedVariables(controlFlow, startOffset, endOffset);
|
||||
for (PsiVariable variable : usedVariables) {
|
||||
if (!HighlightControlFlowUtil.isEffectivelyFinal(variable, body, null)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (ExceptionUtil.getThrownCheckedExceptions(new PsiElement[] {body}).isEmpty()) {
|
||||
TerminalBlock tb = TerminalBlock.from(statement.getIterationParameter(), body);
|
||||
List<Operation> operations = tb.extractOperations();
|
||||
if (ExceptionUtil.getThrownCheckedExceptions(new PsiElement[]{body}).isEmpty()) {
|
||||
TerminalBlock tb = TerminalBlock.from(statement.getIterationParameter(), body);
|
||||
List<Operation> operations = tb.extractOperations();
|
||||
|
||||
if (!isRawSubstitution(iteratedValueType, collectionClass) && isCollectCall(tb, operations)) {
|
||||
boolean addAll = operations.isEmpty() && isAddAllCall(tb);
|
||||
holder.registerProblem(iteratedValue, "Can be replaced with " + (addAll ? "addAll call" : "collect call"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
addAll ? new ReplaceWithAddAllFix() : new ReplaceWithCollectFix());
|
||||
}
|
||||
else if (REPLACE_TRIVIAL_FOREACH || !operations.isEmpty() || !isTrivial(body, statement.getIterationParameter())) {
|
||||
final List<LocalQuickFix> fixes = new ArrayList<>();
|
||||
fixes.add(new ReplaceWithForeachCallFix("forEach"));
|
||||
if (!operations.isEmpty()) {
|
||||
//for .stream()
|
||||
fixes.add(new ReplaceWithForeachCallFix("forEachOrdered"));
|
||||
}
|
||||
holder.registerProblem(iteratedValue, "Can be replaced with foreach call",
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
fixes.toArray(new LocalQuickFix[fixes.size()]));
|
||||
if ((isArray || !isRawSubstitution(iteratedValueType, collectionClass)) && isCollectCall(tb, operations)) {
|
||||
boolean addAll = operations.isEmpty() && isAddAllCall(tb);
|
||||
holder.registerProblem(iteratedValue, "Can be replaced with " + (addAll ? "addAll call" : "collect call"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
addAll ? new ReplaceWithAddAllFix() : new ReplaceWithCollectFix());
|
||||
}
|
||||
// do not replace for(T e : arr) {} with Arrays.stream(arr).forEach(e -> {}) even if flag is set
|
||||
else if (!operations.isEmpty() ||
|
||||
(!isArray && (REPLACE_TRIVIAL_FOREACH || !isTrivial(body, statement.getIterationParameter())))) {
|
||||
final List<LocalQuickFix> fixes = new ArrayList<>();
|
||||
fixes.add(new ReplaceWithForeachCallFix("forEach"));
|
||||
if (!operations.isEmpty()) {
|
||||
//for .stream()
|
||||
fixes.add(new ReplaceWithForeachCallFix("forEachOrdered"));
|
||||
}
|
||||
holder.registerProblem(iteratedValue, "Can be replaced with foreach call",
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
fixes.toArray(new LocalQuickFix[fixes.size()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (AnalysisCanceledException ignored) {
|
||||
}
|
||||
}
|
||||
catch (AnalysisCanceledException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,8 +284,8 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
return ContainerUtil.find(method.getThrowsList().getReferencedTypes(), type -> !ExceptionUtil.isUncheckedException(type)) != null;
|
||||
}
|
||||
|
||||
private static boolean isIdentityMapping(PsiVariable parameter, PsiExpression mapperCall) {
|
||||
return mapperCall instanceof PsiReferenceExpression && ((PsiReferenceExpression)mapperCall).resolve() == parameter;
|
||||
private static boolean isIdentityMapping(PsiVariable variable, PsiExpression mapperCall) {
|
||||
return mapperCall instanceof PsiReferenceExpression && ((PsiReferenceExpression)mapperCall).resolve() == variable;
|
||||
}
|
||||
|
||||
private static class ReplaceWithForeachCallFix implements LocalQuickFix {
|
||||
@@ -311,11 +322,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
TerminalBlock tb = TerminalBlock.from(parameter, body);
|
||||
List<String> intermediateOps = tb.extractOperationReplacements(elementFactory);
|
||||
|
||||
StringBuilder buffer = new StringBuilder(getIteratedValueText(iteratedValue));
|
||||
if (!intermediateOps.isEmpty()) {
|
||||
buffer.append(".stream()");
|
||||
intermediateOps.forEach(buffer::append);
|
||||
}
|
||||
StringBuilder buffer = generateStream(iteratedValue, intermediateOps);
|
||||
PsiElement block = tb.convertToElement(elementFactory);
|
||||
|
||||
buffer.append(".").append(myForEachMethodName).append("(");
|
||||
@@ -342,13 +349,13 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
}
|
||||
}
|
||||
|
||||
private static String createForEachFunctionalExpressionText(Project project, PsiElement block, PsiVariable parameter) {
|
||||
private static String createForEachFunctionalExpressionText(Project project, PsiElement block, PsiVariable variable) {
|
||||
final PsiCallExpression callExpression = LambdaCanBeMethodReferenceInspection.extractMethodCallFromBlock(block);
|
||||
if (callExpression != null) {
|
||||
final PsiClassType functionalType = createDefaultConsumerType(project, parameter);
|
||||
final PsiClassType functionalType = createDefaultConsumerType(project, variable);
|
||||
String methodReferenceText = null;
|
||||
if(parameter instanceof PsiParameter) {
|
||||
final PsiParameter[] parameters = {(PsiParameter)parameter};
|
||||
if(variable instanceof PsiParameter) {
|
||||
final PsiParameter[] parameters = {(PsiParameter)variable};
|
||||
methodReferenceText =
|
||||
LambdaCanBeMethodReferenceInspection.convertToMethodReference(block, parameters, functionalType, null);
|
||||
}
|
||||
@@ -356,7 +363,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
return methodReferenceText;
|
||||
}
|
||||
}
|
||||
return parameter.getName() + " -> " + wrapInBlock(block);
|
||||
return variable.getName() + " -> " + wrapInBlock(block);
|
||||
}
|
||||
|
||||
private static String wrapInBlock(PsiElement block) {
|
||||
@@ -414,6 +421,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
PsiStatement body = foreachStatement.getBody();
|
||||
final PsiExpression iteratedValue = foreachStatement.getIteratedValue();
|
||||
if (body != null && iteratedValue != null) {
|
||||
final PsiType iteratedValueType = iteratedValue.getType();
|
||||
final PsiParameter parameter = foreachStatement.getIterationParameter();
|
||||
TerminalBlock tb = TerminalBlock.from(parameter, body);
|
||||
List<String> intermediateOps = tb.extractOperationReplacements(elementFactory);
|
||||
@@ -425,15 +433,15 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
restoreComments(foreachStatement, body);
|
||||
final PsiExpression qualifierExpression = methodCallExpression.getMethodExpression().getQualifierExpression();
|
||||
final String qualifierText = qualifierExpression != null ? qualifierExpression.getText() : "";
|
||||
final String callText = StringUtil.getQualifiedName(qualifierText, "addAll(" + getIteratedValueText(iteratedValue) + ");");
|
||||
final String collectionText = iteratedValueType instanceof PsiArrayType ? "java.util.Arrays.asList("+iteratedValue.getText()+")" :
|
||||
getIteratedValueText(iteratedValue);
|
||||
final String callText = StringUtil.getQualifiedName(qualifierText, "addAll(" + collectionText + ");");
|
||||
PsiElement result = foreachStatement.replace(elementFactory.createStatementFromText(callText, foreachStatement));
|
||||
reformatWhenNeeded(project, result);
|
||||
return;
|
||||
}
|
||||
final StringBuilder builder = new StringBuilder(getIteratedValueText(iteratedValue) + ".stream()");
|
||||
|
||||
intermediateOps.forEach(builder::append);
|
||||
builder.append(createMapperFunctionalExpressionText(tb.getVariable(), methodCallExpression.getArgumentList().getExpressions()[0]));
|
||||
intermediateOps.add(createMapperFunctionalExpressionText(tb.getVariable(), methodCallExpression.getArgumentList().getExpressions()[0]));
|
||||
final StringBuilder builder = generateStream(iteratedValue, intermediateOps);
|
||||
|
||||
builder.append(".collect(java.util.stream.Collectors.");
|
||||
PsiElement result = null;
|
||||
@@ -650,6 +658,9 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
final PsiType iteratedValueType = iteratedValue.getType();
|
||||
Operation op = null;
|
||||
if(iteratedValueType instanceof PsiArrayType) {
|
||||
// do not handle flatMapToPrimitive
|
||||
if (((PsiArrayType)iteratedValueType).getComponentType() instanceof PsiPrimitiveType)
|
||||
return null;
|
||||
op = new ArrayFlatMapOp(iteratedValue, myVariable);
|
||||
} else {
|
||||
final PsiClass iteratorClass = PsiUtil.resolveClassInClassTypeOnly(iteratedValueType);
|
||||
@@ -680,15 +691,18 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
PsiElement element = elements[0];
|
||||
if(element instanceof PsiLocalVariable) {
|
||||
PsiLocalVariable declaredVar = (PsiLocalVariable)element;
|
||||
PsiExpression initializer = declaredVar.getInitializer();
|
||||
if (initializer != null &&
|
||||
ReferencesSearch.search(myVariable, new LocalSearchScope(Arrays.copyOfRange(myStatements, myFrom + 1, myTo)))
|
||||
.findFirst() == null) {
|
||||
MapOp op = new MapOp(initializer, myVariable);
|
||||
myVariable = declaredVar;
|
||||
myFrom++;
|
||||
flatten();
|
||||
return op;
|
||||
// do not handle mapToPrimitive
|
||||
if(!(declaredVar.getType() instanceof PsiPrimitiveType)) {
|
||||
PsiExpression initializer = declaredVar.getInitializer();
|
||||
if (initializer != null &&
|
||||
ReferencesSearch.search(myVariable, new LocalSearchScope(Arrays.copyOfRange(myStatements, myFrom + 1, myTo)))
|
||||
.findFirst() == null) {
|
||||
MapOp op = new MapOp(initializer, myVariable);
|
||||
myVariable = declaredVar;
|
||||
myFrom++;
|
||||
flatten();
|
||||
return op;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -754,12 +768,12 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
}
|
||||
}
|
||||
|
||||
private static String compoundLambdaOrMethodReference(PsiVariable parameter,
|
||||
private static String compoundLambdaOrMethodReference(PsiVariable variable,
|
||||
PsiExpression expression,
|
||||
String samQualifiedName,
|
||||
PsiType[] samParamTypes) {
|
||||
String result = "";
|
||||
final Project project = parameter.getProject();
|
||||
final Project project = variable.getProject();
|
||||
final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project);
|
||||
final PsiClass functionClass = psiFacade.findClass(samQualifiedName, GlobalSearchScope.allScope(project));
|
||||
for (int i = 0; i < samParamTypes.length; i++) {
|
||||
@@ -769,15 +783,15 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
}
|
||||
final PsiClassType functionalInterfaceType = functionClass != null ? psiFacade.getElementFactory().createType(functionClass, samParamTypes) : null;
|
||||
String methodReferenceText = null;
|
||||
if(parameter instanceof PsiParameter) {
|
||||
final PsiParameter[] parameters = {(PsiParameter)parameter};
|
||||
if(variable instanceof PsiParameter) {
|
||||
final PsiParameter[] parameters = {(PsiParameter)variable};
|
||||
methodReferenceText = LambdaCanBeMethodReferenceInspection.convertToMethodReference(expression, parameters, functionalInterfaceType, null);
|
||||
}
|
||||
if (methodReferenceText != null) {
|
||||
LOG.assertTrue(functionalInterfaceType != null);
|
||||
result += "(" + functionalInterfaceType.getCanonicalText() + ")" + methodReferenceText;
|
||||
} else {
|
||||
result += parameter.getName() + " -> " + expression.getText();
|
||||
result += variable.getName() + " -> " + expression.getText();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -803,6 +817,23 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static StringBuilder generateStream(PsiExpression iteratedValue, List<String> intermediateOps) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
final PsiType iteratedValueType = iteratedValue.getType();
|
||||
if (iteratedValueType instanceof PsiArrayType) {
|
||||
buffer.append("java.util.Arrays.stream(").append(iteratedValue.getText()).append(")");
|
||||
}
|
||||
else {
|
||||
buffer.append(getIteratedValueText(iteratedValue));
|
||||
if (!intermediateOps.isEmpty()) {
|
||||
buffer.append(".stream()");
|
||||
}
|
||||
}
|
||||
intermediateOps.forEach(buffer::append);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static String getIteratedValueText(PsiExpression iteratedValue) {
|
||||
return iteratedValue instanceof PsiCallExpression ||
|
||||
iteratedValue instanceof PsiReferenceExpression ||
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Replace with addAll" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
public void test(Integer[] arr) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
result.add(1);
|
||||
result.addAll(Arrays.asList(arr));
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// "Replace with collect" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Main {
|
||||
public void test(Integer[] arr) {
|
||||
List<Integer> result = Arrays.stream(arr).filter(x -> x > 5).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Replace with collect" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Main {
|
||||
public List<String> test(String[][] arr) {
|
||||
List<String> result = Arrays.stream(arr).filter(subArr -> subArr != null).flatMap(Arrays::stream).collect(Collectors.toList());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// "Replace with forEach" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
public List<Integer> test(int[][] arr) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
Arrays.stream(arr).filter(subArr -> subArr != null).forEach(subArr -> {
|
||||
for (int str : subArr) {
|
||||
result.add(str);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// "Replace with addAll" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
public void test(Integer[] arr) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
result.add(1);
|
||||
for(Integer i : ar<caret>r)
|
||||
result.add(i);
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Replace with collect" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
public void test(Integer[] arr) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
for(Integer x : a<caret>rr) {
|
||||
if(x > 5) {
|
||||
result.add(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// "Replace with collect" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
public List<String> test(String[][] arr) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for(String[] subArr : a<caret>rr) {
|
||||
if(subArr != null) {
|
||||
for(String str : subArr) {
|
||||
result.add(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// "Replace with forEach" "true"
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Main {
|
||||
public List<Integer> test(int[][] arr) {
|
||||
List<Integer> result = new ArrayList<>();
|
||||
for(int[] subArr : a<caret>rr) {
|
||||
if(subArr != null) {
|
||||
for(int str : subArr) {
|
||||
result.add(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Replace with forEach" "false"
|
||||
public class Main {
|
||||
public void test(int[] arr) {
|
||||
for(int i : arr) {
|
||||
if(i > 0) {
|
||||
System.out.println(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user