StreamApiUtil split and moved to java-analysis-impl module

Refactoring-related methods moved from StreamApiUtil to StreamRefactoringUtil
This commit is contained in:
Tagir Valeev
2017-07-27 11:56:59 +07:00
parent efa7ff2509
commit 2012f8af93
9 changed files with 200 additions and 188 deletions
@@ -15,11 +15,16 @@
*/
package com.intellij.codeInsight.intention.impl;
import com.intellij.psi.LambdaUtil;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiLambdaExpression;
import com.intellij.psi.PsiMethodReferenceExpression;
import com.intellij.codeInspection.util.OptionalUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.util.LambdaRefactoringUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.siyeh.ig.psiutils.ExpressionUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
/**
* @author Tagir Valeev
@@ -35,4 +40,93 @@ public class StreamRefactoringUtil {
}
return false;
}
@NotNull
public static String generateMapOperation(PsiVariable variable, @Nullable PsiType outType, PsiElement mapper) {
String shortcutMappingMethod = getShortcutMappingMethod(variable, outType, mapper);
if (shortcutMappingMethod != null) return shortcutMappingMethod.isEmpty() ? "" : "." + shortcutMappingMethod + "()";
PsiType inType = variable.getType();
String operationName = getMapOperationName(inType, outType);
if(outType != null && mapper instanceof PsiArrayInitializerExpression) {
mapper = RefactoringUtil.convertInitializerToNormalExpression((PsiExpression)mapper, outType);
}
String typeArgument = mapper instanceof PsiExpression ? OptionalUtil.getMapTypeArgument((PsiExpression)mapper, outType) : "";
return "." + typeArgument + operationName +
"(" + variable.getName() + "->" + mapper.getText() + ")";
}
/**
* Returns the shortcut mapping method name
*
* @param variable mapper input variable
* @param outType output type of the mapper
* @param mapper mapper code
* @return shortcut mapping name ("boxed", "asLongStream", "asDoubleStream") if applicable, empty string if it's
* ditto mapping (no mapping is necessary at all) and null if no shortcut is applicable for given mapper
*/
@Nullable
public static String getShortcutMappingMethod(PsiVariable variable, @Nullable PsiType outType, PsiElement mapper) {
if (!(mapper instanceof PsiExpression)) return null;
PsiExpression expression = PsiUtil.skipParenthesizedExprDown(((PsiExpression)mapper));
PsiType inType = variable.getType();
if (expression instanceof PsiTypeCastExpression && inType instanceof PsiPrimitiveType && Objects.equals(expression.getType(), outType)) {
expression = ((PsiTypeCastExpression)expression).getOperand();
}
if (ExpressionUtils.isReferenceTo(expression, variable)) {
if (!(outType instanceof PsiPrimitiveType)) {
return inType instanceof PsiPrimitiveType ? "boxed" : "";
}
if (outType.equals(inType)) {
return "";
}
if (PsiType.LONG.equals(outType) && PsiType.INT.equals(inType)) {
return "asLongStream";
}
if (PsiType.DOUBLE.equals(outType) && (PsiType.LONG.equals(inType) || PsiType.INT.equals(inType))) {
return "asDoubleStream";
}
}
return null;
}
/**
* Returns name of the Stream API mapping operation which maps from inType to outType (assuming both types are supported by Stream API)
*
* @param inType input stream element type
* @param outType output stream element type
* @return a name of the mapping operation like "map" or "mapToLong".
*/
@NotNull
public static String getMapOperationName(PsiType inType, @Nullable PsiType outType) {
if(outType instanceof PsiPrimitiveType) {
if(!outType.equals(inType)) {
if(PsiType.INT.equals(outType)) {
return "mapToInt";
} else if(PsiType.LONG.equals(outType)) {
return "mapToLong";
} else if(PsiType.DOUBLE.equals(outType)) {
return "mapToDouble";
}
}
} else if(inType instanceof PsiPrimitiveType) {
return "mapToObj";
}
return "map";
}
@Nullable
public static String getFlatMapOperationName(PsiType inType, PsiType outType) {
if (!(inType instanceof PsiPrimitiveType)) {
if (PsiType.INT.equals(outType)) {
return "flatMapToInt";
}
else if (PsiType.LONG.equals(outType)) {
return "flatMapToLong";
}
else if (PsiType.DOUBLE.equals(outType)) {
return "flatMapToDouble";
}
} else if (!inType.equals(outType)) return null;
return "flatMap";
}
}
@@ -16,6 +16,7 @@
package com.intellij.codeInspection;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.intention.impl.StreamRefactoringUtil;
import com.intellij.codeInspection.dataFlow.DfaUtil;
import com.intellij.codeInspection.dataFlow.Nullness;
import com.intellij.openapi.diagnostic.Logger;
@@ -1041,7 +1042,7 @@ public class SimplifyStreamApiCallChainsInspection extends BaseJavaBatchLocalIns
PsiLambdaExpression newLambda = (PsiLambdaExpression)factory
.createExpressionFromText("(" + elementType.getCanonicalText() + " " + name + ")->" + ct.text(body), mapToObjCall);
PsiParameter newParameter = ArrayUtil.getFirstElement(newLambda.getParameterList().getParameters());
replacement += StreamApiUtil.generateMapOperation(newParameter, outElementType, newLambda.getBody());
replacement += StreamRefactoringUtil.generateMapOperation(newParameter, outElementType, newLambda.getBody());
PsiElement result = ct.replaceAndRestoreComments(mapToObjCall, replacement);
LambdaCanBeMethodReferenceInspection.replaceAllLambdasWithMethodReferences(result);
result = JavaCodeStyleManager.getInstance(project).shortenClassReferences(result);
@@ -15,6 +15,7 @@
*/
package com.intellij.codeInspection.streamMigration;
import com.intellij.codeInsight.intention.impl.StreamRefactoringUtil;
import com.intellij.codeInspection.streamMigration.StreamApiMigrationInspection.CountingLoopSource;
import com.intellij.codeInspection.util.LambdaGenerationUtil;
import com.intellij.openapi.diagnostic.Logger;
@@ -280,7 +281,7 @@ class CollectMigration extends BaseStreamApiMigration {
PsiType addedType = getAddedElementType(myAddCall);
PsiExpression mapping = getMapping();
if (addedType == null) addedType = mapping.getType();
return StreamApiUtil.generateMapOperation(myElement, addedType, mapping);
return StreamRefactoringUtil.generateMapOperation(myElement, addedType, mapping);
}
public String generateCollector() {
@@ -562,7 +563,7 @@ class CollectMigration extends BaseStreamApiMigration {
public String generateIntermediate() {
PsiExpression mapping = myAppendCall.getArgumentList().getExpressions()[0];
mapping = JavaPsiFacade.getElementFactory(mapping.getProject()).createExpressionFromText(expressionToCharSequence(mapping), mapping);
return StreamApiUtil.generateMapOperation(myElement, null, mapping);
return StreamRefactoringUtil.generateMapOperation(myElement, null, mapping);
}
@NotNull
@@ -19,6 +19,7 @@ import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightControlFlowUtil;
import com.intellij.codeInsight.intention.impl.StreamRefactoringUtil;
import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
import com.intellij.codeInspection.LambdaCanBeMethodReferenceInspection;
import com.intellij.codeInspection.ProblemHighlightType;
@@ -772,7 +773,7 @@ public class StreamApiMigrationInspection extends BaseJavaBatchLocalInspectionTo
@Override
public String createReplacement() {
return StreamApiUtil.generateMapOperation(myVariable, myType, myExpression);
return StreamRefactoringUtil.generateMapOperation(myVariable, myType, myExpression);
}
@Override
@@ -16,6 +16,7 @@
package com.intellij.codeInspection.util;
import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.codeInsight.intention.impl.StreamRefactoringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
@@ -182,13 +183,13 @@ public class OptionalUtil {
return qualifier + ".stream()";
}
if(arg.getType() != null && elementType.isAssignableFrom(arg.getType())) {
return qualifier + ".stream()" + StreamApiUtil.generateMapOperation(var, elementType, arg);
return qualifier + ".stream()" + StreamRefactoringUtil.generateMapOperation(var, elementType, arg);
}
}
}
}
}
String flatMapOperationName = StreamApiUtil.getFlatMapOperationName(var.getType(), elementType);
String flatMapOperationName = StreamRefactoringUtil.getFlatMapOperationName(var.getType(), elementType);
if(flatMapOperationName != null) {
return qualifier + ".stream()."+flatMapOperationName+"(" + LambdaUtil.createLambda(var, trueExpression) + ")";
}
@@ -15,6 +15,7 @@
*/
package com.intellij.refactoring.chainCall;
import com.intellij.codeInsight.intention.impl.StreamRefactoringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.siyeh.ig.psiutils.StreamApiUtil;
@@ -39,11 +40,11 @@ public class CollectionChainCallExtractor implements ChainCallExtractor {
@Override
public String getMethodName(PsiVariable variable, PsiExpression expression, PsiType expressionType) {
return "stream()." + StreamApiUtil.getMapOperationName(variable.getType(), expressionType);
return "stream()." + StreamRefactoringUtil.getMapOperationName(variable.getType(), expressionType);
}
@Override
public String buildChainCall(PsiVariable variable, PsiExpression expression, PsiType expressionType) {
return ".stream()" + StreamApiUtil.generateMapOperation(variable, expressionType, expression);
return ".stream()" + StreamRefactoringUtil.generateMapOperation(variable, expressionType, expression);
}
}
@@ -16,6 +16,7 @@
package com.intellij.refactoring.chainCall;
import com.intellij.codeInsight.intention.impl.InlineStreamMapAction;
import com.intellij.codeInsight.intention.impl.StreamRefactoringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.siyeh.ig.psiutils.StreamApiUtil;
@@ -53,23 +54,23 @@ public class StreamChainCallExtractor implements ChainCallExtractor {
PsiType outType = StreamApiUtil.getStreamElementType(call.getType(), false);
String methodName = Objects.requireNonNull(call.getMethodExpression().getReferenceName());
if (methodName.startsWith("flatMap")) {
return Objects.requireNonNull(StreamApiUtil.getFlatMapOperationName(inType, outType));
return Objects.requireNonNull(StreamRefactoringUtil.getFlatMapOperationName(inType, outType));
}
if (methodName.startsWith("map")) {
return StreamApiUtil.getMapOperationName(inType, outType);
return StreamRefactoringUtil.getMapOperationName(inType, outType);
}
return methodName;
}
@Override
public String getMethodName(PsiVariable variable, PsiExpression expression, PsiType expressionType) {
String shortcutMappingMethod = StreamApiUtil.getShortcutMappingMethod(variable, expressionType, expression);
String shortcutMappingMethod = StreamRefactoringUtil.getShortcutMappingMethod(variable, expressionType, expression);
if(shortcutMappingMethod != null) return shortcutMappingMethod;
return StreamApiUtil.getMapOperationName(variable.getType(), expressionType);
return StreamRefactoringUtil.getMapOperationName(variable.getType(), expressionType);
}
@Override
public String buildChainCall(PsiVariable variable, PsiExpression expression, PsiType expressionType) {
return StreamApiUtil.generateMapOperation(variable, expressionType, expression);
return StreamRefactoringUtil.generateMapOperation(variable, expressionType, expression);
}
}
@@ -0,0 +1,83 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.siyeh.ig.psiutils;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.Contract;
/**
* @author Tagir Valeev
*/
public class StreamApiUtil {
@Contract("null -> null")
public static PsiType getStreamElementType(PsiType type) {
return getStreamElementType(type, true);
}
@Contract("null, _ -> null")
public static PsiType getStreamElementType(PsiType type, boolean variableType) {
if(!(type instanceof PsiClassType)) return null;
PsiClass aClass = ((PsiClassType)type).resolve();
if(com.intellij.psi.util.InheritanceUtil.isInheritor(aClass, false, CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM)) {
return PsiType.INT;
}
if(com.intellij.psi.util.InheritanceUtil.isInheritor(aClass, false, CommonClassNames.JAVA_UTIL_STREAM_LONG_STREAM)) {
return PsiType.LONG;
}
if(com.intellij.psi.util.InheritanceUtil.isInheritor(aClass, false, CommonClassNames.JAVA_UTIL_STREAM_DOUBLE_STREAM)) {
return PsiType.DOUBLE;
}
if(!com.intellij.psi.util.InheritanceUtil.isInheritor(aClass, false, CommonClassNames.JAVA_UTIL_STREAM_STREAM)) {
return null;
}
PsiType streamType = PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_UTIL_STREAM_STREAM, 0, false);
if (variableType) {
if (streamType instanceof PsiIntersectionType) {
return null;
}
streamType = GenericsUtil.getVariableTypeByExpressionType(streamType);
}
return streamType;
}
public static boolean isNullOrEmptyStream(PsiExpression expression) {
if(ExpressionUtils.isNullLiteral(expression)) {
return true;
}
if (!(expression instanceof PsiMethodCallExpression)) return false;
PsiMethodCallExpression call = (PsiMethodCallExpression)expression;
String name = call.getMethodExpression().getReferenceName();
if ((!"empty".equals(name) && !"of".equals(name)) || !(call.getArgumentList().getExpressions().length == 0)) {
return false;
}
PsiMethod method = call.resolveMethod();
if (method == null || !method.hasModifierProperty(PsiModifier.STATIC)) return false;
PsiClass aClass = method.getContainingClass();
if(aClass == null) return false;
String qualifiedName = aClass.getQualifiedName();
return qualifiedName != null && qualifiedName.startsWith("java.util.stream.");
}
@Contract("null -> false")
public static boolean isSupportedStreamElement(PsiType type) {
if(type == null) return false;
if(type instanceof PsiPrimitiveType) {
return type.equals(PsiType.INT) || type.equals(PsiType.LONG) || type.equals(PsiType.DOUBLE);
}
return true;
}
}
@@ -1,171 +0,0 @@
/*
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.siyeh.ig.psiutils;
import com.intellij.codeInspection.util.OptionalUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
/**
* @author Tagir Valeev
*/
public class StreamApiUtil {
@Contract("null -> null")
public static PsiType getStreamElementType(PsiType type) {
return getStreamElementType(type, true);
}
@Contract("null, _ -> null")
public static PsiType getStreamElementType(PsiType type, boolean variableType) {
if(!(type instanceof PsiClassType)) return null;
PsiClass aClass = ((PsiClassType)type).resolve();
if(com.intellij.psi.util.InheritanceUtil.isInheritor(aClass, false, CommonClassNames.JAVA_UTIL_STREAM_INT_STREAM)) {
return PsiType.INT;
}
if(com.intellij.psi.util.InheritanceUtil.isInheritor(aClass, false, CommonClassNames.JAVA_UTIL_STREAM_LONG_STREAM)) {
return PsiType.LONG;
}
if(com.intellij.psi.util.InheritanceUtil.isInheritor(aClass, false, CommonClassNames.JAVA_UTIL_STREAM_DOUBLE_STREAM)) {
return PsiType.DOUBLE;
}
if(!com.intellij.psi.util.InheritanceUtil.isInheritor(aClass, false, CommonClassNames.JAVA_UTIL_STREAM_STREAM)) {
return null;
}
PsiType streamType = PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_UTIL_STREAM_STREAM, 0, false);
if (variableType) {
if (streamType instanceof PsiIntersectionType) {
return null;
}
streamType = GenericsUtil.getVariableTypeByExpressionType(streamType);
}
return streamType;
}
public static boolean isNullOrEmptyStream(PsiExpression expression) {
if(ExpressionUtils.isNullLiteral(expression)) {
return true;
}
if (!(expression instanceof PsiMethodCallExpression)) return false;
PsiMethodCallExpression call = (PsiMethodCallExpression)expression;
String name = call.getMethodExpression().getReferenceName();
if ((!"empty".equals(name) && !"of".equals(name)) || !(call.getArgumentList().getExpressions().length == 0)) {
return false;
}
PsiMethod method = call.resolveMethod();
if (method == null || !method.hasModifierProperty(PsiModifier.STATIC)) return false;
PsiClass aClass = method.getContainingClass();
if(aClass == null) return false;
String qualifiedName = aClass.getQualifiedName();
return qualifiedName != null && qualifiedName.startsWith("java.util.stream.");
}
@Contract("null -> false")
public static boolean isSupportedStreamElement(PsiType type) {
if(type == null) return false;
if(type instanceof PsiPrimitiveType) {
return type.equals(PsiType.INT) || type.equals(PsiType.LONG) || type.equals(PsiType.DOUBLE);
}
return true;
}
@NotNull
public static String generateMapOperation(PsiVariable variable, @Nullable PsiType outType, PsiElement mapper) {
String shortcutMappingMethod = getShortcutMappingMethod(variable, outType, mapper);
if (shortcutMappingMethod != null) return shortcutMappingMethod.isEmpty() ? "" : "." + shortcutMappingMethod + "()";
PsiType inType = variable.getType();
String operationName = getMapOperationName(inType, outType);
if(outType != null && mapper instanceof PsiArrayInitializerExpression) {
mapper = RefactoringUtil.convertInitializerToNormalExpression((PsiExpression)mapper, outType);
}
String typeArgument = mapper instanceof PsiExpression ? OptionalUtil.getMapTypeArgument((PsiExpression)mapper, outType) : "";
return "." + typeArgument + operationName +
"(" + variable.getName() + "->" + mapper.getText() + ")";
}
/**
* Returns the shortcut mapping method name
*
* @param variable mapper input variable
* @param outType output type of the mapper
* @param mapper mapper code
* @return shortcut mapping name ("boxed", "asLongStream", "asDoubleStream") if applicable, empty string if it's
* ditto mapping (no mapping is necessary at all) and null if no shortcut is applicable for given mapper
*/
@Nullable
public static String getShortcutMappingMethod(PsiVariable variable, @Nullable PsiType outType, PsiElement mapper) {
if (!(mapper instanceof PsiExpression)) return null;
PsiExpression expression = PsiUtil.skipParenthesizedExprDown(((PsiExpression)mapper));
PsiType inType = variable.getType();
if (expression instanceof PsiTypeCastExpression && inType instanceof PsiPrimitiveType && Objects.equals(expression.getType(), outType)) {
expression = ((PsiTypeCastExpression)expression).getOperand();
}
if (ExpressionUtils.isReferenceTo(expression, variable)) {
if (!(outType instanceof PsiPrimitiveType)) {
return inType instanceof PsiPrimitiveType ? "boxed" : "";
}
if (outType.equals(inType)) {
return "";
}
if (PsiType.LONG.equals(outType) && PsiType.INT.equals(inType)) {
return "asLongStream";
}
if (PsiType.DOUBLE.equals(outType) && (PsiType.LONG.equals(inType) || PsiType.INT.equals(inType))) {
return "asDoubleStream";
}
}
return null;
}
@NotNull
public static String getMapOperationName(PsiType inType, @Nullable PsiType outType) {
if(outType instanceof PsiPrimitiveType) {
if(!outType.equals(inType)) {
if(PsiType.INT.equals(outType)) {
return "mapToInt";
} else if(PsiType.LONG.equals(outType)) {
return "mapToLong";
} else if(PsiType.DOUBLE.equals(outType)) {
return "mapToDouble";
}
}
} else if(inType instanceof PsiPrimitiveType) {
return "mapToObj";
}
return "map";
}
@Nullable
public static String getFlatMapOperationName(PsiType inType, PsiType outType) {
if (!(inType instanceof PsiPrimitiveType)) {
if (PsiType.INT.equals(outType)) {
return "flatMapToInt";
}
else if (PsiType.LONG.equals(outType)) {
return "flatMapToLong";
}
else if (PsiType.DOUBLE.equals(outType)) {
return "flatMapToDouble";
}
} else if (!inType.equals(outType)) return null;
return "flatMap";
}
}