blob: 494ed110b254319e74bea0fff24320fa0807bbcd [file] [log] [blame]
/*
* Copyright 2000-2013 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.completion;
import com.intellij.codeInsight.ExpectedTypeInfo;
import com.intellij.codeInsight.ExpectedTypeInfoImpl;
import com.intellij.codeInsight.PsiEquivalenceUtil;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.Consumer;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
public class MethodReferenceCompletionProvider extends CompletionProvider<CompletionParameters> {
private static final Logger LOG = Logger.getInstance("#" + MethodReferenceCompletionProvider.class.getName());
@Override
protected void addCompletions(@NotNull CompletionParameters parameters,
ProcessingContext context,
@NotNull final CompletionResultSet result) {
if (!PsiUtil.isLanguageLevel8OrHigher(parameters.getOriginalFile())) return;
final ExpectedTypeInfo[] expectedTypes = JavaSmartCompletionContributor.getExpectedTypes(parameters);
for (ExpectedTypeInfo expectedType : expectedTypes) {
final PsiType defaultType = expectedType.getDefaultType();
if (LambdaUtil.isFunctionalType(defaultType)) {
final PsiType functionalType = FunctionalInterfaceParameterizationUtil.getGroundTargetType(defaultType);
final PsiType returnType = LambdaUtil.getFunctionalInterfaceReturnType(functionalType);
if (returnType != null) {
final PsiElement position = parameters.getPosition();
final PsiElement refPlace = position.getParent();
final ExpectedTypeInfoImpl typeInfo =
new ExpectedTypeInfoImpl(returnType, ExpectedTypeInfo.TYPE_OR_SUBTYPE, returnType, TailType.UNKNOWN, null,
ExpectedTypeInfoImpl.NULL);
final Map<PsiMethodReferenceExpression, PsiType> map = PsiMethodReferenceUtil.getFunctionalTypeMap();
Consumer<LookupElement> noTypeCheck = new Consumer<LookupElement>() {
@Override
public void consume(final LookupElement lookupElement) {
final PsiElement element = lookupElement.getPsiElement();
if (element instanceof PsiMethod) {
final PsiMethodReferenceExpression referenceExpression = createMethodReferenceExpression((PsiMethod)element);
if (referenceExpression == null) {
return;
}
final PsiType added = map.put(referenceExpression, functionalType);
try {
final PsiElement resolve = referenceExpression.resolve();
if (resolve != null && PsiEquivalenceUtil.areElementsEquivalent(element, resolve) &&
PsiMethodReferenceUtil.checkMethodReferenceContext(referenceExpression, resolve, functionalType) == null) {
result.addElement(new JavaMethodReferenceElement((PsiMethod)element, refPlace));
}
}
finally {
if (added == null) {
map.remove(referenceExpression);
}
}
}
}
private PsiMethodReferenceExpression createMethodReferenceExpression(PsiMethod method) {
PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(method.getProject());
if (refPlace instanceof PsiMethodReferenceExpression) {
final PsiMethodReferenceExpression referenceExpression = (PsiMethodReferenceExpression)refPlace.copy();
final PsiElement referenceNameElement = referenceExpression.getReferenceNameElement();
LOG.assertTrue(referenceNameElement != null, referenceExpression);
referenceNameElement.replace(method.isConstructor() ? elementFactory.createKeyword("new") : elementFactory.createIdentifier(method.getName()));
return referenceExpression;
}
else if (method.hasModifierProperty(PsiModifier.STATIC)) {
final PsiClass aClass = method.getContainingClass();
LOG.assertTrue(aClass != null);
final String qualifiedName = aClass.getQualifiedName();
return (PsiMethodReferenceExpression)elementFactory.createExpressionFromText(
qualifiedName + "::" + (method.isConstructor() ? "new" : method.getName()), refPlace);
}
else {
return null;
}
}
};
final Runnable runnable = ReferenceExpressionCompletionContributor
.fillCompletionVariants(new JavaSmartCompletionParameters(parameters, typeInfo), noTypeCheck);
if (runnable != null) {
runnable.run();
}
}
}
}
}
private static class JavaMethodReferenceElement extends JavaMethodCallElement {
private final PsiMethod myMethod;
private final PsiElement myRefPlace;
public JavaMethodReferenceElement(PsiMethod method, PsiElement refPlace) {
super(method, method.isConstructor() ? "new" : method.getName());
myMethod = method;
myRefPlace = refPlace;
}
@Override
public void handleInsert(InsertionContext context) {
if (!(myRefPlace instanceof PsiMethodReferenceExpression)) {
final PsiClass containingClass = myMethod.getContainingClass();
LOG.assertTrue(containingClass != null);
final String qualifiedName = containingClass.getQualifiedName();
LOG.assertTrue(qualifiedName != null);
final Editor editor = context.getEditor();
final Document document = editor.getDocument();
final int startOffset = context.getStartOffset();
document.insertString(startOffset, qualifiedName + "::");
JavaCompletionUtil.shortenReference(context.getFile(), startOffset + qualifiedName.length() - 1);
JavaCompletionUtil.insertTail(context, this, handleCompletionChar(context.getEditor(), this, context.getCompletionChar()), false);
}
}
}
}