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
.NotNull
;
29 import org
.jetbrains
.annotations
.Nullable
;
30 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.GroovyPsiElement
;
31 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.GroovyResolveResult
;
32 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.expressions
.GrBinaryExpression
;
33 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.expressions
.GrExpression
;
34 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.expressions
.GrUnaryExpression
;
35 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.impl
.GrClosureType
;
36 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.impl
.GrTupleType
;
37 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.util
.GrStringUtil
;
38 import org
.jetbrains
.plugins
.groovy
.lang
.resolve
.ResolveUtil
;
39 import org
.jetbrains
.plugins
.groovy
.lang
.resolve
.processors
.MethodResolverProcessor
;
43 import static com
.intellij
.psi
.CommonClassNames
.*;
44 import static org
.jetbrains
.plugins
.groovy
.lang
.lexer
.GroovyTokenTypes
.*;
49 public class TypesUtil
{
51 public static final Map
<String
, PsiType
> ourQNameToUnboxed
= new HashMap
<String
, PsiType
>();
57 public static PsiType
getNumericResultType(GrBinaryExpression binaryExpression
, PsiType lType
) {
58 final GrExpression rop
= binaryExpression
.getRightOperand();
59 PsiType rType
= rop
== null ?
null : rop
.getType();
60 if (lType
== null || rType
== null) return null;
61 String lCanonical
= lType
.getCanonicalText();
62 String rCanonical
= rType
.getCanonicalText();
63 if (TYPE_TO_RANK
.containsKey(lCanonical
) && TYPE_TO_RANK
.containsKey(rCanonical
)) {
64 int lRank
= TYPE_TO_RANK
.get(lCanonical
);
65 int rRank
= TYPE_TO_RANK
.get(rCanonical
);
66 int resultRank
= Math
.max(lRank
, rRank
);
67 String qName
= RANK_TO_TYPE
.get(resultRank
);
68 GlobalSearchScope scope
= binaryExpression
.getResolveScope();
69 if (qName
== null) return null;
70 return JavaPsiFacade
.getInstance(binaryExpression
.getProject()).getElementFactory().createTypeByFQClassName(qName
, scope
);
73 final PsiType type
= getOverloadedOperatorType(lType
, binaryExpression
.getOperationTokenType(), binaryExpression
, new PsiType
[]{rType
});
78 if (rType
.equalsToText(GrStringUtil
.GROOVY_LANG_GSTRING
)) {
79 PsiType gstringType
= JavaPsiFacade
.getInstance(binaryExpression
.getProject()).getElementFactory()
80 .createTypeByFQClassName(GrStringUtil
.GROOVY_LANG_GSTRING
, binaryExpression
.getResolveScope());
81 return getOverloadedOperatorType(lType
, binaryExpression
.getOperationTokenType(), binaryExpression
, new PsiType
[]{gstringType
});
87 public static PsiType
getOverloadedOperatorType(PsiType thisType
,
88 IElementType tokenType
,
89 GroovyPsiElement place
,
90 PsiType
[] argumentTypes
) {
91 return getOverloadedOperatorType(thisType
, ourOperationsToOperatorNames
.get(tokenType
), place
, argumentTypes
);
95 public static PsiType
getOverloadedOperatorType(PsiType thisType
, String operatorName
, GroovyPsiElement place
, PsiType
[] argumentTypes
) {
96 if (operatorName
!= null) {
97 MethodResolverProcessor processor
=
98 new MethodResolverProcessor(operatorName
, place
, false, thisType
, argumentTypes
, PsiType
.EMPTY_ARRAY
);
99 if (thisType
instanceof PsiClassType
) {
100 final PsiClassType classtype
= (PsiClassType
)thisType
;
101 final PsiClassType
.ClassResolveResult resolveResult
= classtype
.resolveGenerics();
102 final PsiClass lClass
= resolveResult
.getElement();
103 if (lClass
!= null) {
104 lClass
.processDeclarations(processor
, ResolveState
.initial().put(PsiSubstitutor
.KEY
, resolveResult
.getSubstitutor()), null, place
);
108 ResolveUtil
.processNonCodeMethods(thisType
, processor
, place
.getProject(), place
, false);
109 final GroovyResolveResult
[] candidates
= processor
.getCandidates();
110 if (candidates
.length
== 1) {
111 final PsiElement element
= candidates
[0].getElement();
112 if (element
instanceof PsiMethod
) {
113 return candidates
[0].getSubstitutor().substitute(((PsiMethod
)element
).getReturnType());
119 private static final Map
<IElementType
, String
> ourPrimitiveTypesToClassNames
= new HashMap
<IElementType
, String
>();
120 private static final String NULL
= "null";
121 private static final String JAVA_MATH_BIG_DECIMAL
= "java.math.BigDecimal";
122 private static final String JAVA_MATH_BIG_INTEGER
= "java.math.BigInteger";
125 ourPrimitiveTypesToClassNames
.put(mSTRING_LITERAL
, JAVA_LANG_STRING
);
126 ourPrimitiveTypesToClassNames
.put(mGSTRING_LITERAL
, JAVA_LANG_STRING
);
127 ourPrimitiveTypesToClassNames
.put(mREGEX_LITERAL
, JAVA_LANG_STRING
);
128 ourPrimitiveTypesToClassNames
.put(mNUM_INT
, JAVA_LANG_INTEGER
);
129 ourPrimitiveTypesToClassNames
.put(mNUM_LONG
, JAVA_LANG_LONG
);
130 ourPrimitiveTypesToClassNames
.put(mNUM_FLOAT
, JAVA_LANG_FLOAT
);
131 ourPrimitiveTypesToClassNames
.put(mNUM_DOUBLE
, JAVA_LANG_DOUBLE
);
132 ourPrimitiveTypesToClassNames
.put(mNUM_BIG_INT
, JAVA_MATH_BIG_INTEGER
);
133 ourPrimitiveTypesToClassNames
.put(mNUM_BIG_DECIMAL
, JAVA_MATH_BIG_DECIMAL
);
134 ourPrimitiveTypesToClassNames
.put(kFALSE
, JAVA_LANG_BOOLEAN
);
135 ourPrimitiveTypesToClassNames
.put(kTRUE
, JAVA_LANG_BOOLEAN
);
136 ourPrimitiveTypesToClassNames
.put(kNULL
, NULL
);
139 private static final Map
<IElementType
, String
> ourOperationsToOperatorNames
= new HashMap
<IElementType
, String
>();
142 ourOperationsToOperatorNames
.put(mPLUS
, "plus");
143 ourOperationsToOperatorNames
.put(mMINUS
, "minus");
144 ourOperationsToOperatorNames
.put(mBAND
, "and");
145 ourOperationsToOperatorNames
.put(mBOR
, "or");
146 ourOperationsToOperatorNames
.put(mBXOR
, "xor");
147 ourOperationsToOperatorNames
.put(mDIV
, "div");
148 ourOperationsToOperatorNames
.put(mMOD
, "mod");
149 ourOperationsToOperatorNames
.put(mSTAR
, "multiply");
150 ourOperationsToOperatorNames
.put(mDEC
, "previous");
151 ourOperationsToOperatorNames
.put(mINC
, "next");
154 private static final TObjectIntHashMap
<String
> TYPE_TO_RANK
= new TObjectIntHashMap
<String
>();
157 TYPE_TO_RANK
.put(JAVA_LANG_BYTE
, 1);
158 TYPE_TO_RANK
.put(JAVA_LANG_SHORT
, 2);
159 TYPE_TO_RANK
.put(JAVA_LANG_CHARACTER
, 2);
160 TYPE_TO_RANK
.put(JAVA_LANG_INTEGER
, 3);
161 TYPE_TO_RANK
.put(JAVA_LANG_LONG
, 4);
162 TYPE_TO_RANK
.put(JAVA_MATH_BIG_INTEGER
, 5);
163 TYPE_TO_RANK
.put(JAVA_MATH_BIG_DECIMAL
, 6);
164 TYPE_TO_RANK
.put(JAVA_LANG_FLOAT
, 7);
165 TYPE_TO_RANK
.put(JAVA_LANG_DOUBLE
, 8);
166 TYPE_TO_RANK
.put(JAVA_LANG_NUMBER
, 9);
170 ourQNameToUnboxed
.put(JAVA_LANG_BOOLEAN
, PsiType
.BOOLEAN
);
171 ourQNameToUnboxed
.put(JAVA_LANG_BYTE
, PsiType
.BYTE
);
172 ourQNameToUnboxed
.put(JAVA_LANG_CHARACTER
, PsiType
.CHAR
);
173 ourQNameToUnboxed
.put(JAVA_LANG_SHORT
, PsiType
.SHORT
);
174 ourQNameToUnboxed
.put(JAVA_LANG_INTEGER
, PsiType
.INT
);
175 ourQNameToUnboxed
.put(JAVA_LANG_LONG
, PsiType
.LONG
);
176 ourQNameToUnboxed
.put(JAVA_LANG_FLOAT
, PsiType
.FLOAT
);
177 ourQNameToUnboxed
.put(JAVA_LANG_DOUBLE
, PsiType
.DOUBLE
);
181 private static final TIntObjectHashMap
<String
> RANK_TO_TYPE
= new TIntObjectHashMap
<String
>();
184 RANK_TO_TYPE
.put(1, JAVA_LANG_INTEGER
);
185 RANK_TO_TYPE
.put(2, JAVA_LANG_INTEGER
);
186 RANK_TO_TYPE
.put(3, JAVA_LANG_INTEGER
);
187 RANK_TO_TYPE
.put(4, JAVA_LANG_LONG
);
188 RANK_TO_TYPE
.put(5, JAVA_MATH_BIG_INTEGER
);
189 RANK_TO_TYPE
.put(6, JAVA_MATH_BIG_DECIMAL
);
190 RANK_TO_TYPE
.put(7, JAVA_LANG_DOUBLE
);
191 RANK_TO_TYPE
.put(8, JAVA_LANG_DOUBLE
);
192 RANK_TO_TYPE
.put(9, JAVA_LANG_NUMBER
);
195 public static boolean isAssignable(PsiType lType
, PsiType rType
, PsiManager manager
, GlobalSearchScope scope
) {
196 //all numeric types are assignable
197 if (isNumericType(lType
)) {
198 return isNumericType(rType
) || rType
.equals(PsiType
.NULL
);
200 if (rType
instanceof GrTupleType
) {
201 final GrTupleType tuple
= (GrTupleType
)rType
;
202 if (tuple
.getComponentTypes().length
== 0) {
203 if (lType
instanceof PsiArrayType
||
204 InheritanceUtil
.isInheritor(lType
, JAVA_UTIL_LIST
) ||
205 InheritanceUtil
.isInheritor(lType
, JAVA_UTIL_SET
)) {
211 if (lType
.equalsToText(JAVA_LANG_STRING
)) return true;
213 rType
= boxPrimitiveType(rType
, manager
, scope
);
214 lType
= boxPrimitiveType(lType
, manager
, scope
);
216 return lType
.isAssignableFrom(rType
);
219 public static boolean isAssignableByMethodCallConversion(PsiType lType
, PsiType rType
, PsiManager manager
, GlobalSearchScope scope
) {
220 if (lType
== null || rType
== null) return false;
222 if (rType
instanceof GrTupleType
) {
223 final GrTupleType tuple
= (GrTupleType
)rType
;
224 if (tuple
.getComponentTypes().length
== 0) {
225 if (lType
instanceof PsiArrayType
||
226 InheritanceUtil
.isInheritor(lType
, JAVA_UTIL_LIST
) ||
227 InheritanceUtil
.isInheritor(lType
, JAVA_UTIL_SET
)) {
233 if (rType
.equalsToText(GrStringUtil
.GROOVY_LANG_GSTRING
)) {
234 final PsiClass javaLangString
= JavaPsiFacade
.getInstance(manager
.getProject()).findClass(JAVA_LANG_STRING
, scope
);
235 if (javaLangString
!= null &&
236 isAssignable(lType
, JavaPsiFacade
.getElementFactory(manager
.getProject()).createType(javaLangString
), manager
, scope
)) {
241 if (isNumericType(lType
) && isNumericType(rType
)) {
242 lType
= unboxPrimitiveTypeWrapper(lType
);
243 if (lType
.equalsToText(JAVA_MATH_BIG_DECIMAL
)) lType
= PsiType
.DOUBLE
;
244 rType
= unboxPrimitiveTypeWrapper(rType
);
245 if (rType
.equalsToText(JAVA_MATH_BIG_DECIMAL
)) rType
= PsiType
.DOUBLE
;
248 rType
= boxPrimitiveType(rType
, manager
, scope
);
249 lType
= boxPrimitiveType(lType
, manager
, scope
);
252 return TypeConversionUtil
.isAssignable(lType
, rType
);
256 public static boolean isNumericType(PsiType type
) {
257 if (type
instanceof PsiClassType
) {
258 return TYPE_TO_RANK
.contains(type
.getCanonicalText());
261 return type
instanceof PsiPrimitiveType
&& TypeConversionUtil
.isNumericType(type
);
264 public static PsiType
unboxPrimitiveTypeWraperAndEraseGenerics(PsiType result
) {
265 return TypeConversionUtil
.erasure(unboxPrimitiveTypeWrapper(result
));
268 public static PsiType
unboxPrimitiveTypeWrapper(PsiType type
) {
269 if (type
instanceof PsiClassType
) {
270 PsiType unboxed
= ourQNameToUnboxed
.get(type
.getCanonicalText());
271 if (unboxed
!= null) type
= unboxed
;
276 public static PsiType
boxPrimitiveType(PsiType result
, PsiManager manager
, GlobalSearchScope resolveScope
) {
277 if (result
instanceof PsiPrimitiveType
&& result
!= PsiType
.VOID
) {
278 PsiPrimitiveType primitive
= (PsiPrimitiveType
)result
;
279 String boxedTypeName
= primitive
.getBoxedTypeName();
280 if (boxedTypeName
!= null) {
281 return JavaPsiFacade
.getInstance(manager
.getProject()).getElementFactory().createTypeByFQClassName(boxedTypeName
, resolveScope
);
289 public static PsiType
getTypeForIncOrDecExpression(GrUnaryExpression expr
) {
290 final GrExpression op
= expr
.getOperand();
292 final PsiType opType
= op
.getType();
293 if (opType
!= null) {
294 final PsiType overloaded
= getOverloadedOperatorType(opType
, expr
.getOperationTokenType(), expr
, PsiType
.EMPTY_ARRAY
);
295 if (overloaded
!= null) {
298 if (isNumericType(opType
)) {
307 public static PsiClassType
createType(String fqName
, PsiElement context
) {
308 JavaPsiFacade facade
= JavaPsiFacade
.getInstance(context
.getProject());
309 return facade
.getElementFactory().createTypeByFQClassName(fqName
, context
.getResolveScope());
312 public static PsiClassType
getJavaLangObject(PsiElement context
) {
313 return PsiType
.getJavaLangObject(context
.getManager(), context
.getResolveScope());
317 public static PsiType
getLeastUpperBound(@NotNull PsiType type1
, @NotNull PsiType type2
, PsiManager manager
) {
318 if (type1
instanceof GrTupleType
&& type2
instanceof GrTupleType
) {
319 GrTupleType tuple1
= (GrTupleType
)type1
;
320 GrTupleType tuple2
= (GrTupleType
)type2
;
321 PsiType
[] components1
= tuple1
.getComponentTypes();
322 PsiType
[] components2
= tuple2
.getComponentTypes();
323 PsiType
[] components3
= new PsiType
[Math
.min(components1
.length
, components2
.length
)];
324 for (int i
= 0; i
< components3
.length
; i
++) {
325 PsiType c1
= components1
[i
];
326 PsiType c2
= components2
[i
];
327 if (c1
== null || c2
== null) {
328 components3
[i
] = null;
331 components3
[i
] = getLeastUpperBound(c1
, c2
, manager
);
334 return new GrTupleType(components3
, JavaPsiFacade
.getInstance(manager
.getProject()),
335 tuple1
.getScope().intersectWith(tuple2
.getResolveScope()));
337 else if (type1
instanceof GrClosureType
&& type2
instanceof GrClosureType
) {
338 GrClosureType clType1
= (GrClosureType
)type1
;
339 GrClosureType clType2
= (GrClosureType
)type2
;
340 PsiType
[] parameterTypes1
= clType1
.getClosureParameterTypes();
341 PsiType
[] parameterTypes2
= clType2
.getClosureParameterTypes();
342 if (parameterTypes1
.length
== parameterTypes2
.length
) {
343 PsiType
[] paramTypes
= new PsiType
[parameterTypes1
.length
];
344 boolean[] opts
= new boolean[parameterTypes1
.length
];
345 for (int i
= 0; i
< paramTypes
.length
; i
++) {
346 paramTypes
[i
] = GenericsUtil
.getGreatestLowerBound(parameterTypes1
[i
], parameterTypes2
[i
]);
347 opts
[i
] = clType1
.isOptionalParameter(i
) && clType2
.isOptionalParameter(i
);
349 final PsiType ret1
= clType1
.getClosureReturnType();
350 final PsiType ret2
= clType2
.getClosureReturnType();
351 PsiType returnType
= ret1
== null ? ret2
: ret2
== null ? ret1
: getLeastUpperBound(ret1
, ret2
, manager
);
352 GlobalSearchScope scope
= clType1
.getResolveScope().intersectWith(clType2
.getResolveScope());
353 return GrClosureType
.create(returnType
, paramTypes
, opts
, manager
, scope
, LanguageLevel
.JDK_1_5
);
356 else if (GrStringUtil
.GROOVY_LANG_GSTRING
.equals(type1
.getCanonicalText()) &&
357 CommonClassNames
.JAVA_LANG_STRING
.equals(type2
.getInternalCanonicalText())) {
360 else if (GrStringUtil
.GROOVY_LANG_GSTRING
.equals(type2
.getCanonicalText()) &&
361 CommonClassNames
.JAVA_LANG_STRING
.equals(type1
.getInternalCanonicalText())) {
365 return GenericsUtil
.getLeastUpperBound(type1
, type2
, manager
);
369 public static PsiType
getPsiType(PsiElement context
, IElementType elemType
) {
370 if (elemType
== kNULL
) {
373 final String typeName
= getPsiTypeName(elemType
);
374 if (typeName
!= null) {
375 return JavaPsiFacade
.getElementFactory(context
.getProject()).createTypeByFQClassName(typeName
, context
.getResolveScope());
381 private static String
getPsiTypeName(IElementType elemType
) {
382 return ourPrimitiveTypesToClassNames
.get(elemType
);