mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Insoection to convert guava's fluent iterables to java 8 streams
This commit is contained in:
+138
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.intellij.codeInspection.java18StreamApi;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
abstract class FluentIterableMethodTransformer {
|
||||
private final static Logger LOG = Logger.getInstance(FluentIterableMethodTransformer.class);
|
||||
|
||||
protected abstract String formatMethod(PsiExpression[] initialMethodParameters);
|
||||
|
||||
protected boolean negate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public final PsiMethodCallExpression transform(PsiMethodCallExpression expression, PsiElementFactory elementFactory) {
|
||||
final String formatted = formatMethod(expression.getArgumentList().getExpressions());
|
||||
final String negation = negate() ? "!" : "";
|
||||
final PsiExpression qualifierExpression = expression.getMethodExpression().getQualifierExpression();
|
||||
LOG.assertTrue(qualifierExpression != null);
|
||||
final String oldQualifierText = qualifierExpression.getText();
|
||||
final String expressionText = negation + oldQualifierText + "." + formatted;
|
||||
final PsiElement replaced = expression.replace(elementFactory.createExpressionFromText(expressionText, null));
|
||||
if (replaced instanceof PsiMethodCallExpression) {
|
||||
PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)replaced;
|
||||
while (true) {
|
||||
final PsiExpression qualifier = methodCallExpression.getMethodExpression().getQualifierExpression();
|
||||
LOG.assertTrue(qualifier != null);
|
||||
if (oldQualifierText.equals(qualifier.getText())) {
|
||||
return methodCallExpression;
|
||||
}
|
||||
methodCallExpression = (PsiMethodCallExpression)qualifier;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class OneParameterMethodTransformer extends FluentIterableMethodTransformer {
|
||||
private final String myTemplate;
|
||||
private final boolean myParameterIsFunctionOrPredicate;
|
||||
|
||||
OneParameterMethodTransformer(String template, boolean parameterIsFunctionOrPredicate) {
|
||||
myTemplate = template;
|
||||
myParameterIsFunctionOrPredicate = parameterIsFunctionOrPredicate;
|
||||
}
|
||||
|
||||
OneParameterMethodTransformer(String parameterIsFunctionOrPredicate) {
|
||||
this(parameterIsFunctionOrPredicate, false);
|
||||
}
|
||||
|
||||
protected String formatMethod(PsiExpression[] initialMethodParameters) {
|
||||
final String matchedElementText;
|
||||
if (myParameterIsFunctionOrPredicate) {
|
||||
if (initialMethodParameters.length == 1) {
|
||||
PsiExpression parameter = initialMethodParameters[0];
|
||||
final PsiType type = parameter.getType();
|
||||
Boolean role;
|
||||
if (!(type instanceof PsiMethodReferenceType) && !(type instanceof PsiLambdaExpressionType)
|
||||
&& (role = GuavaFunctionAndPredicateConverter.isClassConditionPredicate(parameter)) != null) {
|
||||
matchedElementText = GuavaFunctionAndPredicateConverter.convertFunctionOrPredicateParameter(parameter, role);
|
||||
} else {
|
||||
matchedElementText = parameter.getText();
|
||||
}
|
||||
} else {
|
||||
matchedElementText = "";
|
||||
}
|
||||
} else {
|
||||
matchedElementText = initialMethodParameters.length > 0 ? initialMethodParameters[0].getText() : "";
|
||||
}
|
||||
return String.format(myTemplate, matchedElementText);
|
||||
}
|
||||
}
|
||||
|
||||
static class ToArrayMethodTransformer extends FluentIterableMethodTransformer {
|
||||
@Override
|
||||
protected String formatMethod(PsiExpression[] initialMethodParameters) {
|
||||
final PsiExpression parameter = initialMethodParameters[0];
|
||||
if (parameter instanceof PsiClassObjectAccessExpression) {
|
||||
final PsiType type = ((PsiClassObjectAccessExpression)parameter).getOperand().getType();
|
||||
if (type instanceof PsiClassType) {
|
||||
final PsiClass resolvedClass = ((PsiClassType)type).resolve();
|
||||
if (resolvedClass != null) {
|
||||
final String qName = resolvedClass.getQualifiedName();
|
||||
if (qName != null) {
|
||||
return String.format("toArray(%s[]::new)", qName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return "toArray()";
|
||||
}
|
||||
}
|
||||
|
||||
static class ParameterlessMethodTransformer extends FluentIterableMethodTransformer {
|
||||
private final String myTemplate;
|
||||
private final boolean myNegation;
|
||||
|
||||
ParameterlessMethodTransformer(String template) {
|
||||
this(template.endsWith(")") ? template : (template + "()"), false);
|
||||
}
|
||||
|
||||
ParameterlessMethodTransformer(String template, boolean negation) {
|
||||
myTemplate = template;
|
||||
myNegation = negation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean negate() {
|
||||
return myNegation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String formatMethod(PsiExpression[] initialMethodParameters) {
|
||||
return myTemplate;
|
||||
}
|
||||
}
|
||||
}
|
||||
+476
@@ -0,0 +1,476 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.intellij.codeInspection.java18StreamApi;
|
||||
|
||||
import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.module.impl.scopes.JdkScope;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import gnu.trove.THashSet;
|
||||
import gnu.trove.TObjectIdentityHashingStrategy;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
@SuppressWarnings("DialogTitleCapitalization")
|
||||
public class GuavaFluentIterableInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
private final static Logger LOG = Logger.getInstance(GuavaFluentIterableInspection.class);
|
||||
private final static String PROBLEM_DESCRIPTION = "FluentIterable is used while Stream API is accessible";
|
||||
public final static String GUAVA_FLUENT_ITERABLE = "com.google.common.collect.FluentIterable";
|
||||
public final static String GUAVA_OPTIONAL = "com.google.common.base.Optional";
|
||||
public final static String GUAVA_IMMUTABLE_MAP = "com.google.common.collect.ImmutableMap";
|
||||
public final static String FLUENT_ITERABLE_FROM = "from";
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
|
||||
final PsiClass fluentIterable = JavaPsiFacade.getInstance(holder.getProject())
|
||||
.findClass(GUAVA_FLUENT_ITERABLE, GlobalSearchScope.allScope(holder.getProject()));
|
||||
if (fluentIterable == null) {
|
||||
return PsiElementVisitor.EMPTY_VISITOR;
|
||||
}
|
||||
return new JavaRecursiveElementVisitor() {
|
||||
private final SmartPointerManager mySmartPointerManager = SmartPointerManager.getInstance(holder.getProject());
|
||||
private final Set<PsiMethodCallExpression> myMethodCallsToIgnore =
|
||||
new THashSet<PsiMethodCallExpression>(new TObjectIdentityHashingStrategy<PsiMethodCallExpression>());
|
||||
|
||||
private final MultiMap<PsiLocalVariable, PsiExpression> myLocalVariablesUsages = new MultiMap<PsiLocalVariable, PsiExpression>();
|
||||
private final Set<PsiLocalVariable> myUnconvertibleVariables = new THashSet<PsiLocalVariable>();
|
||||
|
||||
@Override
|
||||
public void visitLocalVariable(final PsiLocalVariable localVariable) {
|
||||
final PsiType type = localVariable.getType();
|
||||
if (!(type instanceof PsiClassType) || myUnconvertibleVariables.contains(localVariable)) {
|
||||
return;
|
||||
}
|
||||
final PsiClass variableClass = ((PsiClassType)type).resolve();
|
||||
if (variableClass == null) {
|
||||
return;
|
||||
}
|
||||
final String qualifiedName = variableClass.getQualifiedName();
|
||||
if (!GUAVA_FLUENT_ITERABLE.equals(qualifiedName)) {
|
||||
return;
|
||||
}
|
||||
final PsiCodeBlock context = PsiTreeUtil.getParentOfType(localVariable, PsiCodeBlock.class);
|
||||
if (context == null || !checkDeclaration(localVariable.getInitializer())) {
|
||||
myUnconvertibleVariables.add(localVariable);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkDeclaration(PsiExpression declaration) {
|
||||
if (declaration == null) {
|
||||
return true;
|
||||
}
|
||||
if (!(declaration instanceof PsiMethodCallExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiMethodCallExpression currentCallExpression = (PsiMethodCallExpression)declaration;
|
||||
while (true) {
|
||||
final PsiExpression qualifier = currentCallExpression.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier instanceof PsiMethodCallExpression) {
|
||||
final PsiMethod method = currentCallExpression.resolveMethod();
|
||||
if (method == null || GuavaFluentIterableMethodConverters.isStopMethod(method.getName())) {
|
||||
return false;
|
||||
}
|
||||
final PsiClass aClass = method.getContainingClass();
|
||||
if (aClass == null || !aClass.isEquivalentTo(fluentIterable)) {
|
||||
return false;
|
||||
}
|
||||
currentCallExpression = (PsiMethodCallExpression)qualifier;
|
||||
} else {
|
||||
if (qualifier instanceof PsiReferenceExpression) {
|
||||
final PsiMethod method = currentCallExpression.resolveMethod();
|
||||
if (method == null || !FLUENT_ITERABLE_FROM.equals(method.getName())) {
|
||||
return false;
|
||||
}
|
||||
final PsiClass aClass = method.getContainingClass();
|
||||
if (aClass == null || !GUAVA_FLUENT_ITERABLE.equals(aClass.getQualifiedName())) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
super.visitReferenceExpression(expression);
|
||||
final PsiElement resolvedElement = expression.resolve();
|
||||
if (resolvedElement instanceof PsiLocalVariable) {
|
||||
PsiLocalVariable fluentIterableVariable = (PsiLocalVariable)resolvedElement;
|
||||
if (!fluentIterable.isEquivalentTo(PsiTypesUtil.getPsiClass(fluentIterableVariable.getType())) ||
|
||||
myUnconvertibleVariables.contains(fluentIterableVariable)) {
|
||||
return;
|
||||
}
|
||||
analyzeExpression(expression, fluentIterableVariable);
|
||||
}
|
||||
}
|
||||
|
||||
private void addToUnconvertible(PsiLocalVariable variable) {
|
||||
myUnconvertibleVariables.add(variable);
|
||||
myLocalVariablesUsages.remove(variable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitJavaFile(PsiJavaFile file) {
|
||||
super.visitJavaFile(file);
|
||||
for (Map.Entry<PsiLocalVariable, Collection<PsiExpression>> e : myLocalVariablesUsages.entrySet()) {
|
||||
final PsiLocalVariable localVariable = e.getKey();
|
||||
final Collection<PsiExpression> foundUsages = e.getValue();
|
||||
final SmartPsiElementPointer<PsiLocalVariable> variablePointer = mySmartPointerManager.createSmartPsiElementPointer(localVariable);
|
||||
final ConvertGuavaFluentIterableQuickFix quickFix = new ConvertGuavaFluentIterableQuickFix(variablePointer, ContainerUtil.map(
|
||||
new THashSet<PsiExpression>(foundUsages), new Function<PsiExpression, SmartPsiElementPointer<PsiExpression>>() {
|
||||
@Override
|
||||
public SmartPsiElementPointer<PsiExpression> fun(PsiExpression expression) {
|
||||
return mySmartPointerManager.createSmartPsiElementPointer(expression);
|
||||
}
|
||||
}));
|
||||
holder.registerProblem(localVariable, PROBLEM_DESCRIPTION, quickFix);
|
||||
for (PsiExpression usage : foundUsages) {
|
||||
holder.registerProblem(usage, PROBLEM_DESCRIPTION, quickFix);
|
||||
}
|
||||
}
|
||||
myLocalVariablesUsages.clear();
|
||||
myUnconvertibleVariables.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
|
||||
super.visitMethodCallExpression(expression);
|
||||
if (!myMethodCallsToIgnore.add(expression)) {
|
||||
return;
|
||||
}
|
||||
final PsiReferenceExpression methodExpression = expression.getMethodExpression();
|
||||
final String methodName = methodExpression.getReferenceName();
|
||||
if (FLUENT_ITERABLE_FROM.equals(methodName)) {
|
||||
final PsiMethod method = expression.resolveMethod();
|
||||
if (method == null || !method.hasModifierProperty(PsiModifier.STATIC) || !fluentIterable.isEquivalentTo(method.getContainingClass())) {
|
||||
return;
|
||||
}
|
||||
PsiMethodCallExpression currentExpression = expression;
|
||||
while (true) {
|
||||
myMethodCallsToIgnore.add(currentExpression);
|
||||
PsiMethodCallExpression parentMethodCall = PsiTreeUtil.getParentOfType(currentExpression, PsiMethodCallExpression.class);
|
||||
if (parentMethodCall != null) {
|
||||
if (parentMethodCall.getMethodExpression().getQualifierExpression() == currentExpression) {
|
||||
final PsiMethod parentCallMethod = parentMethodCall.resolveMethod();
|
||||
if (parentCallMethod != null && fluentIterable.isEquivalentTo(parentCallMethod.getContainingClass())) {
|
||||
if (GuavaFluentIterableMethodConverters.isStopMethod(parentCallMethod.getName())) {
|
||||
return;
|
||||
}
|
||||
currentExpression = parentMethodCall;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
final PsiElement expressionParent = currentExpression.getParent();
|
||||
if (expressionParent instanceof PsiReturnStatement) {
|
||||
final PsiType containingMethodReturnType = findContainingMethodReturnType(currentExpression);
|
||||
if (containingMethodReturnType instanceof PsiClassType) {
|
||||
final PsiClass resolvedClass = ((PsiClassType)containingMethodReturnType).resolve();
|
||||
if (resolvedClass == null || !(resolvedClass.getResolveScope() instanceof JdkScope)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (expressionParent instanceof PsiLocalVariable) {
|
||||
final PsiType type = ((PsiLocalVariable)expressionParent).getType();
|
||||
if (type instanceof PsiClassType) {
|
||||
final PsiClass resolvedClass = ((PsiClassType)type).resolve();
|
||||
if (resolvedClass == null || !(resolvedClass.getResolveScope() instanceof JdkScope)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (expressionParent instanceof PsiExpressionList) {
|
||||
if (expressionParent.getParent() instanceof PsiMethodCallExpression
|
||||
&& !isMethodWithParamAcceptsConversion((PsiMethodCallExpression)expressionParent.getParent(),
|
||||
currentExpression,
|
||||
fluentIterable)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final List<SmartPsiElementPointer<PsiExpression>> exprAsList =
|
||||
ContainerUtil.list(mySmartPointerManager.createSmartPsiElementPointer((PsiExpression)currentExpression));
|
||||
holder.registerProblem(currentExpression, PROBLEM_DESCRIPTION, new ConvertGuavaFluentIterableQuickFix(null, exprAsList));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
final PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
|
||||
if (GuavaFluentIterableMethodConverters.isFluentIterableMethod(methodName) &&
|
||||
qualifierExpression instanceof PsiReferenceExpression) {
|
||||
final PsiElement resolvedElement = ((PsiReferenceExpression)qualifierExpression).resolve();
|
||||
if (resolvedElement instanceof PsiLocalVariable) {
|
||||
PsiLocalVariable fluentIterableLocalVariable = (PsiLocalVariable)resolvedElement;
|
||||
if (!fluentIterable.isEquivalentTo(PsiTypesUtil.getPsiClass(fluentIterableLocalVariable.getType())) ||
|
||||
myUnconvertibleVariables.contains(fluentIterableLocalVariable)) {
|
||||
return;
|
||||
}
|
||||
analyzeExpression(expression, fluentIterableLocalVariable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void analyzeExpression(PsiExpression expression, PsiLocalVariable fluentIterableLocalVariable) {
|
||||
PsiExpression baseExpression = expression;
|
||||
while (true) {
|
||||
final PsiMethodCallExpression
|
||||
methodCallExpression = PsiTreeUtil.getParentOfType(baseExpression, PsiMethodCallExpression.class);
|
||||
if (methodCallExpression != null && methodCallExpression.getMethodExpression().getQualifierExpression() == baseExpression) {
|
||||
final String currentMethodName = methodCallExpression.getMethodExpression().getReferenceName();
|
||||
if (GuavaFluentIterableMethodConverters.isFluentIterableMethod(currentMethodName)) {
|
||||
if (GuavaFluentIterableMethodConverters.isStopMethod((currentMethodName))) {
|
||||
addToUnconvertible(fluentIterableLocalVariable);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
final PsiMethod method = methodCallExpression.resolveMethod();
|
||||
if (method != null && method.getContainingClass() != null && method.getContainingClass().isEquivalentTo(fluentIterable)) {
|
||||
baseExpression = methodCallExpression;
|
||||
myMethodCallsToIgnore.add(methodCallExpression);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
final PsiElement parent = baseExpression.getParent();
|
||||
if (parent instanceof PsiExpressionList) {
|
||||
myLocalVariablesUsages.putValue(fluentIterableLocalVariable, baseExpression);
|
||||
final boolean suitable = parent.getParent() instanceof PsiMethodCallExpression &&
|
||||
isMethodWithParamAcceptsConversion((PsiMethodCallExpression)parent.getParent(), baseExpression,
|
||||
fluentIterable);
|
||||
if (!suitable) {
|
||||
addToUnconvertible(fluentIterableLocalVariable);
|
||||
}
|
||||
} else if (parent instanceof PsiReferenceExpression) {
|
||||
final PsiMethodCallExpression parentMethodCall = PsiTreeUtil.getParentOfType(baseExpression, PsiMethodCallExpression.class);
|
||||
if (parentMethodCall != null && parentMethodCall.getMethodExpression().getQualifier() == baseExpression) {
|
||||
if (GuavaOptionalConverter.isConvertibleIfOption(parentMethodCall)) {
|
||||
myLocalVariablesUsages.putValue(fluentIterableLocalVariable, baseExpression);
|
||||
} else {
|
||||
addToUnconvertible(fluentIterableLocalVariable);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (parent instanceof PsiLocalVariable) {
|
||||
myLocalVariablesUsages.putValue(fluentIterableLocalVariable, baseExpression);
|
||||
} else if (parent instanceof PsiAssignmentExpression) {
|
||||
final PsiAssignmentExpression assignment = (PsiAssignmentExpression)parent;
|
||||
final PsiExpression lExpression = assignment.getLExpression();
|
||||
if (lExpression instanceof PsiReferenceExpression) {
|
||||
if (((PsiReferenceExpression)lExpression).isReferenceTo(fluentIterableLocalVariable)) {
|
||||
if (isSelfAssignment(assignment, fluentIterableLocalVariable)) {
|
||||
myLocalVariablesUsages.putValue(fluentIterableLocalVariable, baseExpression);
|
||||
return;
|
||||
}
|
||||
if (checkDeclaration(assignment.getRExpression())) {
|
||||
myLocalVariablesUsages.putValue(fluentIterableLocalVariable, assignment.getRExpression());
|
||||
return;
|
||||
}
|
||||
addToUnconvertible(fluentIterableLocalVariable);
|
||||
} else {
|
||||
myLocalVariablesUsages.putValue(fluentIterableLocalVariable, baseExpression);
|
||||
}
|
||||
}
|
||||
else {
|
||||
myLocalVariablesUsages.putValue(fluentIterableLocalVariable, baseExpression);
|
||||
}
|
||||
} else if (parent instanceof PsiReturnStatement) {
|
||||
final PsiType containingMethodReturnType = findContainingMethodReturnType(baseExpression);
|
||||
if (baseExpression == expression) {
|
||||
if (!(containingMethodReturnType instanceof PsiClassType)) {
|
||||
addToUnconvertible(fluentIterableLocalVariable);
|
||||
return;
|
||||
}
|
||||
final PsiClass resolvedClass = ((PsiClassType)containingMethodReturnType).resolve();
|
||||
if (resolvedClass == null || (!CommonClassNames.JAVA_LANG_ITERABLE.equals(resolvedClass.getQualifiedName()) &&
|
||||
!CommonClassNames.JAVA_LANG_OBJECT.equals(resolvedClass.getQualifiedName()))) {
|
||||
addToUnconvertible(fluentIterableLocalVariable);
|
||||
} else {
|
||||
myLocalVariablesUsages.putValue(fluentIterableLocalVariable, baseExpression);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (containingMethodReturnType instanceof PsiClassType) {
|
||||
final PsiClass resolvedClass = ((PsiClassType)containingMethodReturnType).resolve();
|
||||
if (resolvedClass == null || !(resolvedClass.getResolveScope() instanceof JdkScope)) {
|
||||
addToUnconvertible(fluentIterableLocalVariable);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (parent instanceof PsiExpressionStatement) {
|
||||
myLocalVariablesUsages.putValue(fluentIterableLocalVariable, baseExpression);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public static boolean isMethodWithParamAcceptsConversion(PsiMethodCallExpression methodCallExpression,
|
||||
PsiExpression baseExpression,
|
||||
PsiClass fluentIterable) {
|
||||
final PsiExpressionList argList = methodCallExpression.getArgumentList();
|
||||
final PsiMethod method = methodCallExpression.resolveMethod();
|
||||
if (method == null) {
|
||||
return true;
|
||||
}
|
||||
final PsiParameterList paramList = method.getParameterList();
|
||||
if (paramList.getParametersCount() != argList.getExpressions().length &&
|
||||
!(paramList.getParameters()[paramList.getParametersCount() - 1].getType() instanceof PsiEllipsisType)) {
|
||||
return false;
|
||||
}
|
||||
int index = -1;
|
||||
PsiExpression[] expressions = argList.getExpressions();
|
||||
for (int i = 0, length = expressions.length; i < length; i++) {
|
||||
if (expressions[i] == baseExpression) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOG.assertTrue(index >= 0);
|
||||
PsiType parameterType;
|
||||
if (index > paramList.getParametersCount() - 1) {
|
||||
parameterType = paramList.getParameters()[paramList.getParametersCount() - 1].getType();
|
||||
} else {
|
||||
parameterType = paramList.getParameters()[index].getType();
|
||||
}
|
||||
if (parameterType instanceof PsiEllipsisType) {
|
||||
parameterType = ((PsiEllipsisType)parameterType).getComponentType();
|
||||
}
|
||||
if (parameterType instanceof PsiClassType) {
|
||||
final PsiClass resolvedParameterClass = ((PsiClassType)parameterType).resolve();
|
||||
final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(methodCallExpression.getProject());
|
||||
final GlobalSearchScope scope = GlobalSearchScope.allScope(methodCallExpression.getProject());
|
||||
final PsiClass optional = javaPsiFacade.findClass(GUAVA_OPTIONAL, scope);
|
||||
final PsiClass immutableMap = javaPsiFacade.findClass(GUAVA_IMMUTABLE_MAP, scope);
|
||||
if (resolvedParameterClass != null &&
|
||||
(InheritanceUtil.isInheritorOrSelf(resolvedParameterClass, fluentIterable, true) ||
|
||||
InheritanceUtil.isInheritorOrSelf(resolvedParameterClass, optional, true) ||
|
||||
InheritanceUtil.isInheritorOrSelf(resolvedParameterClass, immutableMap, true))){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static PsiType findContainingMethodReturnType(PsiElement methodElement) {
|
||||
final PsiMethod containingMethod = PsiTreeUtil.getParentOfType(methodElement, PsiMethod.class);
|
||||
if (containingMethod == null) {
|
||||
return null;
|
||||
}
|
||||
final PsiType containingMethodReturnType = containingMethod.getReturnType();
|
||||
if (containingMethodReturnType == null) {
|
||||
return null;
|
||||
}
|
||||
return containingMethodReturnType;
|
||||
}
|
||||
|
||||
private static boolean isSelfAssignment(PsiAssignmentExpression expression, PsiLocalVariable localVariable) {
|
||||
final PsiExpression rExpression = expression.getRExpression();
|
||||
if (!(rExpression instanceof PsiMethodCallExpression)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiMethodCallExpression methodCall = (PsiMethodCallExpression)rExpression;
|
||||
while (true) {
|
||||
final PsiExpression qualifier = methodCall.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier instanceof PsiMethodCallExpression) {
|
||||
methodCall = (PsiMethodCallExpression)qualifier;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final PsiExpression qualifierExpression = methodCall.getMethodExpression().getQualifierExpression();
|
||||
if (qualifierExpression instanceof PsiReferenceExpression &&
|
||||
!((PsiReferenceExpression)qualifierExpression).isReferenceTo(localVariable)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static class ConvertGuavaFluentIterableQuickFix implements LocalQuickFix {
|
||||
@Nullable private final SmartPsiElementPointer<PsiLocalVariable> myVariable;
|
||||
@NotNull private final List<SmartPsiElementPointer<PsiExpression>> myFoundUsages;
|
||||
|
||||
protected ConvertGuavaFluentIterableQuickFix(@Nullable SmartPsiElementPointer<PsiLocalVariable> variable,
|
||||
@NotNull List<SmartPsiElementPointer<PsiExpression>> foundUsages) {
|
||||
myVariable = variable;
|
||||
myFoundUsages = foundUsages;
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return getFamilyName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Convert Guava's FluentIterable to java.util.stream.Stream";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project);
|
||||
final PsiElementFactory elementFactory = javaPsiFacade.getElementFactory();
|
||||
final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
|
||||
for (SmartPsiElementPointer<PsiExpression> usage : myFoundUsages) {
|
||||
final PsiExpression element = usage.getElement();
|
||||
if (element != null) {
|
||||
GuavaFluentIterableMethodConverters.convert(element, elementFactory, codeStyleManager);
|
||||
}
|
||||
}
|
||||
if (myVariable != null) {
|
||||
final PsiLocalVariable element = myVariable.getElement();
|
||||
if (element != null) {
|
||||
GuavaFluentIterableMethodConverters.convert(element, elementFactory, codeStyleManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+254
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.intellij.codeInspection.java18StreamApi;
|
||||
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class GuavaFluentIterableMethodConverters {
|
||||
private static final Logger LOG = Logger.getInstance(GuavaFluentIterableMethodConverters.class);
|
||||
|
||||
private static final Map<String, FluentIterableMethodTransformer> METHOD_INDEX = new HashMap<String, FluentIterableMethodTransformer>();
|
||||
private static final Map<String, String> TO_OTHER_COLLECTION_METHODS = new HashMap<String, String>();
|
||||
private static final Set<String> STOP_METHODS = new HashSet<String>();
|
||||
|
||||
static {
|
||||
METHOD_INDEX.put("allMatch", new FluentIterableMethodTransformer.OneParameterMethodTransformer(StreamApiConstants.ALL_MATCH + "(%s)", true));
|
||||
METHOD_INDEX.put("anyMatch", new FluentIterableMethodTransformer.OneParameterMethodTransformer(StreamApiConstants.ANY_MATCH + "(%s)", true));
|
||||
|
||||
METHOD_INDEX.put("contains", new FluentIterableMethodTransformer.OneParameterMethodTransformer("anyMatch(e -> e != null && e.equals(%s))"));
|
||||
METHOD_INDEX.put("copyInto", new FluentIterableMethodTransformer.OneParameterMethodTransformer("forEach(%s::add)"));
|
||||
METHOD_INDEX.put("filter", new FluentIterableMethodTransformer.OneParameterMethodTransformer(StreamApiConstants.FILTER + "(%s)", true));
|
||||
METHOD_INDEX.put("first", new FluentIterableMethodTransformer.ParameterlessMethodTransformer(StreamApiConstants.FIND_FIRST));
|
||||
METHOD_INDEX.put("firstMatch", new FluentIterableMethodTransformer.OneParameterMethodTransformer("filter(%s).findFirst()", true));
|
||||
METHOD_INDEX.put("get", new FluentIterableMethodTransformer.OneParameterMethodTransformer("collect(java.util.stream.Collectors.toList()).get(%s)"));
|
||||
METHOD_INDEX
|
||||
.put("index", new FluentIterableMethodTransformer.OneParameterMethodTransformer("collect(java.util.stream.Collectors.toList()).indexOf(%s)"));
|
||||
METHOD_INDEX.put("isEmpty", new FluentIterableMethodTransformer.ParameterlessMethodTransformer("findAny().isPresent()", true));
|
||||
METHOD_INDEX.put("last", new FluentIterableMethodTransformer.ParameterlessMethodTransformer("reduce((previous, current) -> current)"));
|
||||
METHOD_INDEX.put("limit", new FluentIterableMethodTransformer.OneParameterMethodTransformer(StreamApiConstants.LIMIT + "(%s)"));
|
||||
METHOD_INDEX.put("size", new FluentIterableMethodTransformer.ParameterlessMethodTransformer("collect(java.util.stream.Collectors.toList()).size()"));
|
||||
METHOD_INDEX.put("skip", new FluentIterableMethodTransformer.OneParameterMethodTransformer(StreamApiConstants.SKIP + "(%s)"));
|
||||
METHOD_INDEX.put("toArray", new FluentIterableMethodTransformer.ToArrayMethodTransformer());
|
||||
METHOD_INDEX.put("transform", new FluentIterableMethodTransformer.OneParameterMethodTransformer(StreamApiConstants.MAP + "(%s)", true));
|
||||
METHOD_INDEX.put("transformAndConcat", new FluentIterableMethodTransformer.OneParameterMethodTransformer(StreamApiConstants.FLAT_MAP + "(%s)", true));
|
||||
METHOD_INDEX.put("uniqueIndex", new FluentIterableMethodTransformer.OneParameterMethodTransformer(
|
||||
"collect(java.util.stream.Collectors.toMap(%s, java.util.function.Function.identity()))"));
|
||||
|
||||
TO_OTHER_COLLECTION_METHODS.put("toMap", "collect(java.util.stream.Collectors.toMap(java.util.function.Function.identity(), %s))");
|
||||
TO_OTHER_COLLECTION_METHODS.put("toList", "collect(java.util.stream.Collectors.toList())");
|
||||
TO_OTHER_COLLECTION_METHODS.put("toSet", "collect(java.util.stream.Collectors.toSet())");
|
||||
TO_OTHER_COLLECTION_METHODS.put("toSortedList", "sorted(%s).collect(java.util.stream.Collectors.toList())");
|
||||
TO_OTHER_COLLECTION_METHODS.put("toSortedSet", "sorted(%s).collect(java.util.stream.Collectors.toSet())");
|
||||
|
||||
STOP_METHODS.add("append");
|
||||
STOP_METHODS.add("cycle");
|
||||
}
|
||||
|
||||
public static boolean isFluentIterableMethod(final String methodName) {
|
||||
return STOP_METHODS.contains(methodName) ||
|
||||
TO_OTHER_COLLECTION_METHODS.containsKey(methodName) ||
|
||||
METHOD_INDEX.containsKey(methodName);
|
||||
}
|
||||
|
||||
public static boolean isStopMethod(final String methodName) {
|
||||
return STOP_METHODS.contains(methodName);
|
||||
}
|
||||
|
||||
public static void convert(final PsiLocalVariable localVariable,
|
||||
final PsiElementFactory elementFactory,
|
||||
final JavaCodeStyleManager codeStyleManager) {
|
||||
final PsiTypeElement typeElement = localVariable.getTypeElement();
|
||||
final PsiReferenceParameterList generics = PsiTreeUtil.findChildOfType(typeElement, PsiReferenceParameterList.class);
|
||||
typeElement.replace(elementFactory.createTypeElementFromText(
|
||||
StreamApiConstants.JAVA_UTIL_STREAM_STREAM + (generics == null ? "" : generics.getText()), null));
|
||||
|
||||
final PsiExpression initializer = localVariable.getInitializer();
|
||||
if (initializer != null) {
|
||||
PsiMethodCallExpression initializerMethodCall = (PsiMethodCallExpression)initializer;
|
||||
convertMethodCallDeep(elementFactory, initializerMethodCall);
|
||||
}
|
||||
codeStyleManager.shortenClassReferences(localVariable);
|
||||
}
|
||||
|
||||
public static void convert(PsiExpression expression,
|
||||
final PsiElementFactory elementFactory,
|
||||
final JavaCodeStyleManager codeStyleManager) {
|
||||
if (expression instanceof PsiReferenceExpression) {
|
||||
final PsiElement expressionParent = expression.getParent();
|
||||
if (expressionParent instanceof PsiReturnStatement || isIterableMethodParameter(expressionParent, expression)) {
|
||||
expression = (PsiExpression)expression.replace(
|
||||
elementFactory.createExpressionFromText(expression.getText() + ".collect(java.util.stream.Collectors.toList())", null));
|
||||
codeStyleManager.shortenClassReferences(expression);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final PsiMethodCallExpression parentMethodCall = PsiTreeUtil.getParentOfType(expression, PsiMethodCallExpression.class);
|
||||
if (parentMethodCall != null && parentMethodCall.getMethodExpression().getQualifierExpression() == expression) {
|
||||
final PsiMethod seqTailMethod = parentMethodCall.resolveMethod();
|
||||
if (seqTailMethod == null) {
|
||||
return;
|
||||
}
|
||||
final PsiClass seqTailMethodClass = seqTailMethod.getContainingClass();
|
||||
if (seqTailMethodClass != null && GuavaFluentIterableInspection.GUAVA_OPTIONAL.equals(seqTailMethodClass.getQualifiedName())) {
|
||||
final PsiMethodCallExpression newParentMethodCall =
|
||||
GuavaOptionalConverter.convertGuavaOptionalToJava(parentMethodCall, elementFactory);
|
||||
expression = newParentMethodCall.getMethodExpression().getQualifierExpression();
|
||||
}
|
||||
}
|
||||
if (expression instanceof PsiMethodCallExpression) {
|
||||
expression = convertMethodCallDeep(elementFactory, (PsiMethodCallExpression)expression);
|
||||
}
|
||||
if (expression == null) {
|
||||
return;
|
||||
}
|
||||
final PsiElement parent = expression.getParent();
|
||||
if (parent instanceof PsiExpressionList) {
|
||||
final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)parent.getParent();
|
||||
PsiExpression[] expressions = methodCall.getArgumentList().getExpressions();
|
||||
int index = ArrayUtil.indexOf(expressions, expression);
|
||||
LOG.assertTrue(index >= 0);
|
||||
final PsiMethod method = methodCall.resolveMethod();
|
||||
LOG.assertTrue(method != null);
|
||||
final PsiType parameterType = method.getParameterList().getParameters()[index].getType();
|
||||
expression = addCollectToListIfNeed(expression, parameterType, elementFactory);
|
||||
} else if (parent instanceof PsiReturnStatement) {
|
||||
final PsiMethod containingMethod = PsiTreeUtil.getParentOfType(parent, PsiMethod.class);
|
||||
LOG.assertTrue(containingMethod != null);
|
||||
final PsiType returnType = containingMethod.getReturnType();
|
||||
expression = addCollectToListIfNeed(expression, returnType, elementFactory);
|
||||
}
|
||||
codeStyleManager.shortenClassReferences(expression);
|
||||
}
|
||||
|
||||
private static PsiExpression addCollectToListIfNeed(PsiExpression expression, PsiType type, PsiElementFactory elementFactory) {
|
||||
if (type instanceof PsiClassType) {
|
||||
PsiClass resolvedParamClass = ((PsiClassType)type).resolve();
|
||||
if (resolvedParamClass != null && CommonClassNames.JAVA_LANG_ITERABLE.equals(resolvedParamClass.getQualifiedName())) {
|
||||
final PsiExpression newExpression =
|
||||
elementFactory.createExpressionFromText(expression.getText() + ".collect(java.util.stream.Collectors.toList())", null);
|
||||
return (PsiExpression) expression.replace(newExpression);
|
||||
}
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
private static boolean isIterableMethodParameter(PsiElement listExpression, PsiExpression parameterExpression) {
|
||||
if (!(listExpression instanceof PsiExpressionList)) {
|
||||
return false;
|
||||
}
|
||||
if (!(listExpression.getParent() instanceof PsiMethodCallExpression)) {
|
||||
return false;
|
||||
}
|
||||
final Project project = parameterExpression.getProject();
|
||||
final PsiClass fluentIterable = JavaPsiFacade.getInstance(project)
|
||||
.findClass(GuavaFluentIterableInspection.GUAVA_FLUENT_ITERABLE, GlobalSearchScope.allScope(project));
|
||||
return GuavaFluentIterableInspection.isMethodWithParamAcceptsConversion((PsiMethodCallExpression)listExpression.getParent(), parameterExpression, fluentIterable);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiMethodCallExpression convertMethodCallDeep(PsiElementFactory elementFactory,
|
||||
@NotNull PsiMethodCallExpression methodCall) {
|
||||
PsiMethodCallExpression newMethodCall = methodCall;
|
||||
PsiMethodCallExpression returnCall = null;
|
||||
while (true) {
|
||||
final Pair<PsiMethodCallExpression, Boolean> converted = convertMethodCall(elementFactory, newMethodCall);
|
||||
if (converted.getSecond()) {
|
||||
return returnCall;
|
||||
}
|
||||
if (returnCall == null) {
|
||||
returnCall = converted.getFirst();
|
||||
}
|
||||
if (converted.getFirst() == null) {
|
||||
return returnCall;
|
||||
}
|
||||
newMethodCall = converted.getFirst();
|
||||
final PsiExpression expression = newMethodCall.getMethodExpression().getQualifierExpression();
|
||||
if (expression instanceof PsiMethodCallExpression) {
|
||||
newMethodCall = (PsiMethodCallExpression)expression;
|
||||
}
|
||||
else {
|
||||
return returnCall;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Pair<PsiMethodCallExpression, Boolean> convertMethodCall(PsiElementFactory elementFactory, PsiMethodCallExpression methodCall) {
|
||||
final PsiReferenceExpression methodExpression = methodCall.getMethodExpression();
|
||||
final String name = methodExpression.getReferenceName();
|
||||
if (TO_OTHER_COLLECTION_METHODS.containsKey(name)) {
|
||||
return Pair.create(convertToCollection(methodCall, name, elementFactory), false);
|
||||
}
|
||||
else if (GuavaFluentIterableInspection.FLUENT_ITERABLE_FROM.equals(name)) {
|
||||
final PsiExpression[] argumentList = methodCall.getArgumentList().getExpressions();
|
||||
LOG.assertTrue(argumentList.length == 1);
|
||||
final PsiExpression expression = argumentList[0];
|
||||
|
||||
final PsiType type = expression.getType();
|
||||
LOG.assertTrue(type instanceof PsiClassType);
|
||||
final PsiClass resolvedClass = ((PsiClassType)type).resolve();
|
||||
final String newExpressionText;
|
||||
if (InheritanceUtil.isInheritor(resolvedClass, CommonClassNames.JAVA_UTIL_COLLECTION)) {
|
||||
newExpressionText = expression.getText() + ".stream()";
|
||||
} else {
|
||||
newExpressionText = "java.util.stream.StreamSupport.stream(" + expression.getText() + ".spliterator(), false)";
|
||||
}
|
||||
return Pair.create((PsiMethodCallExpression)methodCall.replace(elementFactory.createExpressionFromText(newExpressionText, null)), true);
|
||||
}
|
||||
else {
|
||||
final FluentIterableMethodTransformer transformer = METHOD_INDEX.get(name);
|
||||
LOG.assertTrue(transformer != null, name);
|
||||
final PsiMethodCallExpression transformedExpression = transformer.transform(methodCall, elementFactory);
|
||||
return Pair.create(transformedExpression, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static PsiMethodCallExpression convertToCollection(final PsiMethodCallExpression methodCall,
|
||||
final String methodName,
|
||||
final PsiElementFactory elementFactory) {
|
||||
final PsiExpression[] expressions = methodCall.getArgumentList().getExpressions();
|
||||
assert expressions.length < 2;
|
||||
String template = TO_OTHER_COLLECTION_METHODS.get(methodName);
|
||||
if (expressions.length == 1) {
|
||||
template = String.format(template, expressions[0].getText());
|
||||
}
|
||||
final PsiExpression qualifier = methodCall.getMethodExpression().getQualifierExpression();
|
||||
if (qualifier == null) {
|
||||
return null;
|
||||
}
|
||||
final String text = qualifier.getText() + "." + template;
|
||||
final PsiExpression expression = elementFactory.createExpressionFromText(text, null);
|
||||
return (PsiMethodCallExpression)methodCall.replace(expression);
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.intellij.codeInspection.java18StreamApi;
|
||||
|
||||
import com.intellij.codeInspection.AnonymousCanBeLambdaInspection;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class GuavaFunctionAndPredicateConverter {
|
||||
private final static Logger LOG = Logger.getInstance(GuavaFunctionAndPredicateConverter.class);
|
||||
|
||||
@Nullable
|
||||
public static Boolean isClassConditionPredicate(final PsiExpression expression) {
|
||||
final PsiType type = expression.getType();
|
||||
if (type instanceof PsiClassType) {
|
||||
final PsiClass resolvedClass = ((PsiClassType)type).resolve();
|
||||
if (InheritanceUtil.isInheritor(resolvedClass, "com.google.common.base.Function") ||
|
||||
InheritanceUtil.isInheritor(resolvedClass, "com.google.common.base.Predicate")) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
else if (InheritanceUtil.isInheritor(resolvedClass, CommonClassNames.JAVA_LANG_CLASS)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String convertFunctionOrPredicateParameter(final @NotNull PsiExpression expression,
|
||||
final boolean role) {
|
||||
if (role) {
|
||||
final String pattern = expression instanceof PsiMethodCallExpression || expression instanceof PsiReferenceExpression
|
||||
? "%s::isInstance"
|
||||
: "(%s)::isInstance";
|
||||
return String.format(pattern, expression.getText());
|
||||
}
|
||||
if (expression instanceof PsiNewExpression) {
|
||||
final PsiAnonymousClass anonymousClass = ((PsiNewExpression)expression).getAnonymousClass();
|
||||
if (anonymousClass != null && AnonymousCanBeLambdaInspection.canBeConvertedToLambda(anonymousClass, true)) {
|
||||
final PsiLambdaExpression lambdaExpression = AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, true);
|
||||
LOG.assertTrue(lambdaExpression != null);
|
||||
return lambdaExpression.getText();
|
||||
}
|
||||
}
|
||||
String qualifierText = expression.getText();
|
||||
if (!(expression instanceof PsiMethodCallExpression) && !(expression instanceof PsiReferenceExpression)) {
|
||||
qualifierText = "(" + qualifierText + ")";
|
||||
}
|
||||
return qualifierText + "::apply";
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.intellij.codeInspection.java18StreamApi;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.containers.hash.HashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Dmitry Batkovich
|
||||
*/
|
||||
public class GuavaOptionalConverter {
|
||||
private static final Logger LOG = Logger.getInstance(GuavaOptionalConverter.class);
|
||||
private static final Map<String, FluentIterableMethodTransformer> METHODS_CONVERSION = new HashMap<String, FluentIterableMethodTransformer>();
|
||||
private static final String OR_METHOD = "or";
|
||||
|
||||
static {
|
||||
METHODS_CONVERSION.put("isPresent", new FluentIterableMethodTransformer.ParameterlessMethodTransformer("isPresent"));
|
||||
METHODS_CONVERSION.put("get", new FluentIterableMethodTransformer.ParameterlessMethodTransformer("get"));
|
||||
METHODS_CONVERSION.put(OR_METHOD, new FluentIterableMethodTransformer.OneParameterMethodTransformer("orElse(%s)"));
|
||||
METHODS_CONVERSION.put("orNull", new FluentIterableMethodTransformer.ParameterlessMethodTransformer("orElse(null)"));
|
||||
}
|
||||
|
||||
public static boolean isConvertibleIfOption(PsiMethodCallExpression methodCallExpression) {
|
||||
final PsiMethod method = methodCallExpression.resolveMethod();
|
||||
if (method == null) {
|
||||
return false;
|
||||
}
|
||||
final PsiClass aClass = method.getContainingClass();
|
||||
if (aClass == null) {
|
||||
return false;
|
||||
}
|
||||
if (!GuavaFluentIterableInspection.GUAVA_OPTIONAL.equals(aClass.getQualifiedName())) {
|
||||
return true;
|
||||
}
|
||||
return METHODS_CONVERSION.containsKey(method.getName());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static PsiMethodCallExpression convertGuavaOptionalToJava(final PsiMethodCallExpression methodCall,
|
||||
final PsiElementFactory elementFactory) {
|
||||
final String methodName = methodCall.getMethodExpression().getReferenceName();
|
||||
if (methodName == OR_METHOD) {
|
||||
final PsiExpression[] arguments = methodCall.getArgumentList().getExpressions();
|
||||
if (arguments.length != 1) {
|
||||
return methodCall;
|
||||
}
|
||||
final PsiExpression argument = arguments[0];
|
||||
final PsiType type = argument.getType();
|
||||
if (type instanceof PsiClassType) {
|
||||
final PsiClass resolvedClass = ((PsiClassType)type).resolve();
|
||||
if (resolvedClass != null) {
|
||||
final String qName = resolvedClass.getQualifiedName();
|
||||
if (GuavaFluentIterableInspection.GUAVA_OPTIONAL.equals(qName) || "com.google.common.base.Supplier".equals(qName)) {
|
||||
return methodCall;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
final FluentIterableMethodTransformer conversion = METHODS_CONVERSION.get(methodName);
|
||||
if (conversion == null) {
|
||||
return methodCall;
|
||||
}
|
||||
final PsiMethodCallExpression transformed = conversion.transform(methodCall, elementFactory);
|
||||
LOG.assertTrue(transformed != null);
|
||||
return transformed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
Inspection detects most of cases when Guava's Fluent Iterable can be replaced by Java Stream API.
|
||||
</body>
|
||||
</html>
|
||||
@@ -740,7 +740,11 @@
|
||||
groupName="Performance issues" enabledByDefault="false" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.CollectionAddAllCanBeReplacedWithConstructorInspection"
|
||||
displayName="Collection.addAll() can be replaced with parametrized constructor"/>
|
||||
|
||||
<localInspection groupPath="Java" language="JAVA" shortName="GuavaFluentIterable"
|
||||
groupBundle="messages.InspectionsBundle"
|
||||
groupName="Java language level migration aids" enabledByDefault="false" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.java18StreamApi.GuavaFluentIterableInspection"
|
||||
displayName="Guava's FluentIterable can be replaced by java.util.stream.Stream"/>
|
||||
<intentionAction>
|
||||
<className>com.intellij.codeInsight.daemon.quickFix.RedundantLambdaParameterTypeIntention</className>
|
||||
<category>Java/Declaration</category>
|
||||
|
||||
Reference in New Issue
Block a user