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
;
42 import static com
.intellij
.psi
.CommonClassNames
.*;
43 import static org
.jetbrains
.plugins
.groovy
.lang
.lexer
.GroovyTokenTypes
.*;
48 public class TypesUtil
{
50 public static final Map
<String
, PsiType
> ourQNameToUnboxed
= new HashMap
<String
, PsiType
>();
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
});
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
});
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
);
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());
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";
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
>();
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
>();
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);
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
>();
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
)) {
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
)) {
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
;
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
;
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
);
287 public static PsiType
getTypeForIncOrDecExpression(GrUnaryExpression expr
) {
288 final GrExpression op
= expr
.getOperand();
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) {
296 if (isNumericType(opType
)) {
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());
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;
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())) {
356 else if (GrStringUtil
.GROOVY_LANG_GSTRING
.equals(type2
.getCanonicalText()) &&
357 CommonClassNames
.JAVA_LANG_STRING
.equals(type1
.getInternalCanonicalText())) {
361 return GenericsUtil
.getLeastUpperBound(type1
, type2
, manager
);
365 public static PsiType
getPsiType(PsiElement context
, IElementType elemType
) {
366 if (elemType
== kNULL
) {
369 final String typeName
= getPsiTypeName(elemType
);
370 if (typeName
!= null) {
371 return JavaPsiFacade
.getElementFactory(context
.getProject()).createTypeByFQClassName(typeName
, context
.getResolveScope());
377 private static String
getPsiTypeName(IElementType elemType
) {
378 return ourPrimitiveTypesToClassNames
.get(elemType
);