/* * Copyright 2000-2016 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.codeInsight.daemon.impl.quickfix; import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class AddTypeArgumentsConditionalFix implements IntentionAction { private static final Logger LOG = Logger.getInstance(AddTypeArgumentsConditionalFix.class); private final PsiSubstitutor mySubstitutor; private final PsiMethodCallExpression myExpression; private final PsiMethod myMethod; public AddTypeArgumentsConditionalFix(PsiSubstitutor substitutor, PsiMethodCallExpression expression, PsiMethod method) { mySubstitutor = substitutor; myExpression = expression; myMethod = method; } @NotNull @Override public String getText() { return "Add explicit type arguments"; } @NotNull @Override public String getFamilyName() { return getText(); } @Override public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { if (mySubstitutor.isValid() && myExpression.isValid() && myMethod.isValid()) { return true; } return false; } @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { final PsiTypeParameter[] typeParameters = myMethod.getTypeParameters(); final String typeArguments = "<" + StringUtil.join(typeParameters, parameter -> { final PsiType substituteTypeParam = mySubstitutor.substitute(parameter); LOG.assertTrue(substituteTypeParam != null); return GenericsUtil.eliminateWildcards(substituteTypeParam).getCanonicalText(); }, ", ") + ">"; final PsiExpression expression = myExpression.getMethodExpression().getQualifierExpression(); String withTypeArgsText; if (expression != null) { withTypeArgsText = expression.getText(); } else { if (isInStaticContext(myExpression, null) || myMethod.hasModifierProperty(PsiModifier.STATIC)) { final PsiClass aClass = myMethod.getContainingClass(); LOG.assertTrue(aClass != null); withTypeArgsText = aClass.getQualifiedName(); } else { withTypeArgsText = "this"; } } withTypeArgsText += "." + typeArguments + myExpression.getMethodExpression().getReferenceName(); final PsiExpression withTypeArgs = JavaPsiFacade.getElementFactory(project).createExpressionFromText(withTypeArgsText + myExpression.getArgumentList().getText(), myExpression); myExpression.replace(withTypeArgs); } public static boolean isInStaticContext(PsiElement element, @Nullable final PsiClass aClass) { return PsiUtil.getEnclosingStaticElement(element, aClass) != null; } @Override public boolean startInWriteAction() { return true; } public static void register(HighlightInfo highlightInfo, PsiExpression expression, @NotNull PsiType lType) { if (lType != PsiType.NULL && expression instanceof PsiConditionalExpression) { final PsiExpression thenExpression = ((PsiConditionalExpression)expression).getThenExpression(); final PsiExpression elseExpression = ((PsiConditionalExpression)expression).getElseExpression(); if (thenExpression != null && elseExpression != null) { final PsiType thenType = thenExpression.getType(); final PsiType elseType = elseExpression.getType(); if (thenType != null && elseType != null) { final boolean thenAssignable = TypeConversionUtil.isAssignable(lType, thenType); final boolean elseAssignable = TypeConversionUtil.isAssignable(lType, elseType); if (!thenAssignable && thenExpression instanceof PsiMethodCallExpression) { inferTypeArgs(highlightInfo, lType, thenExpression); } if (!elseAssignable && elseExpression instanceof PsiMethodCallExpression) { inferTypeArgs(highlightInfo, lType, elseExpression); } } } } } private static void inferTypeArgs(HighlightInfo highlightInfo, PsiType lType, PsiExpression thenExpression) { final JavaResolveResult result = ((PsiMethodCallExpression)thenExpression).resolveMethodGenerics(); final PsiMethod method = (PsiMethod)result.getElement(); if (method != null) { final PsiType returnType = method.getReturnType(); final PsiClass aClass = method.getContainingClass(); if (returnType != null && aClass != null && aClass.getQualifiedName() != null) { final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(method.getProject()); final PsiDeclarationStatement variableDeclarationStatement = javaPsiFacade.getElementFactory().createVariableDeclarationStatement("xxx", lType, thenExpression); final PsiExpression initializer = ((PsiLocalVariable)variableDeclarationStatement.getDeclaredElements()[0]).getInitializer(); LOG.assertTrue(initializer != null); final PsiSubstitutor substitutor = javaPsiFacade.getResolveHelper() .inferTypeArguments(method.getTypeParameters(), method.getParameterList().getParameters(), ((PsiMethodCallExpression)thenExpression).getArgumentList().getExpressions(), PsiSubstitutor.EMPTY, initializer, DefaultParameterTypeInferencePolicy.INSTANCE); PsiType substitutedType = substitutor.substitute(returnType); if (substitutedType != null && TypeConversionUtil.isAssignable(lType, substitutedType)) { QuickFixAction.registerQuickFixAction(highlightInfo, thenExpression.getTextRange(), new AddTypeArgumentsConditionalFix(substitutor, (PsiMethodCallExpression)thenExpression, method)); } } } } }