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.
17 package org
.jetbrains
.plugins
.groovy
.lang
.psi
.util
;
19 import com
.intellij
.openapi
.util
.Condition
;
20 import com
.intellij
.openapi
.util
.Pair
;
21 import com
.intellij
.psi
.*;
22 import com
.intellij
.psi
.impl
.PsiClassImplUtil
;
23 import com
.intellij
.psi
.infos
.CandidateInfo
;
24 import com
.intellij
.psi
.scope
.NameHint
;
25 import com
.intellij
.psi
.scope
.PsiScopeProcessor
;
26 import com
.intellij
.psi
.util
.MethodSignature
;
27 import com
.intellij
.psi
.util
.TypeConversionUtil
;
28 import com
.intellij
.util
.ArrayUtil
;
29 import com
.intellij
.util
.Function
;
30 import com
.intellij
.util
.containers
.ContainerUtil
;
31 import org
.jetbrains
.annotations
.NonNls
;
32 import org
.jetbrains
.annotations
.NotNull
;
33 import org
.jetbrains
.annotations
.Nullable
;
34 import org
.jetbrains
.plugins
.groovy
.GroovyFileType
;
35 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.GrField
;
36 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.blocks
.GrClosableBlock
;
37 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.expressions
.GrExpression
;
38 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.expressions
.GrReferenceExpression
;
39 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.expressions
.path
.GrMethodCallExpression
;
40 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.typedef
.GrAnonymousClassDefinition
;
41 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.typedef
.GrReferenceList
;
42 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.typedef
.GrTypeDefinition
;
43 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.typedef
.GrTypeDefinitionBody
;
44 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.typedef
.members
.GrAccessorMethod
;
45 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.statements
.typedef
.members
.GrGdkMethod
;
46 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.api
.types
.GrCodeReferenceElement
;
47 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.impl
.GrClassReferenceType
;
48 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.impl
.PsiImplUtil
;
49 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.impl
.statements
.expressions
.TypesUtil
;
50 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.impl
.statements
.typedef
.GrTypeDefinitionImpl
;
51 import org
.jetbrains
.plugins
.groovy
.lang
.psi
.impl
.synthetic
.GrSyntheticMethodImplementation
;
52 import org
.jetbrains
.plugins
.groovy
.lang
.resolve
.CollectClassMembersUtil
;
53 import org
.jetbrains
.plugins
.groovy
.lang
.resolve
.ResolveUtil
;
54 import org
.jetbrains
.plugins
.groovy
.lang
.resolve
.processors
.ClassHint
;
59 * @author Maxim.Medvedev
61 public class GrClassImplUtil
{
62 private static final Condition
<PsiClassType
> IS_GROOVY_OBJECT
= new Condition
<PsiClassType
>() {
63 public boolean value(PsiClassType psiClassType
) {
64 return psiClassType
.equalsToText(GrTypeDefinition
.DEFAULT_BASE_CLASS_NAME
);
68 private GrClassImplUtil() {
73 public static PsiClass
getSuperClass(GrTypeDefinition grType
) {
74 final PsiClassType
[] extendsList
= grType
.getExtendsListTypes();
75 if (extendsList
.length
== 0) return getBaseClass(grType
);
76 final PsiClass superClass
= extendsList
[0].resolve();
77 return superClass
!= null ? superClass
: getBaseClass(grType
);
81 public static PsiClass
getBaseClass(GrTypeDefinition grType
) {
82 if (grType
.isEnum()) {
83 return JavaPsiFacade
.getInstance(grType
.getProject()).findClass(CommonClassNames
.JAVA_LANG_ENUM
, grType
.getResolveScope());
86 return JavaPsiFacade
.getInstance(grType
.getProject()).findClass(CommonClassNames
.JAVA_LANG_OBJECT
, grType
.getResolveScope());
91 public static PsiClassType
[] getExtendsListTypes(GrTypeDefinition grType
) {
92 final List
<PsiClassType
> extendsTypes
= getReferenceListTypes(grType
.getExtendsClause());
93 return extendsTypes
.toArray(new PsiClassType
[extendsTypes
.size()]);
97 public static PsiClassType
[] getImplementsListTypes(GrTypeDefinition grType
) {
98 final List
<PsiClassType
> implementsTypes
= getReferenceListTypes(grType
.getImplementsClause());
99 if (!grType
.isInterface() &&
100 !ContainerUtil
.or(implementsTypes
, IS_GROOVY_OBJECT
) &&
101 !ContainerUtil
.or(getReferenceListTypes(grType
.getExtendsClause()), IS_GROOVY_OBJECT
)) {
102 implementsTypes
.add(getGroovyObjectType(grType
));
104 return implementsTypes
.toArray(new PsiClassType
[implementsTypes
.size()]);
107 private static PsiClassType
getGroovyObjectType(GrTypeDefinition grType
) {
108 return JavaPsiFacade
.getInstance(grType
.getProject()).getElementFactory()
109 .createTypeByFQClassName(GrTypeDefinition
.DEFAULT_BASE_CLASS_NAME
, grType
.getResolveScope());
113 public static PsiClassType
[] getSuperTypes(GrTypeDefinition grType
) {
114 PsiClassType
[] extendsList
= grType
.getExtendsListTypes();
115 if (extendsList
.length
== 0) {
116 extendsList
= new PsiClassType
[]{createBaseClassType(grType
)};
119 return ArrayUtil
.mergeArrays(extendsList
, grType
.getImplementsListTypes(), PsiClassType
.class);
122 public static PsiClassType
createBaseClassType(GrTypeDefinition grType
) {
123 if (grType
.isEnum()) {
124 return JavaPsiFacade
.getInstance(grType
.getProject()).getElementFactory()
125 .createTypeByFQClassName(CommonClassNames
.JAVA_LANG_ENUM
, grType
.getResolveScope());
128 return JavaPsiFacade
.getInstance(grType
.getProject()).getElementFactory()
129 .createTypeByFQClassName(CommonClassNames
.JAVA_LANG_OBJECT
, grType
.getResolveScope());
134 public static PsiMethod
[] getAllMethods(GrTypeDefinition grType
) {
135 List
<PsiMethod
> allMethods
= new ArrayList
<PsiMethod
>();
136 getAllMethodsInner(grType
, allMethods
, new HashSet
<PsiClass
>());
138 return allMethods
.toArray(new PsiMethod
[allMethods
.size()]);
141 private static void getAllMethodsInner(PsiClass clazz
, List
<PsiMethod
> allMethods
, HashSet
<PsiClass
> visited
) {
142 if (visited
.contains(clazz
)) return;
145 allMethods
.addAll(Arrays
.asList(clazz
.getMethods()));
147 final PsiField
[] fields
= clazz
.getFields();
148 for (PsiField field
: fields
) {
149 if (field
instanceof GrField
) {
150 final GrField groovyField
= (GrField
)field
;
151 if (groovyField
.isProperty()) {
152 PsiMethod
[] getters
= groovyField
.getGetters();
153 if (getters
.length
> 0) allMethods
.addAll(Arrays
.asList(getters
));
154 PsiMethod setter
= groovyField
.getSetter();
155 if (setter
!= null) allMethods
.add(setter
);
160 // addGroovyObjectMethods(clazz, allMethods);
162 final PsiClass
[] supers
= clazz
.getSupers();
163 if (supers
.length
<2) {
164 addGroovyObjectMethods(clazz
, allMethods
);
166 for (PsiClass aSuper
: supers
) {
167 getAllMethodsInner(aSuper
, allMethods
, visited
);
171 public static void addGroovyObjectMethods(PsiClass clazz
, List
<PsiMethod
> allMethods
) {
172 if (clazz
instanceof GrTypeDefinition
&& !clazz
.isInterface() /*&& clazz.getExtendsListTypes().length == 0*/) {
173 final PsiClass groovyObject
=
174 JavaPsiFacade
.getInstance(clazz
.getProject()).findClass(GrTypeDefinition
.DEFAULT_BASE_CLASS_NAME
, clazz
.getResolveScope());
175 if (groovyObject
!= null) {
176 for (final PsiMethod method
: groovyObject
.getMethods()) {
177 allMethods
.add(new GrSyntheticMethodImplementation(method
, clazz
));
184 private static List
<PsiClassType
> getReferenceListTypes(@Nullable GrReferenceList list
) {
185 final ArrayList
<PsiClassType
> types
= new ArrayList
<PsiClassType
>();
187 for (GrCodeReferenceElement ref
: list
.getReferenceElements()) {
188 types
.add(new GrClassReferenceType(ref
));
195 public static PsiClass
[] getInterfaces(GrTypeDefinition grType
) {
196 final PsiClassType
[] implementsListTypes
= grType
.getImplementsListTypes();
197 List
<PsiClass
> result
= new ArrayList
<PsiClass
>(implementsListTypes
.length
);
198 for (PsiClassType type
: implementsListTypes
) {
199 final PsiClass psiClass
= type
.resolve();
200 if (psiClass
!= null) result
.add(psiClass
);
202 return result
.toArray(new PsiClass
[result
.size()]);
206 public static PsiClass
[] getSupers(GrTypeDefinition grType
) {
207 PsiClassType
[] superTypes
= grType
.getSuperTypes();
208 List
<PsiClass
> result
= new ArrayList
<PsiClass
>();
209 for (PsiClassType superType
: superTypes
) {
210 PsiClass superClass
= superType
.resolve();
211 if (superClass
!= null) {
212 result
.add(superClass
);
216 return result
.toArray(new PsiClass
[result
.size()]);
219 public static boolean processDeclarations(@NotNull GrTypeDefinition grType
,
220 @NotNull PsiScopeProcessor processor
,
221 @NotNull ResolveState state
,
222 PsiElement lastParent
,
223 @NotNull PsiElement place
) {
224 for (final PsiTypeParameter typeParameter
: grType
.getTypeParameters()) {
225 if (!ResolveUtil
.processElement(processor
, typeParameter
)) return false;
228 NameHint nameHint
= processor
.getHint(NameHint
.KEY
);
229 //todo [DIANA] look more carefully
230 String name
= nameHint
== null ?
null : nameHint
.getName(state
);
231 ClassHint classHint
= processor
.getHint(ClassHint
.KEY
);
232 if (classHint
== null || classHint
.shouldProcess(ClassHint
.ResolveKind
.PROPERTY
)) {
233 Map
<String
, CandidateInfo
> fieldsMap
= CollectClassMembersUtil
.getAllFields(grType
);
235 CandidateInfo fieldInfo
= fieldsMap
.get(name
);
236 if (fieldInfo
!= null) {
237 final PsiElement element
= fieldInfo
.getElement();
238 if (!isSameDeclaration(place
, element
)) { //the same variable declaration
239 if (!processor
.execute(element
, ResolveState
.initial().put(PsiSubstitutor
.KEY
, state
.get(PsiSubstitutor
.KEY
)))) return false;
244 for (CandidateInfo info
: fieldsMap
.values()) {
245 final PsiElement element
= info
.getElement();
246 if (!isSameDeclaration(place
, element
)) { //the same variable declaration
247 if (!processor
.execute(element
, ResolveState
.initial().put(PsiSubstitutor
.KEY
, state
.get(PsiSubstitutor
.KEY
)))) return false;
253 if (classHint
== null || classHint
.shouldProcess(ClassHint
.ResolveKind
.METHOD
)) {
254 Map
<String
, List
<CandidateInfo
>> methodsMap
= CollectClassMembersUtil
.getAllMethods(grType
, true);
255 boolean isPlaceGroovy
= place
.getLanguage() == GroovyFileType
.GROOVY_FILE_TYPE
.getLanguage();
257 for (List
<CandidateInfo
> list
: methodsMap
.values()) {
258 for (CandidateInfo info
: list
) {
259 PsiMethod method
= (PsiMethod
)info
.getElement();
260 if (!isSameDeclaration(place
, method
) &&
261 isMethodVisible(isPlaceGroovy
, method
) &&
262 !processor
.execute(method
, ResolveState
.initial().put(PsiSubstitutor
.KEY
, state
.get(PsiSubstitutor
.KEY
)))) {
269 List
<CandidateInfo
> byName
= methodsMap
.get(name
);
270 if (byName
!= null) {
271 for (CandidateInfo info
: byName
) {
272 PsiMethod method
= (PsiMethod
)info
.getElement();
273 if (!isSameDeclaration(place
, method
) &&
274 isMethodVisible(isPlaceGroovy
, method
) &&
275 !processor
.execute(method
, ResolveState
.initial().put(PsiSubstitutor
.KEY
, state
.get(PsiSubstitutor
.KEY
)))) {
283 final GrTypeDefinitionBody body
= grType
.getBody();
284 if (body
!= null && !isSuperClassReferenceResolving(grType
, lastParent
)) {
285 if (classHint
== null || classHint
.shouldProcess(ClassHint
.ResolveKind
.CLASS
)) {
286 for (CandidateInfo info
: CollectClassMembersUtil
.getAllInnerClasses(grType
, false).values()) {
287 final PsiClass innerClass
= (PsiClass
)info
.getElement();
288 assert innerClass
!= null;
289 final String innerClassName
= innerClass
.getName();
290 if (nameHint
!= null && !innerClassName
.equals(nameHint
.getName(state
))) {
294 if (!processor
.execute(innerClass
, state
)) {
305 private static boolean isSuperClassReferenceResolving(GrTypeDefinition grType
, PsiElement lastParent
) {
306 return lastParent
instanceof GrReferenceList
||
307 grType
.isAnonymous() && lastParent
== ((GrAnonymousClassDefinition
)grType
).getBaseClassReferenceGroovy();
311 private static boolean isSameDeclaration(PsiElement place
, PsiElement element
) {
312 if (element
instanceof GrAccessorMethod
) element
= ((GrAccessorMethod
)element
).getProperty();
314 if (!(element
instanceof GrField
)) return false;
315 while (place
!= null) {
316 place
= place
.getParent();
317 if (place
== element
) return true;
318 if (place
instanceof GrClosableBlock
) return false;
323 private static boolean isMethodVisible(boolean isPlaceGroovy
, PsiMethod method
) {
324 return isPlaceGroovy
|| !(method
instanceof GrGdkMethod
);
327 private static boolean isPropertyReference(PsiElement place
, PsiField aField
, boolean isGetter
) {
328 //filter only in groovy, todo: analyze java place
329 if (place
.getLanguage() != GroovyFileType
.GROOVY_FILE_TYPE
.getLanguage()) return true;
331 if (place
instanceof GrReferenceExpression
) {
332 final PsiElement parent
= place
.getParent();
333 if (parent
instanceof GrMethodCallExpression
) {
334 final GrMethodCallExpression call
= (GrMethodCallExpression
)parent
;
335 if (call
.getNamedArguments().length
> 0 || call
.getClosureArguments().length
> 0) return false;
336 final GrExpression
[] args
= call
.getExpressionArguments();
338 return args
.length
== 0;
341 return args
.length
== 1 &&
343 .isAssignableByMethodCallConversion(aField
.getType(), args
[0].getType(), place
.getManager(), place
.getResolveScope());
352 public static PsiMethod
findMethodBySignature(GrTypeDefinition grType
, PsiMethod patternMethod
, boolean checkBases
) {
353 final MethodSignature patternSignature
= patternMethod
.getSignature(PsiSubstitutor
.EMPTY
);
354 for (PsiMethod method
: findMethodsByName(grType
, patternMethod
.getName(), checkBases
, false)) {
355 final PsiClass clazz
= method
.getContainingClass();
356 if (clazz
== null) continue;
357 PsiSubstitutor superSubstitutor
= TypeConversionUtil
.getClassSubstitutor(clazz
, grType
, PsiSubstitutor
.EMPTY
);
358 if (superSubstitutor
== null) continue;
359 final MethodSignature signature
= method
.getSignature(superSubstitutor
);
360 if (signature
.equals(patternSignature
)) return method
;
366 private static PsiMethod
[] findMethodsByName(GrTypeDefinition grType
,
369 boolean includeSyntheticAccessors
) {
371 List
<PsiMethod
> result
= new ArrayList
<PsiMethod
>();
372 for (PsiMethod method
: includeSyntheticAccessors ? grType
.getMethods() : grType
.getGroovyMethods()) {
373 if (name
.equals(method
.getName())) result
.add(method
);
376 return result
.toArray(new PsiMethod
[result
.size()]);
379 Map
<String
, List
<CandidateInfo
>> methodsMap
= CollectClassMembersUtil
.getAllMethods(grType
, includeSyntheticAccessors
);
380 return PsiImplUtil
.mapToMethods(methodsMap
.get(name
));
384 public static PsiMethod
[] findMethodsBySignature(GrTypeDefinition grType
, PsiMethod patternMethod
, boolean checkBases
) {
385 return findMethodsBySignature(grType
, patternMethod
, checkBases
, true);
389 public static PsiMethod
[] findCodeMethodsBySignature(GrTypeDefinition grType
, PsiMethod patternMethod
, boolean checkBases
) {
390 return findMethodsBySignature(grType
, patternMethod
, checkBases
, false);
394 public static PsiMethod
[] findMethodsByName(GrTypeDefinition grType
, @NonNls String name
, boolean checkBases
) {
395 return findMethodsByName(grType
, name
, checkBases
, true);
398 private static PsiMethod
[] findMethodsBySignature(GrTypeDefinition grType
,
399 PsiMethod patternMethod
,
401 boolean includeSynthetic
) {
402 ArrayList
<PsiMethod
> result
= new ArrayList
<PsiMethod
>();
403 final MethodSignature patternSignature
= patternMethod
.getSignature(PsiSubstitutor
.EMPTY
);
404 for (PsiMethod method
: findMethodsByName(grType
, patternMethod
.getName(), checkBases
, includeSynthetic
)) {
405 final PsiClass clazz
= method
.getContainingClass();
406 if (clazz
== null) continue;
407 PsiSubstitutor superSubstitutor
= TypeConversionUtil
.getClassSubstitutor(clazz
, grType
, PsiSubstitutor
.EMPTY
);
408 if (superSubstitutor
== null) continue;
410 final MethodSignature signature
= method
.getSignature(superSubstitutor
);
411 if (signature
.equals(patternSignature
)) //noinspection unchecked
416 return result
.toArray(new PsiMethod
[result
.size()]);
420 public static PsiMethod
[] findCodeMethodsByName(GrTypeDefinition grType
, @NonNls String name
, boolean checkBases
) {
421 return findMethodsByName(grType
, name
, checkBases
, false);
425 public static List
<Pair
<PsiMethod
, PsiSubstitutor
>> findMethodsAndTheirSubstitutorsByName(GrTypeDefinition grType
,
427 boolean checkBases
) {
428 final ArrayList
<Pair
<PsiMethod
, PsiSubstitutor
>> result
= new ArrayList
<Pair
<PsiMethod
, PsiSubstitutor
>>();
431 final PsiMethod
[] methods
= grType
.findMethodsByName( name
, false);
432 for (PsiMethod method
: methods
) {
433 result
.add(new Pair
<PsiMethod
, PsiSubstitutor
>(method
, PsiSubstitutor
.EMPTY
));
437 final Map
<String
, List
<CandidateInfo
>> map
= CollectClassMembersUtil
.getAllMethods(grType
, true);
438 final List
<CandidateInfo
> candidateInfos
= map
.get(name
);
439 if (candidateInfos
!= null) {
440 for (CandidateInfo info
: candidateInfos
) {
441 final PsiElement element
= info
.getElement();
442 result
.add(new Pair
<PsiMethod
, PsiSubstitutor
>((PsiMethod
)element
, info
.getSubstitutor()));
451 public static List
<Pair
<PsiMethod
, PsiSubstitutor
>> getAllMethodsAndTheirSubstitutors(GrTypeDefinition grType
) {
452 final Map
<String
, List
<CandidateInfo
>> allMethodsMap
= CollectClassMembersUtil
.getAllMethods(grType
, true);
453 List
<Pair
<PsiMethod
, PsiSubstitutor
>> result
= new ArrayList
<Pair
<PsiMethod
, PsiSubstitutor
>>();
454 for (List
<CandidateInfo
> infos
: allMethodsMap
.values()) {
455 for (CandidateInfo info
: infos
) {
456 result
.add(new Pair
<PsiMethod
, PsiSubstitutor
>((PsiMethod
)info
.getElement(), info
.getSubstitutor()));
464 public static PsiField
findFieldByName(GrTypeDefinition grType
, String name
, boolean checkBases
) {
466 for (GrField field
: grType
.getFields()) {
467 if (name
.equals(field
.getName())) return field
;
473 Map
<String
, CandidateInfo
> fieldsMap
= CollectClassMembersUtil
.getAllFields(grType
);
474 final CandidateInfo info
= fieldsMap
.get(name
);
475 return info
== null ?
null : (PsiField
)info
.getElement();
478 public static PsiField
[] getAllFields(GrTypeDefinition grType
) {
479 Map
<String
, CandidateInfo
> fieldsMap
= CollectClassMembersUtil
.getAllFields(grType
);
480 return ContainerUtil
.map2Array(fieldsMap
.values(), PsiField
.class, new Function
<CandidateInfo
, PsiField
>() {
481 public PsiField
fun(CandidateInfo entry
) {
482 return (PsiField
)entry
.getElement();
487 public static boolean isClassEquivalentTo(GrTypeDefinitionImpl definition
, PsiElement another
) {
488 return PsiClassImplUtil
.isClassEquivalentTo(definition
, another
);