/* * 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.refactoring.typeMigration.rules.guava; import com.intellij.codeInspection.AnonymousCanBeLambdaInspection; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.*; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTypesUtil; import com.intellij.refactoring.typeMigration.TypeEvaluator; import com.intellij.refactoring.typeMigration.inspections.GuavaConversionSettings; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.Set; /** * @author Dmitry Batkovich */ public class GuavaConversionUtil { private final static Logger LOG = Logger.getInstance(GuavaConversionUtil.class); @Nullable public static PsiType getFunctionReturnType(PsiExpression functionExpression) { if (functionExpression instanceof PsiFunctionalExpression) { return LambdaUtil.getFunctionalInterfaceReturnType((PsiFunctionalExpression)functionExpression); } PsiType currentType = functionExpression.getType(); if (currentType == null) return null; while (true) { if (LambdaUtil.isFunctionalType(currentType)) { return LambdaUtil.getFunctionalInterfaceReturnType(currentType); } final PsiType[] superTypes = currentType.getSuperTypes(); currentType = null; for (PsiType type : superTypes) { final PsiClass aClass = PsiTypesUtil.getPsiClass(type); if (aClass != null && InheritanceUtil.isInheritor(aClass, GuavaLambda.FUNCTION.getClassQName())) { currentType = type; break; } } if (currentType == null) { return null; } } } @NotNull public static PsiType addTypeParameters(@NotNull String baseClassQualifiedName, @Nullable PsiType type, @NotNull PsiElement context) { String parameterText = ""; if (type != null) { final String canonicalText = type.getCanonicalText(false); if (canonicalText.contains("<")) { parameterText = canonicalText.substring(canonicalText.indexOf('<')); } } return JavaPsiFacade.getElementFactory(context.getProject()).createTypeFromText(baseClassQualifiedName + parameterText, context); } public static boolean isJavaLambda(PsiElement element, TypeEvaluator evaluator) { if (element instanceof PsiLocalVariable) { return GuavaLambda.findJavaAnalogueFor(evaluator.getType(element)) != null; } 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 GuavaLambda.findJavaAnalogueFor(methodReturnType) != null; } else if (element instanceof PsiExpressionList) { final PsiElement parent = element.getParent(); if (parent instanceof PsiMethodCallExpression) { return evaluator.getType(parent) != null; } } return false; } public static PsiExpression adjustLambdaContainingExpression(PsiExpression expression, boolean insertTypeCase, PsiType targetType, @NotNull TypeEvaluator evaluator) { if (expression instanceof PsiNewExpression) { final PsiAnonymousClass anonymousClass = ((PsiNewExpression)expression).getAnonymousClass(); if (anonymousClass != null) { return convertAnonymousClass((PsiNewExpression)expression, anonymousClass, evaluator); } else { final GuavaLambda lambda = GuavaLambda.findFor(evaluator.evaluateType(expression)); if (lambda == null) { return expression; } else { final PsiExpression expressionWithMethodReference = addMethodReference(expression, lambda); if (insertTypeCase) { return adjustLambdaContainingExpression(expressionWithMethodReference, true, targetType, evaluator); } else { return expressionWithMethodReference; } } } } if (expression instanceof PsiMethodReferenceExpression) { final PsiExpression qualifier = ((PsiMethodReferenceExpression)expression).getQualifierExpression(); final PsiType evaluatedType = evaluator.evaluateType(qualifier); final GuavaLambda lambda = GuavaLambda.findJavaAnalogueFor(evaluatedType); if (lambda != null) { return adjustLambdaContainingExpression((PsiExpression)expression.replace(qualifier), insertTypeCase, targetType, evaluator); } } if (expression instanceof PsiFunctionalExpression) { if (insertTypeCase) { return JavaPsiFacade.getElementFactory(expression.getProject()).createExpressionFromText("((" + targetType.getCanonicalText() + ")" + expression.getText() + ")", expression); } } else if (expression instanceof PsiMethodCallExpression || expression instanceof PsiReferenceExpression) { final GuavaLambda lambda = GuavaLambda.findFor(evaluator.evaluateType(expression)); if (lambda != null) { expression = addMethodReference(expression, lambda); return adjustLambdaContainingExpression(expression, insertTypeCase, targetType, evaluator); } } return expression; } public static PsiExpression convertAnonymousClass(@NotNull PsiNewExpression expression, @NotNull PsiAnonymousClass anonymousClass, @NotNull TypeEvaluator typeEvaluator) { final GuavaConversionSettings settings = typeEvaluator.getSettings(GuavaConversionSettings.class); final Set<String> ignoredAnnotations = settings != null ? settings.getIgnoredAnnotations() : Collections.emptySet(); if (AnonymousCanBeLambdaInspection.canBeConvertedToLambda(anonymousClass, true, ignoredAnnotations)) { return AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, true, true); } else { return tryConvertClassAndSamNameToJava(expression); } } public static PsiExpression tryConvertClassAndSamNameToJava(PsiNewExpression expression) { final GuavaLambda lambda = GuavaLambda.findFor(expression.getType()); if (lambda == null) return expression; final PsiAnonymousClass aClass = expression.getAnonymousClass(); LOG.assertTrue(aClass != null); final PsiElementFactory factory = JavaPsiFacade.getElementFactory(expression.getProject()); if (!lambda.getSamName().equals(lambda.getJavaAnalogueSamName())) { boolean isFound = false; for (PsiMethod method : aClass.findMethodsByName(lambda.getSamName(), false)) { if (method.getParameterList().getParametersCount() == lambda.getParametersCount()) { for (PsiMethod psiMethod : method.findSuperMethods()) { final PsiClass superMethodContainingClass = psiMethod.getContainingClass(); if (superMethodContainingClass != null && lambda.getClassQName().equals(superMethodContainingClass.getQualifiedName())) { final PsiIdentifier methodNameIdentifier = method.getNameIdentifier(); LOG.assertTrue(methodNameIdentifier != null); methodNameIdentifier.replace(factory.createIdentifier(lambda.getJavaAnalogueSamName())); isFound = true; break; } } } if (isFound) break; } } final PsiElement currentClassName = aClass.getBaseClassReference().getReferenceNameElement(); if (currentClassName != null) { final PsiElement newNameElement = factory.createReferenceFromText(lambda.getJavaAnalogueClassQName(), null); currentClassName.replace(newNameElement); } return (PsiExpression)expression.replace(factory.createExpressionFromText(expression.getText(), null)); } private static PsiExpression addMethodReference(@NotNull PsiExpression expression, @NotNull GuavaLambda lambda) { return (PsiExpression)expression.replace(JavaPsiFacade.getElementFactory(expression.getProject()) .createExpressionFromText(expression.getText() + "::" + lambda.getSamName(), expression)); } }