2 * Copyright 2000-2016 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.psi.util;
18 import com.intellij.lang.ASTNode;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.util.Comparing;
21 import com.intellij.openapi.util.Condition;
22 import com.intellij.pom.java.LanguageLevel;
23 import com.intellij.psi.*;
24 import com.intellij.psi.tree.IElementType;
25 import com.intellij.util.ArrayUtil;
26 import gnu.trove.THashMap;
27 import org.jetbrains.annotations.NonNls;
28 import org.jetbrains.annotations.NotNull;
29 import org.jetbrains.annotations.Nullable;
31 import java.util.HashSet;
35 public class PsiTypesUtil {
36 @NonNls private static final Map<String, String> ourUnboxedTypes = new THashMap<String, String>();
37 @NonNls private static final Map<String, String> ourBoxedTypes = new THashMap<String, String>();
40 ourUnboxedTypes.put(CommonClassNames.JAVA_LANG_BOOLEAN, "boolean");
41 ourUnboxedTypes.put(CommonClassNames.JAVA_LANG_BYTE, "byte");
42 ourUnboxedTypes.put(CommonClassNames.JAVA_LANG_SHORT, "short");
43 ourUnboxedTypes.put(CommonClassNames.JAVA_LANG_INTEGER, "int");
44 ourUnboxedTypes.put(CommonClassNames.JAVA_LANG_LONG, "long");
45 ourUnboxedTypes.put(CommonClassNames.JAVA_LANG_FLOAT, "float");
46 ourUnboxedTypes.put(CommonClassNames.JAVA_LANG_DOUBLE, "double");
47 ourUnboxedTypes.put(CommonClassNames.JAVA_LANG_CHARACTER, "char");
49 ourBoxedTypes.put("boolean", CommonClassNames.JAVA_LANG_BOOLEAN);
50 ourBoxedTypes.put("byte", CommonClassNames.JAVA_LANG_BYTE);
51 ourBoxedTypes.put("short", CommonClassNames.JAVA_LANG_SHORT);
52 ourBoxedTypes.put("int", CommonClassNames.JAVA_LANG_INTEGER);
53 ourBoxedTypes.put("long", CommonClassNames.JAVA_LANG_LONG);
54 ourBoxedTypes.put("float", CommonClassNames.JAVA_LANG_FLOAT);
55 ourBoxedTypes.put("double", CommonClassNames.JAVA_LANG_DOUBLE);
56 ourBoxedTypes.put("char", CommonClassNames.JAVA_LANG_CHARACTER);
59 @NonNls private static final String GET_CLASS_METHOD = "getClass";
61 private PsiTypesUtil() { }
64 public static String getDefaultValueOfType(PsiType type) {
65 if (type instanceof PsiArrayType) {
66 int count = type.getArrayDimensions() - 1;
67 PsiType componentType = type.getDeepComponentType();
69 if (componentType instanceof PsiClassType) {
70 final PsiClassType classType = (PsiClassType)componentType;
71 if (classType.resolve() instanceof PsiTypeParameter) {
72 return PsiKeyword.NULL;
76 StringBuilder buffer = new StringBuilder();
77 buffer.append(PsiKeyword.NEW);
79 buffer.append(componentType.getCanonicalText());
81 for (int i = 0; i < count; i++) {
84 return buffer.toString();
86 if (type instanceof PsiPrimitiveType) {
87 return PsiType.BOOLEAN.equals(type) ? PsiKeyword.FALSE : "0";
89 return PsiKeyword.NULL;
93 * Returns the unboxed type name or parameter.
94 * @param type boxed java type name
95 * @return unboxed type name if available; same value otherwise
98 public static String unboxIfPossible(final String type) {
99 if (type == null) return null;
100 final String s = ourUnboxedTypes.get(type);
101 return s == null? type : s;
105 * Returns the boxed type name or parameter.
106 * @param type primitive java type name
107 * @return boxed type name if available; same value otherwise
110 public static String boxIfPossible(final String type) {
111 if (type == null) return null;
112 final String s = ourBoxedTypes.get(type);
113 return s == null ? type : s;
117 public static PsiClass getPsiClass(@Nullable PsiType psiType) {
118 return psiType instanceof PsiClassType? ((PsiClassType)psiType).resolve() : null;
121 public static PsiClassType getClassType(@NotNull PsiClass psiClass) {
122 return JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass);
126 public static PsiClassType getLowestUpperBoundClassType(@NotNull final PsiDisjunctionType type) {
127 final PsiType lub = type.getLeastUpperBound();
128 if (lub instanceof PsiClassType) {
129 return (PsiClassType)lub;
131 else if (lub instanceof PsiIntersectionType) {
132 for (PsiType subType : ((PsiIntersectionType)lub).getConjuncts()) {
133 if (subType instanceof PsiClassType) {
134 final PsiClass aClass = ((PsiClassType)subType).resolve();
135 if (aClass != null && !aClass.isInterface()) {
136 return (PsiClassType)subType;
144 public static PsiType patchMethodGetClassReturnType(@NotNull PsiMethodReferenceExpression methodExpression,
145 @NotNull PsiMethod method) {
146 if (isGetClass(method)) {
147 final PsiType qualifierType = PsiMethodReferenceUtil.getQualifierType(methodExpression);
148 return qualifierType != null ? createJavaLangClassType(methodExpression, qualifierType, true) : null;
153 public static PsiType patchMethodGetClassReturnType(@NotNull PsiExpression call,
154 @NotNull PsiReferenceExpression methodExpression,
155 @NotNull PsiMethod method,
156 @Nullable Condition<IElementType> condition,
157 @NotNull LanguageLevel languageLevel) {
159 if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5) && isGetClass(method)) {
160 PsiExpression qualifier = methodExpression.getQualifierExpression();
161 PsiType qualifierType = null;
162 final Project project = call.getProject();
163 if (qualifier != null) {
164 qualifierType = TypeConversionUtil.erasure(qualifier.getType());
166 else if (condition != null) {
167 ASTNode parent = call.getNode().getTreeParent();
168 while (parent != null && condition.value(parent.getElementType())) {
169 parent = parent.getTreeParent();
171 if (parent != null) {
172 qualifierType = JavaPsiFacade.getInstance(project).getElementFactory().createType((PsiClass)parent.getPsi());
175 return createJavaLangClassType(methodExpression, qualifierType, true);
180 public static boolean isGetClass(PsiMethod method) {
181 return GET_CLASS_METHOD.equals(method.getName()) && CommonClassNames.JAVA_LANG_OBJECT.equals(method.getContainingClass().getQualifiedName());
185 public static PsiType createJavaLangClassType(@NotNull PsiElement context, @Nullable PsiType qualifierType, boolean captureTopLevelWildcards) {
186 if (qualifierType != null) {
187 PsiUtil.ensureValidType(qualifierType);
188 JavaPsiFacade facade = JavaPsiFacade.getInstance(context.getProject());
189 PsiClass javaLangClass = facade.findClass(CommonClassNames.JAVA_LANG_CLASS, context.getResolveScope());
190 if (javaLangClass != null && javaLangClass.getTypeParameters().length == 1) {
191 PsiSubstitutor substitutor = PsiSubstitutor.EMPTY.
192 put(javaLangClass.getTypeParameters()[0], PsiWildcardType.createExtends(context.getManager(), qualifierType));
193 final PsiClassType classType = facade.getElementFactory().createType(javaLangClass, substitutor, PsiUtil.getLanguageLevel(context));
194 return captureTopLevelWildcards ? PsiUtil.captureToplevelWildcards(classType, context) : classType;
201 public static PsiType getExpectedTypeByParent(PsiElement element) {
202 final PsiElement parent = PsiUtil.skipParenthesizedExprUp(element.getParent());
203 if (parent instanceof PsiVariable) {
204 if (PsiUtil.checkSameExpression(element, ((PsiVariable)parent).getInitializer())) {
205 return ((PsiVariable)parent).getType();
208 else if (parent instanceof PsiAssignmentExpression) {
209 if (PsiUtil.checkSameExpression(element, ((PsiAssignmentExpression)parent).getRExpression())) {
210 return ((PsiAssignmentExpression)parent).getLExpression().getType();
213 else if (parent instanceof PsiReturnStatement) {
214 final PsiElement psiElement = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class, PsiMethod.class);
215 if (psiElement instanceof PsiLambdaExpression) {
218 else if (psiElement instanceof PsiMethod){
219 return ((PsiMethod)psiElement).getReturnType();
222 else if (PsiUtil.isCondition(element, parent)) {
223 return PsiType.BOOLEAN.getBoxedType(parent);
225 else if (parent instanceof PsiArrayInitializerExpression) {
226 final PsiElement gParent = parent.getParent();
227 if (gParent instanceof PsiNewExpression) {
228 final PsiType type = ((PsiNewExpression)gParent).getType();
229 if (type instanceof PsiArrayType) {
230 return ((PsiArrayType)type).getComponentType();
233 else if (gParent instanceof PsiVariable) {
234 final PsiType type = ((PsiVariable)gParent).getType();
235 if (type instanceof PsiArrayType) {
236 return ((PsiArrayType)type).getComponentType();
239 else if (gParent instanceof PsiArrayInitializerExpression) {
240 final PsiType expectedTypeByParent = getExpectedTypeByParent(parent);
241 return expectedTypeByParent != null && expectedTypeByParent instanceof PsiArrayType
242 ? ((PsiArrayType)expectedTypeByParent).getComponentType() : null;
249 * Returns the return type for enclosing method or lambda
251 * @param element element inside method or lambda to determine the return type of
252 * @return the return type or null if cannot be determined
255 public static PsiType getMethodReturnType(PsiElement element) {
256 final PsiElement methodOrLambda = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiLambdaExpression.class);
257 return methodOrLambda instanceof PsiMethod
258 ? ((PsiMethod)methodOrLambda).getReturnType()
259 : methodOrLambda instanceof PsiLambdaExpression ? LambdaUtil.getFunctionalInterfaceReturnType((PsiLambdaExpression)methodOrLambda) : null;
262 public static boolean compareTypes(PsiType leftType, PsiType rightType, boolean ignoreEllipsis) {
263 if (ignoreEllipsis) {
264 if (leftType instanceof PsiEllipsisType) {
265 leftType = ((PsiEllipsisType)leftType).toArrayType();
267 if (rightType instanceof PsiEllipsisType) {
268 rightType = ((PsiEllipsisType)rightType).toArrayType();
271 return Comparing.equal(leftType, rightType);
274 public static boolean isDenotableType(PsiType type) {
275 if (type instanceof PsiWildcardType || type instanceof PsiCapturedWildcardType) {
281 public static boolean hasUnresolvedComponents(@NotNull PsiType type) {
282 return type.accept(new PsiTypeVisitor<Boolean>() {
285 public Boolean visitClassType(PsiClassType classType) {
286 final PsiClass psiClass = classType.resolve();
287 if (psiClass == null) {
290 for (PsiType param : classType.getParameters()) {
291 if (param.accept(this)) {
295 return super.visitClassType(classType);
300 public Boolean visitArrayType(PsiArrayType arrayType) {
301 return arrayType.getComponentType().accept(this);
306 public Boolean visitWildcardType(PsiWildcardType wildcardType) {
307 final PsiType bound = wildcardType.getBound();
308 return bound != null && bound.accept(this);
312 public Boolean visitType(PsiType type) {
318 public static PsiType getParameterType(PsiParameter[] parameters, int i, boolean varargs) {
319 final PsiParameter parameter = parameters[i < parameters.length ? i : parameters.length - 1];
320 PsiType parameterType = parameter.getType();
321 if (parameterType instanceof PsiEllipsisType && varargs) {
322 parameterType = ((PsiEllipsisType)parameterType).getComponentType();
324 if (!parameterType.isValid()) {
325 PsiUtil.ensureValidType(parameterType, "Invalid type of parameter " + parameter + " of " + parameter.getClass());
327 return parameterType;
330 public static PsiTypeParameter[] filterUnusedTypeParameters(final PsiType superReturnTypeInBaseClassType,
331 final PsiTypeParameter[] typeParameters) {
332 if (typeParameters.length == 0) return typeParameters;
334 final Set<PsiTypeParameter> usedParameters = new HashSet<PsiTypeParameter>();
335 superReturnTypeInBaseClassType.accept(new PsiTypeVisitor<Object>(){
338 public Object visitClassType(PsiClassType classType) {
339 final PsiClass aClass = classType.resolve();
340 if (aClass instanceof PsiTypeParameter && ArrayUtil.find(typeParameters, aClass) > -1) {
341 usedParameters.add((PsiTypeParameter)aClass);
344 for (PsiType type : classType.getParameters()) {
352 public Object visitWildcardType(PsiWildcardType wildcardType) {
353 final PsiType bound = wildcardType.getBound();
354 return bound != null ? bound.accept(this) : null;
359 public Object visitArrayType(PsiArrayType arrayType) {
360 return arrayType.getComponentType().accept(this);
363 return usedParameters.toArray(new PsiTypeParameter[usedParameters.size()]);