mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
StreamToLoopInspection: allow custom sources when option is set
This commit is contained in:
@@ -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.
|
||||
@@ -57,7 +57,7 @@ abstract class Operation {
|
||||
|
||||
@Nullable
|
||||
static Operation createIntermediate(@NotNull String name, @NotNull PsiExpression[] args,
|
||||
@NotNull StreamVariable outVar, @NotNull PsiType inType) {
|
||||
@NotNull StreamVariable outVar, @NotNull PsiType inType, boolean supportUnknownSources) {
|
||||
if(name.equals("distinct") && args.length == 0) {
|
||||
return new DistinctOperation();
|
||||
}
|
||||
@@ -83,7 +83,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);
|
||||
return FlatMapOperation.from(outVar, args[0], inType, supportUnknownSources);
|
||||
}
|
||||
if ((name.equals("map") ||
|
||||
name.equals("mapToInt") ||
|
||||
@@ -162,7 +162,7 @@ abstract class Operation {
|
||||
|
||||
@Override
|
||||
String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
|
||||
return outVar.getDeclaration() + " = " + myFn.getText() + ";\n" + code;
|
||||
return outVar.getDeclaration(myFn.getText()) + code;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -177,7 +177,7 @@ abstract class Operation {
|
||||
StreamVariable outVar,
|
||||
String code,
|
||||
StreamToLoopReplacementContext context) {
|
||||
return outVar.getDeclaration() + " = " + inVar + ";\n" + code;
|
||||
return outVar.getDeclaration(inVar.getName()) + code;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -256,7 +256,7 @@ abstract class Operation {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static FlatMapOperation from(StreamVariable outVar, PsiExpression arg, PsiType inType) {
|
||||
public static FlatMapOperation from(StreamVariable outVar, PsiExpression arg, PsiType inType, boolean supportUnknownSources) {
|
||||
FunctionHelper fn = FunctionHelper.create(arg, 1);
|
||||
if(fn == null) return null;
|
||||
String varName = fn.tryLightTransform(inType);
|
||||
@@ -280,7 +280,8 @@ abstract class Operation {
|
||||
}
|
||||
if(!(body instanceof PsiMethodCallExpression)) return null;
|
||||
PsiMethodCallExpression terminalCall = (PsiMethodCallExpression)body;
|
||||
List<StreamToLoopInspection.OperationRecord> records = StreamToLoopInspection.extractOperations(outVar, terminalCall);
|
||||
List<StreamToLoopInspection.OperationRecord> records = StreamToLoopInspection.extractOperations(outVar, terminalCall,
|
||||
supportUnknownSources);
|
||||
if(records == null || StreamToLoopInspection.getTerminal(records) != null) return null;
|
||||
return new FlatMapOperation(varName, fn, records, condition, inverted);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ abstract class SourceOperation extends Operation {
|
||||
abstract String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context);
|
||||
|
||||
@Nullable
|
||||
static SourceOperation createSource(PsiMethodCallExpression call) {
|
||||
static SourceOperation createSource(PsiMethodCallExpression call, boolean supportUnknownSources) {
|
||||
PsiExpression[] args = call.getArgumentList().getExpressions();
|
||||
PsiType callType = call.getType();
|
||||
if(callType == null) return null;
|
||||
@@ -100,6 +100,9 @@ abstract class SourceOperation extends Operation {
|
||||
CommonClassNames.JAVA_UTIL_ARRAYS.equals(className)) {
|
||||
return new ForEachSource(args[0]);
|
||||
}
|
||||
if (supportUnknownSources) {
|
||||
return new StreamIteratorSource(call);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -224,7 +227,7 @@ abstract class SourceOperation extends Operation {
|
||||
}
|
||||
return context.getLoopLabel() +
|
||||
loop+"{\n" +
|
||||
outVar.getDeclaration() + "=" + myFn.getText() + ";\n" + code +
|
||||
outVar.getDeclaration(myFn.getText()) + code +
|
||||
"}\n";
|
||||
}
|
||||
}
|
||||
@@ -300,4 +303,56 @@ abstract class SourceOperation extends Operation {
|
||||
code + "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
private static class StreamIteratorSource extends SourceOperation {
|
||||
private PsiMethodCallExpression myCall;
|
||||
|
||||
public StreamIteratorSource(PsiMethodCallExpression call) {
|
||||
myCall = call;
|
||||
}
|
||||
|
||||
@Override
|
||||
void rename(String oldName, String newName, StreamToLoopReplacementContext context) {
|
||||
myCall = (PsiMethodCallExpression)replaceVarReference(myCall, oldName, newName, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerReusedElements(Consumer<PsiElement> consumer) {
|
||||
consumer.accept(myCall);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestNames(StreamVariable inVar, StreamVariable outVar) {
|
||||
String name = myCall.getMethodExpression().getReferenceName();
|
||||
if (name != null) {
|
||||
String unpluralized = StringUtil.unpluralize(name);
|
||||
if (unpluralized != null && !unpluralized.equals(name)) {
|
||||
outVar.addOtherNameCandidate(unpluralized);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String getIteratorType(String type) {
|
||||
switch(type) {
|
||||
case "int":
|
||||
return "java.util.PrimitiveIterator.OfInt";
|
||||
case "long":
|
||||
return "java.util.PrimitiveIterator.OfLong";
|
||||
case "double":
|
||||
return "java.util.PrimitiveIterator.OfDouble";
|
||||
default:
|
||||
return CommonClassNames.JAVA_UTIL_ITERATOR+"<"+type+">";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
String wrap(StreamVariable outVar, String code, StreamToLoopReplacementContext context) {
|
||||
String iterator = context.registerVarName(Arrays.asList("it", "iter", "iterator"));
|
||||
String declaration = getIteratorType(outVar.getType()) + " " + iterator + "=" + myCall.getText() + ".iterator()";
|
||||
String condition = iterator + ".hasNext()";
|
||||
return "for(" + declaration + ";" + condition + ";) {\n" +
|
||||
outVar.getDeclaration(iterator + ".next()") +
|
||||
code + "}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
|
||||
import com.intellij.lang.java.lexer.JavaLexer;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -43,6 +44,7 @@ import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.codeInspection.streamToLoop.Operation.FlatMapOperation;
|
||||
@@ -58,6 +60,14 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
"findFirst", "findAny", "anyMatch", "allMatch", "noneMatch",
|
||||
"toArray", "average", "forEach", "forEachOrdered", "min", "max").toSet();
|
||||
|
||||
public boolean SUPPORT_UNKNOWN_SOURCES = false;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent createOptionsPanel() {
|
||||
return new SingleCheckboxOptionsPanel("Iterate unknown Stream sources via Stream.iterator()", this, "SUPPORT_UNKNOWN_SOURCES");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
@@ -77,7 +87,7 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
if(!InheritanceUtil.isInheritor(aClass, CommonClassNames.JAVA_UTIL_STREAM_BASE_STREAM)) return;
|
||||
PsiMethodCallExpression currentCall = call;
|
||||
while(true) {
|
||||
Operation op = createOperationFromCall(StreamVariable.STUB, currentCall);
|
||||
Operation op = createOperationFromCall(StreamVariable.STUB, currentCall, SUPPORT_UNKNOWN_SOURCES);
|
||||
if(op == null) return;
|
||||
if(op instanceof SourceOperation) {
|
||||
TextRange range;
|
||||
@@ -137,7 +147,7 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static Operation createOperationFromCall(StreamVariable outVar, PsiMethodCallExpression call) {
|
||||
static Operation createOperationFromCall(StreamVariable outVar, PsiMethodCallExpression call, boolean supportUnknownSources) {
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if(method == null) return null;
|
||||
PsiClass aClass = method.getContainingClass();
|
||||
@@ -157,7 +167,7 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
// Raw type in any stream step is not supported
|
||||
return null;
|
||||
}
|
||||
Operation op = Operation.createIntermediate(name, args, outVar, elementType);
|
||||
Operation op = Operation.createIntermediate(name, args, outVar, elementType, supportUnknownSources);
|
||||
if (op != null) return op;
|
||||
PsiElement parent = call.getParent();
|
||||
boolean isVoid = parent instanceof PsiExpressionStatement ||
|
||||
@@ -166,19 +176,22 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
op = TerminalOperation.createTerminal(name, args, elementType, callType, isVoid);
|
||||
if (op != null) return op;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return SourceOperation.createSource(call);
|
||||
return SourceOperation.createSource(call, supportUnknownSources);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
static List<OperationRecord> extractOperations(StreamVariable outVar, PsiMethodCallExpression terminalCall) {
|
||||
static List<OperationRecord> extractOperations(StreamVariable outVar,
|
||||
PsiMethodCallExpression terminalCall,
|
||||
boolean supportUnknownSources) {
|
||||
List<OperationRecord> operations = new ArrayList<>();
|
||||
PsiMethodCallExpression currentCall = terminalCall;
|
||||
StreamVariable lastVar = outVar;
|
||||
Operation next = null;
|
||||
while(true) {
|
||||
Operation op = createOperationFromCall(lastVar, currentCall);
|
||||
Operation op = createOperationFromCall(lastVar, currentCall, supportUnknownSources);
|
||||
if(op == null) return null;
|
||||
if(next != null) {
|
||||
Operation combined = op.combineWithNext(next);
|
||||
@@ -239,7 +252,7 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
if (terminalCall == null) return;
|
||||
PsiType resultType = terminalCall.getType();
|
||||
if (resultType == null) return;
|
||||
List<OperationRecord> operations = extractOperations(StreamVariable.STUB, terminalCall);
|
||||
List<OperationRecord> operations = extractOperations(StreamVariable.STUB, terminalCall, true);
|
||||
TerminalOperation terminal = getTerminal(operations);
|
||||
if (terminal == null) return;
|
||||
allOperations(operations).forEach(or -> or.myOperation.suggestNames(or.myInVar, or.myOutVar));
|
||||
|
||||
@@ -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.
|
||||
@@ -116,6 +116,10 @@ class StreamVariable {
|
||||
return getType() + " " + getName();
|
||||
}
|
||||
|
||||
String getDeclaration(String initializer) {
|
||||
return getType() + " " + getName() + "=" + initializer + ";\n";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (myName == null) {
|
||||
|
||||
@@ -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.
|
||||
@@ -963,7 +963,7 @@ abstract class TerminalOperation extends Operation {
|
||||
@Override
|
||||
String generate(StreamVariable inVar, StreamToLoopReplacementContext context) {
|
||||
createVariable(context, inVar.getName());
|
||||
return myVariable.getDeclaration() + "=" + myMapper.getText() + ";\n" + myDownstream.generate(myVariable, context);
|
||||
return myVariable.getDeclaration(myMapper.getText()) + myDownstream.generate(myVariable, context);
|
||||
}
|
||||
|
||||
private void createVariable(StreamToLoopReplacementContext context, String item) {
|
||||
@@ -982,8 +982,7 @@ abstract class TerminalOperation extends Operation {
|
||||
|
||||
@Override
|
||||
public String getAccumulatorUpdater(StreamVariable inVar, String acc) {
|
||||
return myVariable.getDeclaration() + "=" + myMapper.getText() + ";\n" +
|
||||
myDownstreamCollector.getAccumulatorUpdater(myVariable, acc);
|
||||
return myVariable.getDeclaration(myMapper.getText()) + myDownstreamCollector.getAccumulatorUpdater(myVariable, acc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// "Replace Stream API chain with loop" "true"
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Test {
|
||||
Stream<String> names() {
|
||||
return Stream.of("foo", "bar");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Iterator<String> it = new Test().names().iterator(); it.hasNext(); ) {
|
||||
String name = it.next();
|
||||
String n = name.trim();
|
||||
if (!n.isEmpty()) {
|
||||
list.add(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// "Replace Stream API chain with loop" "true"
|
||||
|
||||
import java.util.PrimitiveIterator;
|
||||
import java.util.Random;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class Test {
|
||||
static void test() {
|
||||
for (int n = 1; n < 100; n++) {
|
||||
if (n > 20) {
|
||||
Integer integer = n;
|
||||
for (PrimitiveIterator.OfDouble it = new Random(integer).doubles(integer).iterator(); it.hasNext(); ) {
|
||||
double v = it.next();
|
||||
if (v < 0.01) {
|
||||
System.out.println(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
test();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// "Replace Stream API chain with loop" "true"
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Test {
|
||||
Stream<String> names() {
|
||||
return Stream.of("foo", "bar");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<String> list = new Test().names().map(String::trim).filter(n -> !n.isEmpty())
|
||||
.<caret>collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// "Replace Stream API chain with loop" "true"
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class Test {
|
||||
static void test() {
|
||||
IntStream.range(1, 100)
|
||||
.filter(n -> n > 20)
|
||||
.boxed()
|
||||
.flatMapToDouble(n -> new Random(n).doubles(n))
|
||||
.filter(n -> n < 0.01)
|
||||
.fo<caret>rEach(System.out::println);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
test();
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
@@ -24,9 +24,9 @@ public class StreamToLoopInspectionTest extends LightQuickFixParameterizedTestCa
|
||||
@NotNull
|
||||
@Override
|
||||
protected LocalInspectionTool[] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{
|
||||
new StreamToLoopInspection()
|
||||
};
|
||||
StreamToLoopInspection inspection = new StreamToLoopInspection();
|
||||
inspection.SUPPORT_UNKNOWN_SOURCES = true;
|
||||
return new LocalInspectionTool[]{inspection};
|
||||
}
|
||||
|
||||
public void test() throws Exception { doAllTests(); }
|
||||
|
||||
Reference in New Issue
Block a user