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
.equalsToText(GrStringUtil
.GROOVY_LANG_GSTRING
)) {
223 final PsiClass javaLangString
= JavaPsiFacade
.getInstance(manager
.getProject()).findClass(JAVA_LANG_STRING
, scope
);
224 if (javaLangString
!= null &&
225 isAssignable(lType
, JavaPsiFacade
.getElementFactory(manager
.getProject()).createType(javaLangString
), manager
, scope
)) {
230 if (isNumericType(lType
) && isNumericType(rType
)) {
231 lType
= unboxPrimitiveTypeWrapper(lType
);
232 if (lType
.equalsToText(JAVA_MATH_BIG_DECIMAL
)) lType
= PsiType
.DOUBLE
;
233 rType
= unboxPrimitiveTypeWrapper(rType
);
234 if (rType
.equalsToText(JAVA_MATH_BIG_DECIMAL
)) rType
= PsiType
.DOUBLE
;
237 rType
= boxPrimitiveType(rType
, manager
, scope
);
238 lType
= boxPrimitiveType(lType
, manager
, scope
);
241 return TypeConversionUtil
.isAssignable(lType
, rType
);
245 public static boolean isNumericType(PsiType type
) {
246 if (type
instanceof PsiClassType
) {
247 return TYPE_TO_RANK
.contains(type
.getCanonicalText());
250 return type
instanceof PsiPrimitiveType
&& TypeConversionUtil
.isNumericType(type
);
253 public static PsiType
unboxPrimitiveTypeWraperAndEraseGenerics(PsiType result
) {
254 return TypeConversionUtil
.erasure(unboxPrimitiveTypeWrapper(result
));
257 public static PsiType
unboxPrimitiveTypeWrapper(PsiType type
) {
258 if (type
instanceof PsiClassType
) {
259 PsiType unboxed
= ourQNameToUnboxed
.get(type
.getCanonicalText());
260 if (unboxed
!= null) type
= unboxed
;
265 public static PsiType
boxPrimitiveType(PsiType result
, PsiManager manager
, GlobalSearchScope resolveScope
) {
266 if (result
instanceof PsiPrimitiveType
&& result
!= PsiType
.VOID
) {
267 PsiPrimitiveType primitive
= (PsiPrimitiveType
)result
;
268 String boxedTypeName
= primitive
.getBoxedTypeName();
269 if (boxedTypeName
!= null) {
270 return JavaPsiFacade
.getInstance(manager
.getProject()).getElementFactory().createTypeByFQClassName(boxedTypeName
, resolveScope
);
278 public static PsiType
getTypeForIncOrDecExpression(GrUnaryExpression expr
) {
279 final GrExpression op
= expr
.getOperand();
281 final PsiType opType
= op
.getType();
282 if (opType
!= null) {
283 final PsiType overloaded
= getOverloadedOperatorType(opType
, expr
.getOperationTokenType(), expr
, PsiType
.EMPTY_ARRAY
);
284 if (overloaded
!= null) {
287 if (isNumericType(opType
)) {
296 public static PsiClassType
createType(String fqName
, PsiElement context
) {
297 JavaPsiFacade facade
= JavaPsiFacade
.getInstance(context
.getProject());
298 return facade
.getElementFactory().createTypeByFQClassName(fqName
, context
.getResolveScope());
301 public static PsiClassType
getJavaLangObject(GroovyPsiElement context
) {
302 return PsiType
.getJavaLangObject(context
.getManager(), context
.getResolveScope());
306 public static PsiType
getLeastUpperBound(@NotNull PsiType type1
, @NotNull PsiType type2
, PsiManager manager
) {
307 if (type1
instanceof GrTupleType
&& type2
instanceof GrTupleType
) {
308 GrTupleType tuple1
= (GrTupleType
)type1
;
309 GrTupleType tuple2
= (GrTupleType
)type2
;
310 PsiType
[] components1
= tuple1
.getComponentTypes();
311 PsiType
[] components2
= tuple2
.getComponentTypes();
312 PsiType
[] components3
= new PsiType
[Math
.min(components1
.length
, components2
.length
)];
313 for (int i
= 0; i
< components3
.length
; i
++) {
314 PsiType c1
= components1
[i
];
315 PsiType c2
= components2
[i
];
316 if (c1
== null || c2
== null) {
317 components3
[i
] = null;
320 components3
[i
] = getLeastUpperBound(c1
, c2
, manager
);
323 return new GrTupleType(components3
, JavaPsiFacade
.getInstance(manager
.getProject()),
324 tuple1
.getScope().intersectWith(tuple2
.getResolveScope()));
326 else if (type1
instanceof GrClosureType
&& type2
instanceof GrClosureType
) {
327 GrClosureType clType1
= (GrClosureType
)type1
;
328 GrClosureType clType2
= (GrClosureType
)type2
;
329 PsiType
[] parameterTypes1
= clType1
.getClosureParameterTypes();
330 PsiType
[] parameterTypes2
= clType2
.getClosureParameterTypes();
331 if (parameterTypes1
.length
== parameterTypes2
.length
) {
332 PsiType
[] paramTypes
= new PsiType
[parameterTypes1
.length
];
333 boolean[] opts
= new boolean[parameterTypes1
.length
];
334 for (int i
= 0; i
< paramTypes
.length
; i
++) {
335 paramTypes
[i
] = GenericsUtil
.getGreatestLowerBound(parameterTypes1
[i
], parameterTypes2
[i
]);
336 opts
[i
] = clType1
.isOptionalParameter(i
) && clType2
.isOptionalParameter(i
);
338 final PsiType ret1
= clType1
.getClosureReturnType();
339 final PsiType ret2
= clType2
.getClosureReturnType();
340 PsiType returnType
= ret1
== null ? ret2
: ret2
== null ? ret1
: getLeastUpperBound(ret1
, ret2
, manager
);
341 GlobalSearchScope scope
= clType1
.getResolveScope().intersectWith(clType2
.getResolveScope());
342 return GrClosureType
.create(returnType
, paramTypes
, opts
, manager
, scope
, LanguageLevel
.JDK_1_5
);
345 else if (GrStringUtil
.GROOVY_LANG_GSTRING
.equals(type1
.getCanonicalText()) &&
346 CommonClassNames
.JAVA_LANG_STRING
.equals(type2
.getInternalCanonicalText())) {
349 else if (GrStringUtil
.GROOVY_LANG_GSTRING
.equals(type2
.getCanonicalText()) &&
350 CommonClassNames
.JAVA_LANG_STRING
.equals(type1
.getInternalCanonicalText())) {
354 return GenericsUtil
.getLeastUpperBound(type1
, type2
, manager
);
358 public static PsiType
getPsiType(PsiElement context
, IElementType elemType
) {
359 if (elemType
== kNULL
) {
362 final String typeName
= getPsiTypeName(elemType
);
363 if (typeName
!= null) {
364 return JavaPsiFacade
.getElementFactory(context
.getProject()).createTypeByFQClassName(typeName
, context
.getResolveScope());
370 private static String
getPsiTypeName(IElementType elemType
) {
371 return ourPrimitiveTypesToClassNames
.get(elemType
);