guava type migration: quick fix improved to work in one write-action, support guava's Predicates

This commit is contained in:
Dmitry Batkovich
2015-12-23 16:15:47 +03:00
parent 573f2bc03d
commit 2abed8dc94
18 changed files with 306 additions and 262 deletions
@@ -54,11 +54,11 @@ public class TypeEvaluator {
if (types != null) {
for (final Pair<TypeMigrationUsageInfo, PsiType> p : types) {
final LinkedList<PsiType> e = new LinkedList<PsiType>();
e.addFirst(p.getSecond());
myTypeMap.put(p.getFirst(), e);
if (!(p.getFirst().getElement() instanceof PsiExpression)) {
final LinkedList<PsiType> e = new LinkedList<PsiType>();
e.addFirst(p.getSecond());
myTypeMap.put(p.getFirst(), e);
}
}
}
@@ -740,7 +740,7 @@ public class TypeMigrationLabeler {
}
if (myException != null) throw myException;
rememberRootTrace(usageInfo, type, place, alreadyProcessed);
if (!alreadyProcessed && !getTypeEvaluator().setType(usageInfo, type)) {
if (!alreadyProcessed && !(usageInfo.getElement() instanceof PsiExpression) && !getTypeEvaluator().setType(usageInfo, type)) {
alreadyProcessed = true;
}
@@ -853,6 +853,10 @@ public class TypeMigrationLabeler {
}
else if (root instanceof PsiVariable || root instanceof PsiExpression) {
final PsiElement element = getContainingStatement(root);
if (root instanceof PsiExpression) {
migrateExpressionType((PsiExpression)root, migrationType, element, false, true);
myTypeEvaluator.setType(newRootUsageInfo, migrationType);
}
element.accept(new TypeMigrationStatementProcessor(element, this));
}
else if (root instanceof PsiReferenceParameterList) {
@@ -968,6 +972,7 @@ public class TypeMigrationLabeler {
}
private void migrate(boolean autoMigrate, final PsiElement... victims) {
myMigrationRoots = new LinkedList<Pair<TypeMigrationUsageInfo, PsiType>>();
myTypeEvaluator = new TypeEvaluator(myMigrationRoots, this);
@@ -18,14 +18,12 @@ package com.intellij.refactoring.typeMigration.inspections;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.undo.UndoUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.AtomicNotNullLazyValue;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.util.PsiTreeUtil;
@@ -118,7 +116,7 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool {
if (targetType != null) {
holder.registerProblem(variable.getNameIdentifier(),
PROBLEM_DESCRIPTION_FOR_VARIABLE,
new MigrateGuavaTypeFix(variable, targetType, null));
new MigrateGuavaTypeFix(variable, targetType));
}
}
@@ -132,7 +130,7 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool {
if (typeElement != null) {
holder.registerProblem(typeElement,
PROBLEM_DESCRIPTION_FOR_VARIABLE,
new MigrateGuavaTypeFix(method, targetType, null));
new MigrateGuavaTypeFix(method, targetType));
}
}
}
@@ -144,11 +142,7 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool {
}
private void checkPredicatesUtilityMethod(PsiMethodCallExpression expression) {
final PsiElement parent = expression.getParent();
if (parent instanceof PsiAssignmentExpression || parent instanceof PsiVariable) {
return;
}
if (FunctionalInterfaceTypeConversionDescriptor.isPredicates(expression)) {
if (GuavaPredicateConversionRule.isPredicates(expression)) {
final PsiMethod method = expression.resolveMethod();
if (GuavaPredicateConversionRule.isConvertablePredicatesMethod(method)) {
final PsiClassType initialType = (PsiClassType)expression.getType();
@@ -156,7 +150,7 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool {
if (targetType == null) return;
holder.registerProblem(expression.getMethodExpression().getReferenceNameElement(),
PROBLEM_DESCRIPTION_FOR_VARIABLE,
new MigrateGuavaTypeFix(expression, targetType, initialType));
new MigrateGuavaTypeFix(expression, targetType));
}
}
}
@@ -170,21 +164,16 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool {
return;
}
final PsiElement maybeLocalVariable = chain.getParent();
if (maybeLocalVariable instanceof PsiLocalVariable) {
final PsiClass aClass = PsiUtil.resolveClassInType(((PsiLocalVariable)maybeLocalVariable).getType());
if (aClass != null && (GuavaFluentIterableConversionRule.FLUENT_ITERABLE.equals(aClass.getQualifiedName()) ||
GuavaOptionalConversionRule.GUAVA_OPTIONAL.equals(aClass.getQualifiedName()))) {
return;
}
}
PsiClassType initialType = (PsiClassType)expression.getType();
PsiClassType initialType = (PsiClassType)chain.getType();
LOG.assertTrue(initialType != null);
PsiClassType targetType = createTargetType(initialType);
if (targetType == null) return;
holder.registerProblem(chain, PROBLEM_DESCRIPTION_FOR_METHOD_CHAIN, new MigrateGuavaTypeFix(chain, targetType, initialType));
PsiElement highlightedElement = chain;
if (chain.getParent() instanceof PsiReferenceExpression && chain.getParent().getParent() instanceof PsiMethodCallExpression) {
highlightedElement = chain.getParent().getParent();
}
holder.registerProblem(highlightedElement, PROBLEM_DESCRIPTION_FOR_METHOD_CHAIN, new MigrateGuavaTypeFix(chain, targetType));
}
@Nullable
@@ -252,6 +241,15 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool {
|| GuavaOptionalConversionRule.GUAVA_OPTIONAL.equals(containingClass.getQualifiedName()))) {
return chain;
}
final PsiType returnType = method.getReturnType();
final PsiClass returnClass = PsiTypesUtil.getPsiClass(returnType);
if (returnClass == null || !(GuavaFluentIterableConversionRule.FLUENT_ITERABLE.equals(returnClass.getQualifiedName())
|| GuavaOptionalConversionRule.GUAVA_OPTIONAL.equals(returnClass.getQualifiedName()))) {
return chain;
}
if (GuavaTypeConversionDescriptor.isIterable(current)) {
return chain;
}
}
else {
return chain;
@@ -283,14 +281,11 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool {
}
public static class MigrateGuavaTypeFix extends LocalQuickFixAndIntentionActionOnPsiElement implements BatchQuickFix<ProblemDescriptor> {
@Nullable
private final PsiType myInitialType;
private final PsiType myTargetType;
private MigrateGuavaTypeFix(@NotNull PsiElement element, PsiType targetType, @Nullable PsiType initialType) {
private MigrateGuavaTypeFix(@NotNull PsiElement element, PsiType targetType) {
super(element);
myTargetType = targetType;
myInitialType = initialType;
}
@Override
@@ -299,11 +294,7 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool {
@Nullable("is null when called from inspection") Editor editor,
@NotNull PsiElement startElement,
@NotNull PsiElement endElement) {
if (myInitialType == null) {
performTypeMigration(Collections.singletonList(startElement), Collections.singletonList(myTargetType));
} else {
performMethodCallTypeMigration(project, Collections.singletonList(new ChainFixInfo(startElement, myInitialType, myTargetType)));
}
performTypeMigration(Collections.singletonList(startElement), Collections.singletonList(myTargetType));
}
@Override
@@ -342,118 +333,15 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool {
final List<PsiElement> elementsToFix = new ArrayList<PsiElement>();
final List<PsiType> migrationTypes = new ArrayList<PsiType>();
final List<ChainFixInfo> chainFixInfos = new ArrayList<ChainFixInfo>();
for (ProblemDescriptor descriptor : descriptors) {
final MigrateGuavaTypeFix fix = getFix(descriptor);
if (fix.myInitialType == null) {
elementsToFix.add(fix.getStartElement());
migrationTypes.add(fix.myTargetType);
}
else {
chainFixInfos.add(new ChainFixInfo(fix.getStartElement(), fix.myInitialType, fix.myTargetType));
}
elementsToFix.add(fix.getStartElement());
migrationTypes.add(fix.myTargetType);
}
if (!chainFixInfos.isEmpty()) performMethodCallTypeMigration(project, chainFixInfos);
if (!elementsToFix.isEmpty()) performTypeMigration(elementsToFix, migrationTypes);
}
private static void performMethodCallTypeMigration(@NotNull final Project project,
final List<ChainFixInfo> elements) {
Collections.sort(elements, new Comparator<ChainFixInfo>() {
@Override
public int compare(ChainFixInfo o1, ChainFixInfo o2) {
final PsiElement element1 = o1.myElement;
final PsiElement element2 = o2.myElement;
if (element1.getTextRange().contains(element2.getTextRange())) {
return 1;
}
if (element2.getTextRange().contains(element1.getTextRange())) {
return -1;
}
return 0;
}
});
final List<PsiElement> validElement = new ArrayList<PsiElement>();
final List<Boolean> isIterableList = new ArrayList<Boolean>(elements.size());
final List<TypeConversionDescriptorBase> conversionList = new ArrayList<TypeConversionDescriptorBase>(elements.size());
for (ChainFixInfo info : elements) {
final PsiElement element = info.myElement;
final PsiType initialType = info.myFrom;
final PsiType targetType = info.myTo;
if (element.isValid()) {
PsiMethodCallExpression expr = (PsiMethodCallExpression)element;
final TypeMigrationRules rules = new TypeMigrationRules();
rules.setBoundScope(element.getUseScope());
conversionList.add(rules.findConversion(initialType, targetType, expr.resolveMethod(), expr, new TypeMigrationLabeler(rules, targetType)));
isIterableList.add(isIterable(expr));
validElement.add(element);
}
}
if (!validElement.isEmpty()) {
final PsiFile file = validElement.get(0).getContainingFile();
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
final Iterator<Boolean> isIterableIterator = isIterableList.iterator();
final Iterator<TypeConversionDescriptorBase> conversionIterator = conversionList.iterator();
final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
for (PsiElement element : validElement) {
PsiElement replacedExpression = TypeMigrationReplacementUtil.replaceExpression((PsiExpression)element,
project,
conversionIterator.next(),
new TypeEvaluator(null, null));
if (isIterableIterator.next()) {
final String expressionText = replacedExpression.getText() + ".collect(java.util.stream.Collectors.toList())";
replacedExpression = replacedExpression
.replace(JavaPsiFacade.getElementFactory(project).createExpressionFromText(expressionText, replacedExpression));
}
codeStyleManager.shortenClassReferences(replacedExpression);
}
codeStyleManager.optimizeImports(file);
UndoUtil.markPsiFileForUndo(file);
}
});
}
}
private static boolean isIterable(PsiMethodCallExpression expression) {
final PsiElement parent = expression.getParent();
final PsiMethod method = expression.resolveMethod();
if (method != null) {
final PsiClass returnClass = PsiTypesUtil.getPsiClass(method.getReturnType());
if (returnClass == null || !GuavaFluentIterableConversionRule.FLUENT_ITERABLE.equals(returnClass.getQualifiedName())) {
return false;
}
}
if (parent instanceof PsiLocalVariable) {
return isIterable(((PsiLocalVariable)parent).getType());
}
else if (parent instanceof PsiReturnStatement) {
final PsiElement methodOrLambda = PsiTreeUtil.getParentOfType(parent, PsiMethod.class, PsiLambdaExpression.class);
PsiType methodReturnType = null;
if (methodOrLambda instanceof PsiMethod) {
methodReturnType = ((PsiMethod)methodOrLambda).getReturnType();
}
else if (methodOrLambda instanceof PsiLambdaExpression) {
methodReturnType = LambdaUtil.getFunctionalInterfaceReturnType((PsiFunctionalExpression)methodOrLambda);
}
return isIterable(methodReturnType);
}
return false;
}
private static boolean isIterable(@Nullable PsiType type) {
PsiClass aClass;
return (aClass = PsiTypesUtil.getPsiClass(type)) != null && CommonClassNames.JAVA_LANG_ITERABLE.equals(aClass.getQualifiedName());
}
private static MigrateGuavaTypeFix getFix(ProblemDescriptor descriptor) {
final QuickFix[] fixes = descriptor.getFixes();
LOG.assertTrue(fixes != null);
@@ -513,17 +401,5 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool {
}
};
}
private static class ChainFixInfo {
private final PsiElement myElement;
private final PsiType myFrom;
private final PsiType myTo;
private ChainFixInfo(PsiElement element, PsiType from, PsiType to) {
myElement = element;
myFrom = from;
myTo = to;
}
}
}
}
@@ -122,12 +122,12 @@ public class FluentIterableConversionUtil {
return new GuavaFilterInstanceOfConversionDescriptor();
}
else if (GuavaPredicateConversionRule.GUAVA_PREDICATE.equals(resolvedClass.getQualifiedName())) {
return new LambdaParametersTypeConversionDescriptor("$it$.filter($p$)", "$it$." + StreamApiConstants.FILTER + "($p$)");
return new GuavaTypeConversionDescriptor("$it$.filter($p$)", "$it$." + StreamApiConstants.FILTER + "($p$)");
}
return null;
}
static class TransformAndConcatConversionRule extends LambdaParametersTypeConversionDescriptor {
static class TransformAndConcatConversionRule extends GuavaTypeConversionDescriptor {
public TransformAndConcatConversionRule() {
super("$q$.transformAndConcat($params$)", "$q$.flatMap($params$)");
}
@@ -248,8 +248,8 @@ public class FluentIterableConversionUtil {
final String replaceTemplate;
final String returnType;
if ("toMap".equals(methodName) || "uniqueIndex".equals(methodName)) {
final LambdaParametersTypeConversionDescriptor descriptor = new LambdaParametersTypeConversionDescriptor("$it$.$methodName$($f$)",
"$it$.collect(java.util.stream.Collectors.toMap(java.util.function.Function.identity(), $f$))");
final GuavaTypeConversionDescriptor descriptor = new GuavaTypeConversionDescriptor("$it$.$methodName$($f$)",
"$it$.collect(java.util.stream.Collectors.toMap(java.util.function.Function.identity(), $f$))");
return descriptor.withConversionType(GuavaConversionUtil.addTypeParameters(CommonClassNames.JAVA_UTIL_MAP, context.getType(), context));
}
else if ("toList".equals(methodName)) {
@@ -16,8 +16,10 @@
package com.intellij.refactoring.typeMigration.rules.guava;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptor;
import com.intellij.refactoring.typeMigration.TypeEvaluator;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import org.jetbrains.annotations.NotNull;
/**
@@ -26,11 +28,15 @@ import org.jetbrains.annotations.NotNull;
public class FunctionalInterfaceTypeConversionDescriptor extends TypeConversionDescriptor {
@NotNull private final String myMethodName;
@NotNull private final String myTargetMethodName;
@NotNull private final String myTargetClassQName;
FunctionalInterfaceTypeConversionDescriptor(@NotNull String methodName, @NotNull String targetMethodName) {
FunctionalInterfaceTypeConversionDescriptor(@NotNull String methodName,
@NotNull String targetMethodName,
@NotNull String targetClassQName) {
super(null, null);
myMethodName = methodName;
myTargetMethodName = targetMethodName;
myTargetClassQName = targetClassQName;
}
@Override
@@ -39,49 +45,47 @@ public class FunctionalInterfaceTypeConversionDescriptor extends TypeConversionD
expression = (PsiExpression)expression.getParent();
}
if (expression instanceof PsiMethodReferenceExpression) {
setAsMethodReference((PsiMethodReferenceExpression)expression);
expression = setupAsMethodReference(expression);
}
else if (expression instanceof PsiReferenceExpression) {
setAsReference();
setupAsReference();
}
else {
setAsMethodCall();
setupAsMethodCall();
}
return super.replace(expression, evaluator);
final PsiExpression converted = super.replace(expression, evaluator);
final PsiElement parent = converted.getParent();
if (parent instanceof PsiParenthesizedExpression) {
if (!ParenthesesUtils.areParenthesesNeeded((PsiParenthesizedExpression)parent, true)) {
return (PsiExpression)parent.replace(converted);
}
}
return converted;
}
private void setAsReference() {
private void setupAsReference() {
setStringToReplace("$ref$");
setReplaceByString("$ref$::" + myTargetMethodName);
}
private void setAsMethodReference(PsiMethodReferenceExpression methodReference) {
setStringToReplace("$qualifier$::" + myMethodName);
if (methodReference.getParent() instanceof PsiExpressionList &&
methodReference.getParent().getParent() instanceof PsiMethodCallExpression &&
isPredicates((PsiMethodCallExpression)methodReference.getParent().getParent())) {
setReplaceByString("$qualifier$::" + myTargetMethodName);
return;
private PsiExpression setupAsMethodReference(PsiExpression methodReferenceExpression) {
final PsiElement parent = methodReferenceExpression.getParent();
if (parent instanceof PsiTypeCastExpression) {
final PsiTypeElement typeElement = ((PsiTypeCastExpression)parent).getCastType();
if (typeElement != null) {
final PsiClass resolvedClass = PsiTypesUtil.getPsiClass(typeElement.getType());
if (resolvedClass != null && myTargetClassQName.equals(resolvedClass.getQualifiedName())) {
methodReferenceExpression = (PsiExpression)parent.replace(methodReferenceExpression);
}
}
}
setStringToReplace("$qualifier$::" + myMethodName);
setReplaceByString("$qualifier$");
return methodReferenceExpression;
}
private void setAsMethodCall() {
private void setupAsMethodCall() {
setStringToReplace("$qualifier$." + myMethodName + "($param$)");
setReplaceByString("$qualifier$." + myTargetMethodName + "($param$)");
}
public static boolean isPredicates(PsiMethodCallExpression expression) {
final String methodName = expression.getMethodExpression().getReferenceName();
if (GuavaPredicateConversionRule.PREDICATES_NOT.equals(methodName) ||
GuavaPredicateConversionRule.PREDICATES_AND_OR.contains(methodName)) {
final PsiMethod method = expression.resolveMethod();
if (method == null) return false;
final PsiClass aClass = method.getContainingClass();
if (aClass != null && GuavaPredicateConversionRule.GUAVA_PREDICATES_UTILITY.equals(aClass.getQualifiedName())) {
return true;
}
}
return false;
}
}
@@ -77,8 +77,11 @@ public class GuavaFluentIterableConversionRule extends BaseGuavaTypeConversionRu
}
public TypeConversionDescriptor create() {
return myWithLambdaParameter ? new LambdaParametersTypeConversionDescriptor(myStringToReplace, myReplaceByString)
: new TypeConversionDescriptor(myStringToReplace, myReplaceByString);
GuavaTypeConversionDescriptor descriptor = new GuavaTypeConversionDescriptor(myStringToReplace, myReplaceByString);
if (!myWithLambdaParameter) {
descriptor = descriptor.setConvertParameterAsLambda(false);
}
return descriptor;
}
public boolean isChainedMethod() {
@@ -225,7 +228,8 @@ public class GuavaFluentIterableConversionRule extends BaseGuavaTypeConversionRu
if (descriptorBase != null) {
if (needSpecifyType) {
if (conversionType == null) {
conversionType = GuavaConversionUtil.addTypeParameters(StreamApiConstants.JAVA_UTIL_STREAM_STREAM, context.getType(), context);
PsiMethodCallExpression methodCall = (PsiMethodCallExpression) (context instanceof PsiMethodCallExpression ? context : context.getParent());
conversionType = GuavaConversionUtil.addTypeParameters(GuavaTypeConversionDescriptor.isIterable(methodCall) ? CommonClassNames.JAVA_LANG_ITERABLE : StreamApiConstants.JAVA_UTIL_STREAM_STREAM, context.getType(), context);
}
descriptorBase.withConversionType(conversionType);
}
@@ -34,14 +34,14 @@ public class GuavaFunctionConversionRule extends BaseGuavaTypeConversionRule {
@Override
protected void fillSimpleDescriptors(Map<String, TypeConversionDescriptorBase> descriptorsMap) {
descriptorsMap.put("apply", new FunctionalInterfaceTypeConversionDescriptor("apply", "apply"));
descriptorsMap.put("apply", new FunctionalInterfaceTypeConversionDescriptor("apply", "apply", JAVA_UTIL_FUNCTION_FUNCTION));
}
@Nullable
@Override
protected TypeConversionDescriptorBase findConversionForVariableReference(@NotNull PsiReferenceExpression referenceExpression,
@NotNull PsiVariable psiVariable, PsiExpression context) {
return new FunctionalInterfaceTypeConversionDescriptor("apply", "apply");
return new FunctionalInterfaceTypeConversionDescriptor("apply", "apply", JAVA_UTIL_FUNCTION_FUNCTION);
}
@NotNull
@@ -74,7 +74,7 @@ public class GuavaOptionalConversionRule extends BaseGuavaTypeConversionRule {
return descriptor;
}
return GuavaSupplierConversionRule.GUAVA_SUPPLIER.equals(qName)
? new LambdaParametersTypeConversionDescriptor("$val$.or($other$)", "$val$.orElseGet($other$)")
? new GuavaTypeConversionDescriptor("$val$.or($other$)", "$val$.orElseGet($other$)")
: new TypeConversionDescriptor("$val$.or($other$)", "$val$.orElse($other$)");
}
return null;
@@ -86,7 +86,7 @@ public class GuavaOptionalConversionRule extends BaseGuavaTypeConversionRule {
return null;
}
final PsiExpression functionArgument = arguments[0];
final TypeConversionDescriptor descriptor = new LambdaParametersTypeConversionDescriptor("$val$.transform($fun$)", "$val$.map($fun$)");
final TypeConversionDescriptor descriptor = new GuavaTypeConversionDescriptor("$val$.transform($fun$)", "$val$.map($fun$)");
final PsiType typeParameter = GuavaConversionUtil.getFunctionReturnType(functionArgument);
if (typeParameter == null) {
return descriptor;
@@ -17,6 +17,7 @@ package com.intellij.refactoring.typeMigration.rules.guava;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.RedundantCastUtil;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase;
@@ -54,14 +55,14 @@ public class GuavaPredicateConversionRule extends BaseGuavaTypeConversionRule {
@Override
protected void fillSimpleDescriptors(Map<String, TypeConversionDescriptorBase> descriptorsMap) {
descriptorsMap.put("apply", new FunctionalInterfaceTypeConversionDescriptor("apply", "test"));
descriptorsMap.put("apply", new FunctionalInterfaceTypeConversionDescriptor("apply", "test", JAVA_PREDICATE));
}
@Nullable
@Override
protected TypeConversionDescriptorBase findConversionForVariableReference(@NotNull PsiReferenceExpression referenceExpression,
@NotNull PsiVariable psiVariable, PsiExpression context) {
return new FunctionalInterfaceTypeConversionDescriptor("apply", "test");
return new FunctionalInterfaceTypeConversionDescriptor("apply", "test", JAVA_PREDICATE);
}
@Nullable
@@ -127,6 +128,14 @@ public class GuavaPredicateConversionRule extends BaseGuavaTypeConversionRule {
public PsiExpression replace(PsiExpression expression, @NotNull TypeEvaluator evaluator) throws IncorrectOperationException {
String newExpressionString =
adjust(((PsiMethodCallExpression)expression).getArgumentList().getExpressions()[0], true, myTargetType, evaluator) + ".negate()";
final PsiElement parent = expression.getParent();
if (parent instanceof PsiMethodReferenceExpression) {
expression = replaceTypeCast(expression, parent);
}
else if (!isJavaPredicate(parent, evaluator)) {
newExpressionString += "::test";
}
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(expression.getProject());
PsiExpression convertedExpression =
(PsiExpression)expression.replace(elementFactory.createExpressionFromText(newExpressionString, expression));
@@ -165,9 +174,56 @@ public class GuavaPredicateConversionRule extends BaseGuavaTypeConversionRule {
replaceBy.append(".").append(methodName).append("(").append(adjust(argument, false, myTargetType, evaluator)).append(")");
}
replaceBy.insert(0, adjust(arguments[0], true, myTargetType, evaluator));
final PsiElement parent = expression.getParent();
if (parent instanceof PsiMethodReferenceExpression) {
expression = replaceTypeCast(expression, parent);
}
else if (!isJavaPredicate(parent, evaluator)) {
replaceBy.append("::test");
}
return (PsiExpression)expression.replace(elementFactory.createExpressionFromText(replaceBy.toString(), expression));
}
}
private static PsiExpression replaceTypeCast(PsiExpression expression, PsiElement parent) {
final PsiElement parParent = parent.getParent();
if (parParent instanceof PsiTypeCastExpression) {
final PsiTypeElement typeElement = ((PsiTypeCastExpression)parParent).getCastType();
if (typeElement != null) {
final PsiType type = typeElement.getType();
final PsiClass aClass = PsiTypesUtil.getPsiClass(type);
if (aClass != null && JAVA_PREDICATE.equals(aClass.getQualifiedName())) {
expression = (PsiExpression)parParent.replace(expression);
}
}
}
return expression;
}
public static boolean isJavaPredicate(PsiElement element, TypeEvaluator evaluator) {
if (element instanceof PsiLocalVariable) {
return isJavaPredicate(evaluator.getType(element));
}
else if (element instanceof PsiReturnStatement) {
final PsiElement methodOrLambda = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiLambdaExpression.class);
PsiType methodReturnType = null;
if (methodOrLambda instanceof PsiMethod) {
methodReturnType = evaluator.getType(methodOrLambda);
}
return isJavaPredicate(methodReturnType);
}
else if (element instanceof PsiExpressionList) {
final PsiElement parent = element.getParent();
if (parent instanceof PsiMethodCallExpression) {
return evaluator.getType(parent) != null;
}
}
return false;
}
private static boolean isJavaPredicate(@Nullable PsiType type) {
PsiClass aClass;
return (aClass = PsiTypesUtil.getPsiClass(type)) != null && JAVA_PREDICATE.equals(aClass.getQualifiedName());
}
private static boolean isUnconverted(PsiType type) {
@@ -212,4 +268,18 @@ public class GuavaPredicateConversionRule extends BaseGuavaTypeConversionRule {
public String ruleToClass() {
return JAVA_PREDICATE;
}
public static boolean isPredicates(PsiMethodCallExpression expression) {
final String methodName = expression.getMethodExpression().getReferenceName();
if (PREDICATES_NOT.equals(methodName) ||
PREDICATES_AND_OR.contains(methodName)) {
final PsiMethod method = expression.resolveMethod();
if (method == null) return false;
final PsiClass aClass = method.getContainingClass();
if (aClass != null && GUAVA_PREDICATES_UTILITY.equals(aClass.getQualifiedName())) {
return true;
}
}
return false;
}
}
@@ -34,14 +34,14 @@ public class GuavaSupplierConversionRule extends BaseGuavaTypeConversionRule {
@Override
protected void fillSimpleDescriptors(Map<String, TypeConversionDescriptorBase> descriptorsMap) {
descriptorsMap.put("get", new FunctionalInterfaceTypeConversionDescriptor("get", "get"));
descriptorsMap.put("get", new FunctionalInterfaceTypeConversionDescriptor("get", "get", JAVA_SUPPLIER));
}
@Nullable
@Override
protected TypeConversionDescriptorBase findConversionForVariableReference(@NotNull PsiReferenceExpression referenceExpression,
@NotNull PsiVariable psiVariable, PsiExpression context) {
return new FunctionalInterfaceTypeConversionDescriptor("get", "get");
return new FunctionalInterfaceTypeConversionDescriptor("get", "get", JAVA_SUPPLIER);
}
@NotNull
@@ -21,31 +21,43 @@ import com.intellij.openapi.util.Comparing;
import com.intellij.psi.*;
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.refactoring.typeMigration.TypeConversionDescriptor;
import com.intellij.refactoring.typeMigration.TypeEvaluator;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
/**
* @author Dmitry Batkovich
*/
class LambdaParametersTypeConversionDescriptor extends TypeConversionDescriptor {
private static final Logger LOG = Logger.getInstance(LambdaParametersTypeConversionDescriptor.class);
public class GuavaTypeConversionDescriptor extends TypeConversionDescriptor {
private static final Logger LOG = Logger.getInstance(GuavaTypeConversionDescriptor.class);
private final String myReplaceByStringSource;
private boolean myConvertParameterAsLambda = true;
LambdaParametersTypeConversionDescriptor(@NonNls String stringToReplace, @NonNls String replaceByString) {
GuavaTypeConversionDescriptor(@NonNls String stringToReplace, @NonNls String replaceByString) {
super(stringToReplace, replaceByString);
myReplaceByStringSource = replaceByString;
}
public GuavaTypeConversionDescriptor setConvertParameterAsLambda(boolean convertParameterAsLambda) {
myConvertParameterAsLambda = convertParameterAsLambda;
return this;
}
@Override
public PsiExpression replace(PsiExpression expression, TypeEvaluator evaluator) throws IncorrectOperationException {
LOG.assertTrue(expression instanceof PsiMethodCallExpression);
LOG.assertTrue(expression instanceof PsiMethodCallExpression);
PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression;
final PsiExpression[] arguments = methodCall.getArgumentList().getExpressions();
if (arguments.length == 1) {
final PsiExpression functionArg = arguments[0];
customizeParameter(convertParameter(functionArg, evaluator));
setReplaceByString(myReplaceByStringSource + (isIterable(methodCall) ? ".collect(java.util.stream.Collectors.toList())" : ""));
if (myConvertParameterAsLambda) {
final PsiExpression[] arguments = methodCall.getArgumentList().getExpressions();
if (arguments.length == 1) {
final PsiExpression functionArg = arguments[0];
customizeParameter(convertParameter(functionArg, evaluator));
}
}
return super.replace(expression, evaluator);
}
@@ -107,4 +119,28 @@ class LambdaParametersTypeConversionDescriptor extends TypeConversionDescriptor
}
return expression;
}
public static boolean isIterable(PsiMethodCallExpression expression) {
final PsiElement parent = expression.getParent();
if (parent instanceof PsiLocalVariable) {
return isIterable(((PsiLocalVariable)parent).getType());
}
else if (parent instanceof PsiReturnStatement) {
final PsiElement methodOrLambda = PsiTreeUtil.getParentOfType(parent, PsiMethod.class, PsiLambdaExpression.class);
PsiType methodReturnType = null;
if (methodOrLambda instanceof PsiMethod) {
methodReturnType = ((PsiMethod)methodOrLambda).getReturnType();
}
else if (methodOrLambda instanceof PsiLambdaExpression) {
methodReturnType = LambdaUtil.getFunctionalInterfaceReturnType((PsiFunctionalExpression)methodOrLambda);
}
return isIterable(methodReturnType);
}
return false;
}
private static boolean isIterable(@Nullable PsiType type) {
PsiClass aClass;
return (aClass = PsiTypesUtil.getPsiClass(type)) != null && CommonClassNames.JAVA_LANG_ITERABLE.equals(aClass.getQualifiedName());
}
}
@@ -79,7 +79,7 @@ public class GuavaInspectionTest extends JavaCodeInsightFixtureTestCase {
}
public void testFluentIterableChainWithoutVariable() {
doTest();
doTestAllFile();;
}
public void testChainedFluentIterableWithChainedInitializer() {
@@ -114,10 +114,6 @@ public class GuavaInspectionTest extends JavaCodeInsightFixtureTestCase {
doTest();
}
public void testDontShowFluentIterableChainQuickFix() {
doTestNoQuickFixes(PsiMethodCallExpression.class);
}
public void testRemoveMethodReferenceForFunctionalInterfaces() {
doTest();
}
@@ -257,6 +253,10 @@ public class GuavaInspectionTest extends JavaCodeInsightFixtureTestCase {
doTestAllFile();
}
public void testPredicates3() {
doTestAllFile();
}
private void doTestNoQuickFixes(Class<? extends PsiElement>... highlightedElements) {
myFixture.configureByFile(getTestName(true) + ".java");
myFixture.enableInspections(new GuavaInspection());
@@ -1,10 +0,0 @@
import com.google.common.collect.FluentIterable;
import java.util.ArrayList;
class A {
void c() {
ArrayList<String> strings = new ArrayList<String>();
FluentIterable<String> it = FluentIte<caret>rable.from(strings).transform(String::trim);
System.out.println(it.size());
}
}
@@ -1,25 +1,25 @@
/*
* 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.
*/
import java.util.*;
import com.google.common.collect.FluentIterable;
class A {
int m1() {
ArrayList<String> strings = new ArrayList<String>();
int size = FluentIterable.fro<caret>m(strings).transform(s -> s + s).limit(10).size();
return size
import java.util.ArrayList;
import java.util.Iterator;
class Main {
void mmm() {
Iterator<String> iterator = m().iterator();
int i = m1() + 10;
FluentIterable<String> strings = m2();
}
Iterable<String> m() {
return FluentIterable.from(new ArrayList<String>()).transform(s -> s + s).filter(String::isEmpty);
}
int m1() {
return FluentIterable.from(new ArrayList<String>()).transform(s -> s + s).size();
}
FluentIterable<String> m2() {
return FluentIterable.from(new ArrayList<String>()).transform(s -> s + s).filter(String::isEmpty);
}
}
@@ -1,25 +1,25 @@
/*
* 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.
*/
import java.util.ArrayList;
import java.util.Iterator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class A {
int m1() {
ArrayList<String> strings = new ArrayList<String>();
int size = (int) strings.stream().map(s -> s + s).limit(10).count();
return size
class Main {
void mmm() {
Iterator<String> iterator = m().iterator();
int i = m1() + 10;
Stream<String> strings = m2();
}
Iterable<String> m() {
return new ArrayList<String>().stream().map(s -> s + s).filter(String::isEmpty).collect(Collectors.toList());
}
int m1() {
return (int) new ArrayList<String>().stream().map(s -> s + s).count();
}
Stream<String> m2() {
return new ArrayList<String>().stream().map(s -> s + s).filter(String::isEmpty);
}
}
@@ -10,7 +10,7 @@ class Main {
Predicate<String> not1 = p1.negate();
Predicate<String> not2 = ((Predicate<String>) p1).or(((Predicate<String>) p1).or(p2)).negate();
Predicate<String> not2 = p1.or(p1.or(p2)).negate();
}
@@ -0,0 +1,30 @@
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import java.util.ArrayList;
class Main {
void m(Predicate<String> p1, Predicate<String> p2) {
Predicate<String> p = Predicates.and(p1, p2);
}
Predicate<String> m123(Predicate<String> p1, Predicate<String> p2) {
return Predicates.and(p1, p2);
}
void ml(Predicate<String> p1, Predicate<String> p2) {
Predicate<String> p = Predicates.and(p1, Predicates.not(p2));
}
void mll(Predicate<String> p1, Predicate<String> p2) {
Predicate<String> p = Predicates.and(p1, Predicates.not(Predicates.or(p2, p1)));
}
void mlll(Predicate<String> p1, Predicate<String> p2) {
FluentIterable<String> fi = FluentIterable.from(new ArrayList<>());
Iterable<String> ooooooo = fi.filter(Predicates.and(p1, Predicates.not(Predicates.or(p1, p2))));
Iterable<String> ooooooo1 = fi.filter(p2);
}
}
@@ -0,0 +1,29 @@
import java.util.ArrayList;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class Main {
void m(Predicate<String> p1, Predicate<String> p2) {
Predicate<String> p = p1.and(p2);
}
Predicate<String> m123(Predicate<String> p1, Predicate<String> p2) {
return p1.and(p2);
}
void ml(Predicate<String> p1, Predicate<String> p2) {
Predicate<String> p = p1.and(p2.negate());
}
void mll(Predicate<String> p1, Predicate<String> p2) {
Predicate<String> p = p1.and(p2.or(p1).negate());
}
void mlll(Predicate<String> p1, Predicate<String> p2) {
Stream<String> fi = new ArrayList<String>().stream();
Iterable<String> ooooooo = fi.filter(p1.and(p1.or(p2).negate())).collect(Collectors.toList());
Iterable<String> ooooooo1 = fi.filter(p2).collect(Collectors.toList());
}
}