blob: f26a41771228c091328c0de05e40fc7ec630bace [file] [log] [blame]
/*
* Copyright 2011 Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.siyeh.ipp.types;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.SuggestedNameInfo;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.RedundantCastUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.siyeh.ipp.base.Intention;
import com.siyeh.ipp.base.PsiElementPredicate;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Map;
public class ReplaceMethodRefWithLambdaIntention extends Intention {
private static final Logger LOG = Logger.getInstance("#" + ReplaceMethodRefWithLambdaIntention.class.getName());
@NotNull
@Override
protected PsiElementPredicate getElementPredicate() {
return new MethodRefPredicate();
}
@Override
protected void processIntention(@NotNull PsiElement element) throws IncorrectOperationException {
final PsiMethodReferenceExpression referenceExpression = PsiTreeUtil.getParentOfType(element, PsiMethodReferenceExpression.class);
LOG.assertTrue(referenceExpression != null);
final PsiElement resolve = referenceExpression.resolve();
final boolean isReceiver = resolve instanceof PsiMethod && PsiMethodReferenceUtil.hasReceiver(referenceExpression, (PsiMethod)resolve);
final PsiParameter[] psiParameters = resolve instanceof PsiMethod ? ((PsiMethod)resolve).getParameterList().getParameters() : null;
final PsiType functionalInterfaceType = referenceExpression.getFunctionalInterfaceType();
final PsiClassType.ClassResolveResult functionalInterfaceResolveResult = PsiUtil.resolveGenericsClassInType(functionalInterfaceType);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(functionalInterfaceType);
LOG.assertTrue(interfaceMethod != null);
final PsiSubstitutor psiSubstitutor = LambdaUtil.getSubstitutor(interfaceMethod, functionalInterfaceResolveResult);
final StringBuilder buf = new StringBuilder("(");
LOG.assertTrue(functionalInterfaceType != null);
buf.append(functionalInterfaceType.getCanonicalText()).append(")(");
final PsiParameterList parameterList = interfaceMethod.getParameterList();
final PsiParameter[] parameters = parameterList.getParameters();
final Map<PsiParameter, String> map = new HashMap<PsiParameter, String>();
final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(element.getProject());
final String paramsString = StringUtil.join(parameters, new Function<PsiParameter, String>() {
@Override
public String fun(PsiParameter parameter) {
final int parameterIndex = parameterList.getParameterIndex(parameter);
String baseName;
if (isReceiver && parameterIndex == 0) {
final SuggestedNameInfo nameInfo = codeStyleManager.suggestVariableName(VariableKind.PARAMETER, null, null, psiSubstitutor.substitute(parameter.getType()));
baseName = nameInfo.names.length > 0 ? nameInfo.names[0] : parameter.getName();
}
else {
final String initialName = psiParameters != null ? psiParameters[parameterIndex - (isReceiver ? 1 : 0)].getName() : parameter.getName();
baseName = codeStyleManager.variableNameToPropertyName(initialName, VariableKind.PARAMETER);
}
if (baseName != null) {
String parameterName = codeStyleManager.suggestUniqueVariableName(baseName, referenceExpression, true);
map.put(parameter, parameterName);
return parameterName;
}
return "";
}
}, ", ");
buf.append(paramsString);
buf.append(") -> ");
final JavaResolveResult resolveResult = referenceExpression.advancedResolve(false);
final PsiElement resolveElement = resolveResult.getElement();
if (resolveElement instanceof PsiMember) {
boolean needBraces = interfaceMethod.getReturnType() == PsiType.VOID && !(resolveElement instanceof PsiMethod && ((PsiMethod)resolveElement).getReturnType() == PsiType.VOID);
if (needBraces) {
buf.append("{");
}
final PsiElement qualifier = referenceExpression.getQualifier();
PsiClass containingClass = null;
if (resolveElement instanceof PsiMethod) {
containingClass = ((PsiMember)resolveElement).getContainingClass();
LOG.assertTrue(containingClass != null);
} else if (resolveElement instanceof PsiClass) {
containingClass = (PsiClass)resolveElement;
}
final boolean onArrayRef =
JavaPsiFacade.getElementFactory(element.getProject()).getArrayClass(PsiUtil.getLanguageLevel(element)) == containingClass;
final PsiElement referenceNameElement = referenceExpression.getReferenceNameElement();
if (isReceiver){
buf.append(map.get(parameters[0])).append(".");
} else {
if (!(referenceNameElement instanceof PsiKeyword)) {
if (qualifier instanceof PsiTypeElement) {
final PsiJavaCodeReferenceElement referenceElement = ((PsiTypeElement)qualifier).getInnermostComponentReferenceElement();
LOG.assertTrue(referenceElement != null);
buf.append(referenceElement.getReferenceName()).append(".");
}
else if (qualifier != null && !(qualifier instanceof PsiThisExpression && ((PsiThisExpression)qualifier).getQualifier() == null)) {
buf.append(qualifier.getText()).append(".");
}
}
}
//new or method name
buf.append(referenceExpression.getReferenceName());
if (referenceNameElement instanceof PsiKeyword) {
//class name
buf.append(" ");
if (onArrayRef) {
if (qualifier instanceof PsiTypeElement) {
final PsiType type = ((PsiTypeElement)qualifier).getType();
int dim = type.getArrayDimensions();
buf.append(type.getDeepComponentType().getCanonicalText());
buf.append("[");
buf.append(map.get(parameters[0]));
buf.append("]");
while (--dim > 0) {
buf.append("[]");
}
}
} else {
buf.append(((PsiMember)resolveElement).getName());
final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
LOG.assertTrue(containingClass != null);
if (containingClass.hasTypeParameters() && !PsiUtil.isRawSubstitutor(containingClass, substitutor)) {
buf.append("<").append(StringUtil.join(containingClass.getTypeParameters(), new Function<PsiTypeParameter, String>() {
@Override
public String fun(PsiTypeParameter parameter) {
final PsiType psiType = substitutor.substitute(parameter);
LOG.assertTrue(psiType != null);
return psiType.getCanonicalText();
}
}, ", ")).append(">");
}
}
}
if (!onArrayRef || isReceiver) {
//param list
buf.append("(");
boolean first = true;
for (int i = isReceiver ? 1 : 0; i < parameters.length; i++) {
PsiParameter parameter = parameters[i];
if (!first) {
buf.append(", ");
} else {
first = false;
}
buf.append(map.get(parameter));
}
buf.append(")");
}
if (needBraces) {
buf.append(";}");
}
}
final PsiTypeCastExpression typeCastExpression = (PsiTypeCastExpression)referenceExpression
.replace(JavaPsiFacade.getElementFactory(element.getProject()).createExpressionFromText(buf.toString(), referenceExpression));
if (RedundantCastUtil.isCastRedundant(typeCastExpression)) {
final PsiExpression operand = typeCastExpression.getOperand();
LOG.assertTrue(operand != null);
typeCastExpression.replace(operand);
}
}
private static class MethodRefPredicate implements PsiElementPredicate {
@Override
public boolean satisfiedBy(PsiElement element) {
final PsiMethodReferenceExpression methodReferenceExpression = PsiTreeUtil.getParentOfType(element, PsiMethodReferenceExpression.class);
if (methodReferenceExpression != null) {
final PsiType interfaceType = methodReferenceExpression.getFunctionalInterfaceType();
if (interfaceType != null &&
LambdaUtil.getFunctionalInterfaceMethod(interfaceType) != null &&
methodReferenceExpression.resolve() != null) {
return true;
}
}
return false;
}
}
}