IDEA-51822 & IDEA-51820
[fedora-idea.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / psi / impl / statements / expressions / TypesUtil.java
blobe4d6fa8b2ff9c357d2e3ab26ac8169f493002dc9
1 /*
2 * Copyright 2000-2009 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 org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions;
18 import com.intellij.pom.java.LanguageLevel;
19 import com.intellij.psi.*;
20 import com.intellij.psi.search.GlobalSearchScope;
21 import com.intellij.psi.tree.IElementType;
22 import com.intellij.psi.util.InheritanceUtil;
23 import com.intellij.psi.util.TypeConversionUtil;
24 import com.intellij.util.containers.HashMap;
25 import gnu.trove.TIntObjectHashMap;
26 import gnu.trove.TObjectIntHashMap;
27 import org.jetbrains.annotations.NonNls;
28 import org.jetbrains.annotations.Nullable;
29 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
30 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
31 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrBinaryExpression;
32 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
33 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrUnaryExpression;
34 import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
35 import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType;
36 import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil;
37 import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
38 import org.jetbrains.plugins.groovy.lang.resolve.processors.MethodResolverProcessor;
40 import java.util.Map;
42 import static com.intellij.psi.CommonClassNames.*;
43 import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.*;
45 /**
46 * @author ven
48 public class TypesUtil {
49 @NonNls
50 public static final Map<String, PsiType> ourQNameToUnboxed = new HashMap<String, PsiType>();
52 private TypesUtil() {
55 @Nullable
56 public static PsiType getNumericResultType(GrBinaryExpression binaryExpression, PsiType lType) {
57 final GrExpression rop = binaryExpression.getRightOperand();
58 PsiType rType = rop == null ? null : rop.getType();
59 if (lType == null || rType == null) return null;
60 String lCanonical = lType.getCanonicalText();
61 String rCanonical = rType.getCanonicalText();
62 if (TYPE_TO_RANK.containsKey(lCanonical) && TYPE_TO_RANK.containsKey(rCanonical)) {
63 int lRank = TYPE_TO_RANK.get(lCanonical);
64 int rRank = TYPE_TO_RANK.get(rCanonical);
65 int resultRank = Math.max(lRank, rRank);
66 String qName = RANK_TO_TYPE.get(resultRank);
67 GlobalSearchScope scope = binaryExpression.getResolveScope();
68 if (qName == null) return null;
69 return JavaPsiFacade.getInstance(binaryExpression.getProject()).getElementFactory().createTypeByFQClassName(qName, scope);
72 final PsiType type = getOverloadedOperatorType(lType, binaryExpression.getOperationTokenType(), binaryExpression, new PsiType[]{rType});
73 if (type != null) {
74 return type;
77 if (rType.equalsToText(GrStringUtil.GROOVY_LANG_GSTRING)) {
78 PsiType gstringType = JavaPsiFacade.getInstance(binaryExpression.getProject()).getElementFactory()
79 .createTypeByFQClassName(GrStringUtil.GROOVY_LANG_GSTRING, binaryExpression.getResolveScope());
80 return getOverloadedOperatorType(lType, binaryExpression.getOperationTokenType(), binaryExpression, new PsiType[]{gstringType});
82 return null;
85 @Nullable
86 public static PsiType getOverloadedOperatorType(PsiType thisType,
87 IElementType tokenType,
88 GroovyPsiElement place,
89 PsiType[] argumentTypes) {
90 return getOverloadedOperatorType(thisType, ourOperationsToOperatorNames.get(tokenType), place, argumentTypes);
93 @Nullable
94 public static PsiType getOverloadedOperatorType(PsiType thisType, String operatorName, GroovyPsiElement place, PsiType[] argumentTypes) {
95 if (operatorName != null) {
96 MethodResolverProcessor processor =
97 new MethodResolverProcessor(operatorName, place, false, thisType, argumentTypes, PsiType.EMPTY_ARRAY);
98 if (thisType instanceof PsiClassType) {
99 final PsiClassType classtype = (PsiClassType)thisType;
100 final PsiClassType.ClassResolveResult resolveResult = classtype.resolveGenerics();
101 final PsiClass lClass = resolveResult.getElement();
102 if (lClass != null) {
103 lClass.processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, resolveResult.getSubstitutor()), null, place);
107 ResolveUtil.processNonCodeMethods(thisType, processor, place.getProject(), place, false);
108 final GroovyResolveResult[] candidates = processor.getCandidates();
109 if (candidates.length == 1) {
110 final PsiElement element = candidates[0].getElement();
111 if (element instanceof PsiMethod) {
112 return candidates[0].getSubstitutor().substitute(((PsiMethod)element).getReturnType());
116 return null;
118 private static final Map<IElementType, String> ourPrimitiveTypesToClassNames = new HashMap<IElementType, String>();
119 private static final String NULL = "null";
120 private static final String JAVA_MATH_BIG_DECIMAL = "java.math.BigDecimal";
121 private static final String JAVA_MATH_BIG_INTEGER = "java.math.BigInteger";
123 static {
124 ourPrimitiveTypesToClassNames.put(mSTRING_LITERAL, JAVA_LANG_STRING);
125 ourPrimitiveTypesToClassNames.put(mGSTRING_LITERAL, JAVA_LANG_STRING);
126 ourPrimitiveTypesToClassNames.put(mREGEX_LITERAL, JAVA_LANG_STRING);
127 ourPrimitiveTypesToClassNames.put(mNUM_INT, JAVA_LANG_INTEGER);
128 ourPrimitiveTypesToClassNames.put(mNUM_LONG, JAVA_LANG_LONG);
129 ourPrimitiveTypesToClassNames.put(mNUM_FLOAT, JAVA_LANG_FLOAT);
130 ourPrimitiveTypesToClassNames.put(mNUM_DOUBLE, JAVA_LANG_DOUBLE);
131 ourPrimitiveTypesToClassNames.put(mNUM_BIG_INT, JAVA_MATH_BIG_INTEGER);
132 ourPrimitiveTypesToClassNames.put(mNUM_BIG_DECIMAL, JAVA_MATH_BIG_DECIMAL);
133 ourPrimitiveTypesToClassNames.put(kFALSE, JAVA_LANG_BOOLEAN);
134 ourPrimitiveTypesToClassNames.put(kTRUE, JAVA_LANG_BOOLEAN);
135 ourPrimitiveTypesToClassNames.put(kNULL, NULL);
138 private static final Map<IElementType, String> ourOperationsToOperatorNames = new HashMap<IElementType, String>();
140 static {
141 ourOperationsToOperatorNames.put(mPLUS, "plus");
142 ourOperationsToOperatorNames.put(mMINUS, "minus");
143 ourOperationsToOperatorNames.put(mBAND, "and");
144 ourOperationsToOperatorNames.put(mBOR, "or");
145 ourOperationsToOperatorNames.put(mBXOR, "xor");
146 ourOperationsToOperatorNames.put(mDIV, "div");
147 ourOperationsToOperatorNames.put(mMOD, "mod");
148 ourOperationsToOperatorNames.put(mSTAR, "multiply");
149 ourOperationsToOperatorNames.put(mDEC, "previous");
150 ourOperationsToOperatorNames.put(mINC, "next");
153 private static final TObjectIntHashMap<String> TYPE_TO_RANK = new TObjectIntHashMap<String>();
155 static {
156 TYPE_TO_RANK.put(JAVA_LANG_BYTE, 1);
157 TYPE_TO_RANK.put(JAVA_LANG_SHORT, 2);
158 TYPE_TO_RANK.put(JAVA_LANG_CHARACTER, 2);
159 TYPE_TO_RANK.put(JAVA_LANG_INTEGER, 3);
160 TYPE_TO_RANK.put(JAVA_LANG_LONG, 4);
161 TYPE_TO_RANK.put(JAVA_MATH_BIG_INTEGER, 5);
162 TYPE_TO_RANK.put(JAVA_MATH_BIG_DECIMAL, 6);
163 TYPE_TO_RANK.put(JAVA_LANG_FLOAT, 7);
164 TYPE_TO_RANK.put(JAVA_LANG_DOUBLE, 8);
165 TYPE_TO_RANK.put(JAVA_LANG_NUMBER, 9);
168 static {
169 ourQNameToUnboxed.put(JAVA_LANG_BOOLEAN, PsiType.BOOLEAN);
170 ourQNameToUnboxed.put(JAVA_LANG_BYTE, PsiType.BYTE);
171 ourQNameToUnboxed.put(JAVA_LANG_CHARACTER, PsiType.CHAR);
172 ourQNameToUnboxed.put(JAVA_LANG_SHORT, PsiType.SHORT);
173 ourQNameToUnboxed.put(JAVA_LANG_INTEGER, PsiType.INT);
174 ourQNameToUnboxed.put(JAVA_LANG_LONG, PsiType.LONG);
175 ourQNameToUnboxed.put(JAVA_LANG_FLOAT, PsiType.FLOAT);
176 ourQNameToUnboxed.put(JAVA_LANG_DOUBLE, PsiType.DOUBLE);
180 private static final TIntObjectHashMap<String> RANK_TO_TYPE = new TIntObjectHashMap<String>();
182 static {
183 RANK_TO_TYPE.put(1, JAVA_LANG_INTEGER);
184 RANK_TO_TYPE.put(2, JAVA_LANG_INTEGER);
185 RANK_TO_TYPE.put(3, JAVA_LANG_INTEGER);
186 RANK_TO_TYPE.put(4, JAVA_LANG_LONG);
187 RANK_TO_TYPE.put(5, JAVA_MATH_BIG_INTEGER);
188 RANK_TO_TYPE.put(6, JAVA_MATH_BIG_DECIMAL);
189 RANK_TO_TYPE.put(7, JAVA_LANG_DOUBLE);
190 RANK_TO_TYPE.put(8, JAVA_LANG_DOUBLE);
191 RANK_TO_TYPE.put(9, JAVA_LANG_NUMBER);
194 public static boolean isAssignable(PsiType lType, PsiType rType, PsiManager manager, GlobalSearchScope scope) {
195 //all numeric types are assignable
196 if (isNumericType(lType)) {
197 return isNumericType(rType) || rType.equals(PsiType.NULL);
199 if (rType instanceof GrTupleType) {
200 final GrTupleType tuple = (GrTupleType)rType;
201 if (tuple.getComponentTypes().length == 0) {
202 if (lType instanceof PsiArrayType || InheritanceUtil.isInheritor(lType, JAVA_UTIL_LIST)) {
203 return true;
208 if (lType.equalsToText(JAVA_LANG_STRING)) return true;
210 rType = boxPrimitiveType(rType, manager, scope);
211 lType = boxPrimitiveType(lType, manager, scope);
213 return lType.isAssignableFrom(rType);
216 public static boolean isAssignableByMethodCallConversion(PsiType lType, PsiType rType, PsiManager manager, GlobalSearchScope scope) {
217 if (lType == null || rType == null) return false;
219 if (rType.equalsToText(GrStringUtil.GROOVY_LANG_GSTRING)) {
220 final PsiClass javaLangString = JavaPsiFacade.getInstance(manager.getProject()).findClass(JAVA_LANG_STRING, scope);
221 if (javaLangString != null &&
222 isAssignable(lType, JavaPsiFacade.getElementFactory(manager.getProject()).createType(javaLangString), manager, scope)) {
223 return true;
227 if (isNumericType(lType) && isNumericType(rType)) {
228 lType = unboxPrimitiveTypeWrapper(lType);
229 if (lType.equalsToText(JAVA_MATH_BIG_DECIMAL)) lType = PsiType.DOUBLE;
230 rType = unboxPrimitiveTypeWrapper(rType);
231 if (rType.equalsToText(JAVA_MATH_BIG_DECIMAL)) rType = PsiType.DOUBLE;
233 else {
234 rType = boxPrimitiveType(rType, manager, scope);
235 lType = boxPrimitiveType(lType, manager, scope);
238 return TypeConversionUtil.isAssignable(lType, rType);
242 public static boolean isNumericType(PsiType type) {
243 if (type instanceof PsiClassType) {
244 return TYPE_TO_RANK.contains(type.getCanonicalText());
247 return type instanceof PsiPrimitiveType && TypeConversionUtil.isNumericType(type);
250 public static PsiType unboxPrimitiveTypeWraperAndEraseGenerics(PsiType result) {
251 return TypeConversionUtil.erasure(unboxPrimitiveTypeWrapper(result));
254 public static PsiType unboxPrimitiveTypeWrapper(PsiType type) {
255 if (type instanceof PsiClassType) {
256 PsiType unboxed = ourQNameToUnboxed.get(type.getCanonicalText());
257 if (unboxed != null) type = unboxed;
259 return type;
262 public static PsiType boxPrimitiveTypeAndEraseGenerics(PsiType result, PsiManager manager, GlobalSearchScope resolveScope) {
263 if (result instanceof PsiPrimitiveType) {
264 PsiPrimitiveType primitive = (PsiPrimitiveType)result;
265 String boxedTypeName = primitive.getBoxedTypeName();
266 if (boxedTypeName != null) {
267 return JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createTypeByFQClassName(boxedTypeName, resolveScope);
271 return TypeConversionUtil.erasure(result);
274 public static PsiType boxPrimitiveType(PsiType result, PsiManager manager, GlobalSearchScope resolveScope) {
275 if (result instanceof PsiPrimitiveType && result != PsiType.VOID) {
276 PsiPrimitiveType primitive = (PsiPrimitiveType)result;
277 String boxedTypeName = primitive.getBoxedTypeName();
278 if (boxedTypeName != null) {
279 return JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createTypeByFQClassName(boxedTypeName, resolveScope);
283 return result;
286 @Nullable
287 public static PsiType getTypeForIncOrDecExpression(GrUnaryExpression expr) {
288 final GrExpression op = expr.getOperand();
289 if (op != null) {
290 final PsiType opType = op.getType();
291 if (opType != null) {
292 final PsiType overloaded = getOverloadedOperatorType(opType, expr.getOperationTokenType(), expr, PsiType.EMPTY_ARRAY);
293 if (overloaded != null) {
294 return overloaded;
296 if (isNumericType(opType)) {
297 return opType;
302 return null;
305 public static PsiClassType createType(String fqName, PsiElement context) {
306 JavaPsiFacade facade = JavaPsiFacade.getInstance(context.getProject());
307 return facade.getElementFactory().createTypeByFQClassName(fqName, context.getResolveScope());
310 public static PsiClassType getJavaLangObject(GroovyPsiElement context) {
311 return PsiType.getJavaLangObject(context.getManager(), context.getResolveScope());
314 @Nullable
315 public static PsiType getLeastUpperBound(PsiType type1, PsiType type2, PsiManager manager) {
316 if (type1 instanceof GrTupleType && type2 instanceof GrTupleType) {
317 GrTupleType tuple1 = (GrTupleType)type1;
318 GrTupleType tuple2 = (GrTupleType)type2;
319 PsiType[] components1 = tuple1.getComponentTypes();
320 PsiType[] components2 = tuple2.getComponentTypes();
321 PsiType[] components3 = new PsiType[Math.min(components1.length, components2.length)];
322 for (int i = 0; i < components3.length; i++) {
323 PsiType c1 = components1[i];
324 PsiType c2 = components2[i];
325 if (c1 == null || c2 == null) {
326 components3[i] = null;
328 else {
329 components3[i] = getLeastUpperBound(c1, c2, manager);
332 return new GrTupleType(components3, JavaPsiFacade.getInstance(manager.getProject()),
333 tuple1.getScope().intersectWith(tuple2.getResolveScope()));
335 else if (type1 instanceof GrClosureType && type2 instanceof GrClosureType) {
336 GrClosureType clType1 = (GrClosureType)type1;
337 GrClosureType clType2 = (GrClosureType)type2;
338 PsiType[] parameterTypes1 = clType1.getClosureParameterTypes();
339 PsiType[] parameterTypes2 = clType2.getClosureParameterTypes();
340 if (parameterTypes1.length == parameterTypes2.length) {
341 PsiType[] paramTypes = new PsiType[parameterTypes1.length];
342 boolean[] opts = new boolean[parameterTypes1.length];
343 for (int i = 0; i < paramTypes.length; i++) {
344 paramTypes[i] = GenericsUtil.getGreatestLowerBound(parameterTypes1[i], parameterTypes2[i]);
345 opts[i] = clType1.isOptionalParameter(i) && clType2.isOptionalParameter(i);
347 PsiType returnType = getLeastUpperBound(clType1.getClosureReturnType(), clType2.getClosureReturnType(), manager);
348 GlobalSearchScope scope = clType1.getResolveScope().intersectWith(clType2.getResolveScope());
349 return GrClosureType.create(returnType, paramTypes, opts, manager, scope, LanguageLevel.JDK_1_5);
352 else if (GrStringUtil.GROOVY_LANG_GSTRING.equals(type1.getCanonicalText()) &&
353 CommonClassNames.JAVA_LANG_STRING.equals(type2.getInternalCanonicalText())) {
354 return type2;
356 else if (GrStringUtil.GROOVY_LANG_GSTRING.equals(type2.getCanonicalText()) &&
357 CommonClassNames.JAVA_LANG_STRING.equals(type1.getInternalCanonicalText())) {
358 return type1;
361 return GenericsUtil.getLeastUpperBound(type1, type2, manager);
364 @Nullable
365 public static PsiType getPsiType(PsiElement context, IElementType elemType) {
366 if (elemType == kNULL) {
367 return PsiType.NULL;
369 final String typeName = getPsiTypeName(elemType);
370 if (typeName != null) {
371 return JavaPsiFacade.getElementFactory(context.getProject()).createTypeByFQClassName(typeName, context.getResolveScope());
373 return null;
376 @Nullable
377 private static String getPsiTypeName(IElementType elemType) {
378 return ourPrimitiveTypesToClassNames.get(elemType);