Cleanup: NotNull/Nullable
[idea/community.git] / java / java-psi-impl / src / com / intellij / psi / impl / source / resolve / graphInference / FunctionalInterfaceParameterizationUtil.java
1 /*
2  * Copyright 2000-2013 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.psi.impl.source.resolve.graphInference;
17
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.psi.*;
20 import com.intellij.psi.impl.source.resolve.graphInference.constraints.TypeEqualityConstraint;
21 import com.intellij.psi.util.TypeConversionUtil;
22 import com.intellij.util.containers.ContainerUtil;
23 import org.jetbrains.annotations.Nullable;
24
25 import java.util.HashSet;
26
27 public class FunctionalInterfaceParameterizationUtil {
28   private static final Logger LOG = Logger.getInstance(FunctionalInterfaceParameterizationUtil.class);
29
30   public static boolean isWildcardParameterized(@Nullable PsiType classType) {
31     if (classType == null) return false;
32     if (classType instanceof PsiIntersectionType) {
33       for (PsiType type : ((PsiIntersectionType)classType).getConjuncts()) {
34         if (!isWildcardParameterized(type)) return false;
35       }
36     }
37     if (classType instanceof PsiClassType) {
38       final PsiClassType.ClassResolveResult result = ((PsiClassType)classType).resolveGenerics();
39       final PsiClass aClass = result.getElement();
40       if (aClass != null) {
41         final PsiSubstitutor substitutor = result.getSubstitutor();
42         for (PsiTypeParameter parameter : aClass.getTypeParameters()) {
43           if (substitutor.substitute(parameter) instanceof PsiWildcardType) {
44             return true;
45           }
46         }
47       }
48       return false;
49     }
50     return false;
51   }
52
53   @Nullable
54   public static PsiType getGroundTargetType(@Nullable PsiType psiClassType) {
55     return getGroundTargetType(psiClassType, null);
56   }
57
58   @Nullable
59   public static PsiType getGroundTargetType(@Nullable PsiType psiClassType, @Nullable PsiLambdaExpression expr) {
60     return getGroundTargetType(psiClassType, expr, true);
61   }
62
63   @Nullable
64   public static PsiType getGroundTargetType(@Nullable PsiType psiClassType, @Nullable PsiLambdaExpression expr, boolean performFinalCheck) {
65     if (!isWildcardParameterized(psiClassType)) {
66       return psiClassType;
67     }
68
69     if (expr != null && expr.hasFormalParameterTypes()) return getFunctionalTypeExplicit(psiClassType, expr, performFinalCheck);
70
71     return psiClassType instanceof PsiClassType ? getNonWildcardParameterization((PsiClassType)psiClassType) : null;
72   }
73
74   /**
75    * 18.5.3. Functional Interface Parameterization Inference 
76    */
77   private static PsiType getFunctionalTypeExplicit(PsiType psiClassType, PsiLambdaExpression expr, boolean performFinalCheck) {
78     final PsiParameter[] lambdaParams = expr.getParameterList().getParameters();
79     if (psiClassType instanceof PsiIntersectionType) {
80       for (PsiType psiType : ((PsiIntersectionType)psiClassType).getConjuncts()) {
81         final PsiType functionalType = getFunctionalTypeExplicit(psiType, expr, performFinalCheck);
82         if (functionalType != null) return functionalType;
83       }
84       return null;
85     }
86
87     LOG.assertTrue(psiClassType instanceof PsiClassType, "Unexpected type: " + psiClassType);
88     final PsiType[] parameters = ((PsiClassType)psiClassType).getParameters();
89     final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)psiClassType).resolveGenerics();
90     PsiClass psiClass = resolveResult.getElement();
91
92     if (psiClass != null) {
93
94       final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(resolveResult);
95       if (interfaceMethod == null) return null;
96       final PsiClass samClass = interfaceMethod.getContainingClass();
97       if (samClass == null) return null;
98
99       PsiTypeParameter[] typeParameters = psiClass.getTypeParameters();
100       if (typeParameters.length != parameters.length) {
101         return null;
102       }
103       final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(psiClass.getProject());
104       final PsiParameter[] targetMethodParams = interfaceMethod.getParameterList().getParameters();
105       if (targetMethodParams.length != lambdaParams.length) {
106         return null;
107       }
108
109       final PsiSubstitutor lambdaSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(samClass, psiClass, PsiSubstitutor.EMPTY);
110
111       final InferenceSession session = new InferenceSession(typeParameters, PsiSubstitutor.EMPTY, expr.getManager(), expr);
112
113       for (int i = 0; i < targetMethodParams.length; i++) {
114         final PsiType qType = lambdaSubstitutor.substitute(targetMethodParams[i].getType());
115         session.addConstraint(new TypeEqualityConstraint(lambdaParams[i].getType(), session.substituteWithInferenceVariables(qType)));
116       }
117
118       if (!session.repeatInferencePhases()) {
119         return null;
120       }
121
122       final PsiSubstitutor substitutor = session.getInstantiations(session.getInferenceVariables());
123       final PsiType[] newTypeParameters = new PsiType[parameters.length];
124       for (int i = 0; i < typeParameters.length; i++) {
125         PsiTypeParameter typeParameter = typeParameters[i];
126         if (substitutor.getSubstitutionMap().containsKey(typeParameter)) {
127           newTypeParameters[i] = substitutor.substitute(typeParameter);
128         } else {
129           newTypeParameters[i] = parameters[i];
130         }
131       }
132
133       final PsiClassType parameterization = elementFactory.createType(psiClass, newTypeParameters);
134
135       //If F<A'1, ..., A'm> is not a well-formed type (that is, the type arguments are not within their bounds), 
136       // or if F<A'1, ..., A'm> is not a subtype of F<A1, ..., Am>, no valid parameterization exists.
137       if (!isWellFormed(psiClass, typeParameters, newTypeParameters) || performFinalCheck && !psiClassType.isAssignableFrom(parameterization)) {
138         return null;
139       }
140
141       //Otherwise, the inferred parameterization is either F<A'1, ..., A'm>, if all the type arguments are types,
142       if (!isWildcardParameterized(parameterization)) {
143         return parameterization;
144       }
145
146       //or the non-wildcard parameterization (p9.8) of F<A'1, ..., A'm>, if one or more type arguments are still wildcards.
147       return getNonWildcardParameterization(parameterization);
148     }
149     return null;
150   }
151
152   private static boolean isWellFormed(PsiClass psiClass, PsiTypeParameter[] typeParameters, PsiType[] newTypeParameters) {
153     final PsiSubstitutor substitutor = PsiSubstitutor.EMPTY.putAll(psiClass, newTypeParameters);
154     for (int i = 0; i < typeParameters.length; i++) {
155       for (PsiClassType bound : typeParameters[i].getExtendsListTypes()) {
156         if (GenericsUtil.checkNotInBounds(newTypeParameters[i], substitutor.substitute(bound), false)) {
157           return false;
158         }
159       }
160     }
161     return true;
162   }
163
164   /**
165      The function type of a parameterized functional interface, F<A1...An>, where one or more of A1...An is a wildcard, is the function type of the non-wildcard parameterization of F, F<T1...Tn> determined as follows. 
166      Let P1, ..., Pn be the type parameters of F and B1, ..., Bn be the corresponding bounds. For all i, 1 <= i <= n, Ti is derived according to the form of Ai:
167
168      If Ai is a type, then Ti = Ai.
169      If Ai is a wildcard, and the corresponding type parameter bound, Bi, mentions one of P1...Pn, then Ti is undefined and there is no function type.
170      Otherwise:
171      If Ai is an unbound wildcard ?, then Ti = Bi.
172      If Ai is a upper-bounded wildcard ? extends Ui, then Ti = glb(Ui, Bi).
173      If Ai is a lower-bounded wildcard ? super Li, then Ti = Li.
174    */
175   @Nullable
176   public static PsiType getNonWildcardParameterization(PsiClassType psiClassType) {
177     final PsiClassType.ClassResolveResult result = psiClassType.resolveGenerics();
178     final PsiClass psiClass = result.getElement();
179     if (psiClass != null) {
180       final PsiTypeParameter[] typeParameters = psiClass.getTypeParameters();
181       final PsiType[] newParameters = new PsiType[typeParameters.length];
182
183       final PsiSubstitutor substitutor = result.getSubstitutor();
184       final HashSet<PsiTypeParameter> typeParametersSet = ContainerUtil.newHashSet(typeParameters);
185       next: for (int i = 0; i < typeParameters.length; i++) {
186         PsiType paramType = substitutor.substitute(typeParameters[i]);
187         if (paramType instanceof PsiWildcardType) {
188           final PsiType bound = ((PsiWildcardType)paramType).getBound();
189           for (PsiClassType paramBound : typeParameters[i].getExtendsListTypes()) {
190             if (PsiPolyExpressionUtil.mentionsTypeParameters(paramBound, typeParametersSet)) {
191               newParameters[i] = bound;
192               continue next;
193             }
194           }
195
196           if (((PsiWildcardType)paramType).isSuper()) {
197             newParameters[i] = bound;
198           }
199           else {
200             newParameters[i] = bound != null ? bound : PsiType.getJavaLangObject(psiClass.getManager(), psiClassType.getResolveScope());
201             for (PsiClassType paramBound : typeParameters[i].getExtendsListTypes()) {
202               newParameters[i] = GenericsUtil.getGreatestLowerBound(newParameters[i], paramBound);
203             }
204           }
205         } else {
206           newParameters[i] = paramType;
207         }
208       }
209
210       if (!isWellFormed(psiClass, typeParameters, newParameters)) {
211         return null;
212       }
213
214       final PsiClassType parameterization = JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass, newParameters);
215       if (!psiClassType.isAssignableFrom(parameterization)) {
216         return null;
217       }
218       return parameterization;
219     }
220     return null;
221   }
222 }