blob: b9626c1a1e9c1665c4a6f359896593c91eb55c06 [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;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.CompletionParameterTypeInferencePolicy;
import com.intellij.psi.impl.source.resolve.DefaultParameterTypeInferencePolicy;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class ExpectedTypeUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.ExpectedTypeUtil");
public static ExpectedTypeInfo[] intersect(List<ExpectedTypeInfo[]> typeInfos) {
if (typeInfos.isEmpty()) return ExpectedTypeInfo.EMPTY_ARRAY;
ExpectedTypeInfos result = new ExpectedTypeInfos(typeInfos.get(0));
ExpectedTypeInfos acc = new ExpectedTypeInfos();
for (int i = 1; i < typeInfos.size(); i++) {
ExpectedTypeInfo[] next = typeInfos.get(i);
acc.clear();
for (ExpectedTypeInfo info : next) {
for (Iterator<ExpectedTypeInfo> iterator = result.iterator(); iterator.hasNext();) {
ExpectedTypeInfo[] intersection = iterator.next().intersect(info);
for (ExpectedTypeInfo aIntersection : intersection) {
acc.addInfo(aIntersection);
}
}
}
if (acc.isEmpty()) {
return ExpectedTypeInfo.EMPTY_ARRAY;
}
result = new ExpectedTypeInfos(acc.toArray());
}
return result.toArray();
}
private static class ExpectedTypeInfos {
List<ExpectedTypeInfo> myInfos;
public ExpectedTypeInfos() {
myInfos = new ArrayList<ExpectedTypeInfo>();
}
public ExpectedTypeInfos(ExpectedTypeInfo[] infos) {
myInfos = new ArrayList<ExpectedTypeInfo>(Arrays.asList(infos));
}
public void clear () { myInfos.clear(); }
public void addInfo (ExpectedTypeInfo info) {
for (Iterator<ExpectedTypeInfo> iterator = myInfos.iterator(); iterator.hasNext();) {
ExpectedTypeInfo sub = iterator.next();
int cmp = contains(sub, info);
if (cmp > 0) return;
else if (cmp < 0) {
iterator.remove();
}
}
myInfos.add(info);
}
public boolean isEmpty() {
return myInfos.isEmpty();
}
public Iterator<ExpectedTypeInfo> iterator() {
return myInfos.iterator();
}
public ExpectedTypeInfo[] toArray() {
return myInfos.toArray(new ExpectedTypeInfo[myInfos.size()]);
}
}
/**
* @return <0 if info2 contains info1 (or they are equal)
* >0 if info1 contains info2
* 0 otherwise
*/
public static int contains(ExpectedTypeInfo info1, ExpectedTypeInfo info2) {
int kind1 = info1.getKind();
int kind2 = info2.getKind();
if (kind1 == kind2) {
if (matchesStrictly(info1.getType(), info2)) return -1;
if (matchesStrictly(info2.getType(), info1)) return 1;
return 0;
} else if (kind1 == ExpectedTypeInfo.TYPE_STRICTLY) {
return matches(info1.getType(), info2) ? -1 : 0;
} else if (kind2 == ExpectedTypeInfo.TYPE_STRICTLY) {
return matches(info2.getType(), info1) ? 1 : 0;
}
return 0;
}
private static boolean matchesStrictly (PsiType type, ExpectedTypeInfo info) {
if ((type instanceof PsiPrimitiveType) != (info.getType() instanceof PsiPrimitiveType)) return false;
return matches(type, info);
}
public static boolean matches (PsiType type, ExpectedTypeInfo info) {
PsiType infoType = info.getType();
switch (info.getKind()) {
case ExpectedTypeInfo.TYPE_STRICTLY:
return type.equals(infoType);
case ExpectedTypeInfo.TYPE_OR_SUBTYPE:
return infoType.isAssignableFrom(type);
case ExpectedTypeInfo.TYPE_OR_SUPERTYPE:
return type.isAssignableFrom(infoType);
case ExpectedTypeInfo.TYPE_BETWEEN:
return type.isAssignableFrom(info.getDefaultType()) && infoType.isAssignableFrom(type);
}
LOG.error("Unexpected ExpectedInfo kind");
return false;
}
public static class ExpectedClassesFromSetProvider implements ExpectedTypesProvider.ExpectedClassProvider {
private final Set<PsiClass> myOccurrenceClasses;
public ExpectedClassesFromSetProvider(@NotNull Set<PsiClass> occurrenceClasses) {
myOccurrenceClasses = occurrenceClasses;
}
@Override
public PsiField[] findDeclaredFields(final PsiManager manager, String name) {
List<PsiField> fields = new ArrayList<PsiField>();
for (PsiClass aClass : myOccurrenceClasses) {
final PsiField field = aClass.findFieldByName(name, true);
if (field != null) fields.add(field);
}
return fields.toArray(new PsiField[fields.size()]);
}
@Override
public PsiMethod[] findDeclaredMethods(final PsiManager manager, String name) {
List<PsiMethod> methods = new ArrayList<PsiMethod>();
for (PsiClass aClass : myOccurrenceClasses) {
final PsiMethod[] occMethod = aClass.findMethodsByName(name, true);
ContainerUtil.addAll(methods, occMethod);
}
return methods.toArray(new PsiMethod[methods.size()]);
}
}
@Nullable
public static PsiSubstitutor inferSubstitutor(final PsiMethod method, final PsiMethodCallExpression callExpr, boolean forCompletion) {
final PsiResolveHelper helper = JavaPsiFacade.getInstance(method.getProject()).getResolveHelper();
final PsiParameter[] parameters = method.getParameterList().getParameters();
PsiExpression[] args = callExpr.getArgumentList().getExpressions();
PsiSubstitutor result = PsiSubstitutor.EMPTY;
for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(method.getContainingClass())) {
PsiType type = helper.inferTypeForMethodTypeParameter(typeParameter, parameters, args, PsiSubstitutor.EMPTY, callExpr.getParent(),
forCompletion ? CompletionParameterTypeInferencePolicy.INSTANCE : DefaultParameterTypeInferencePolicy.INSTANCE);
if (PsiType.NULL.equals(type)) return null;
result = result.put(typeParameter, type);
}
return result;
}
}