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 final PsiClass
[] supers
= clazz
.getSupers();
161 if (supers
.length
< 2) {
162 addGroovyObjectMethods(clazz
, allMethods
);
164 for (PsiClass aSuper
: supers
) {
165 getAllMethodsInner(aSuper
, allMethods
, visited
);
169 public static void addGroovyObjectMethods(PsiClass clazz
, List
<PsiMethod
> allMethods
) {
170 if (clazz
instanceof GrTypeDefinition
&& !clazz
.isInterface() /*&& clazz.getExtendsListTypes().length == 0*/) {
171 final PsiClass groovyObject
=
172 JavaPsiFacade
.getInstance(clazz
.getProject()).findClass(GrTypeDefinition
.DEFAULT_BASE_CLASS_NAME
, clazz
.getResolveScope());
173 if (groovyObject
!= null) {
174 for (final PsiMethod method
: groovyObject
.getMethods()) {
175 allMethods
.add(new GrSyntheticMethodImplementation(method
, clazz
));
182 private static List
<PsiClassType
> getReferenceListTypes(@Nullable GrReferenceList list
) {
183 final ArrayList
<PsiClassType
> types
= new ArrayList
<PsiClassType
>();
185 for (GrCodeReferenceElement ref
: list
.getReferenceElements()) {
186 types
.add(new GrClassReferenceType(ref
));
193 public static PsiClass
[] getInterfaces(GrTypeDefinition grType
) {
194 final PsiClassType
[] implementsListTypes
= grType
.getImplementsListTypes();
195 List
<PsiClass
> result
= new ArrayList
<PsiClass
>(implementsListTypes
.length
);
196 for (PsiClassType type
: implementsListTypes
) {
197 final PsiClass psiClass
= type
.resolve();
198 if (psiClass
!= null) result
.add(psiClass
);
200 return result
.toArray(new PsiClass
[result
.size()]);
204 public static PsiClass
[] getSupers(GrTypeDefinition grType
) {
205 PsiClassType
[] superTypes
= grType
.getSuperTypes();
206 List
<PsiClass
> result
= new ArrayList
<PsiClass
>();
207 for (PsiClassType superType
: superTypes
) {
208 PsiClass superClass
= superType
.resolve();
209 if (superClass
!= null) {
210 result
.add(superClass
);
214 return result
.toArray(new PsiClass
[result
.size()]);
217 public static boolean processDeclarations(@NotNull GrTypeDefinition grType
,
218 @NotNull PsiScopeProcessor processor
,
219 @NotNull ResolveState state
,
220 PsiElement lastParent
,
221 @NotNull PsiElement place
) {
222 for (final PsiTypeParameter typeParameter
: grType
.getTypeParameters()) {
223 if (!ResolveUtil
.processElement(processor
, typeParameter
)) return false;
226 NameHint nameHint
= processor
.getHint(NameHint
.KEY
);
227 //todo [DIANA] look more carefully
228 String name
= nameHint
== null ?
null : nameHint
.getName(state
);
229 ClassHint classHint
= processor
.getHint(ClassHint
.KEY
);
230 if (classHint
== null || classHint
.shouldProcess(ClassHint
.ResolveKind
.PROPERTY
)) {
231 Map
<String
, CandidateInfo
> fieldsMap
= CollectClassMembersUtil
.getAllFields(grType
);
233 CandidateInfo fieldInfo
= fieldsMap
.get(name
);
234 if (fieldInfo
!= null) {
235 final PsiElement element
= fieldInfo
.getElement();
236 if (!isSameDeclaration(place
, element
)) { //the same variable declaration
237 if (!processor
.execute(element
, ResolveState
.initial().put(PsiSubstitutor
.KEY
, state
.get(PsiSubstitutor
.KEY
)))) return false;
242 for (CandidateInfo info
: fieldsMap
.values()) {
243 final PsiElement element
= info
.getElement();
244 if (!isSameDeclaration(place
, element
)) { //the same variable declaration
245 if (!processor
.execute(element
, ResolveState
.initial().put(PsiSubstitutor
.KEY
, state
.get(PsiSubstitutor
.KEY
)))) return false;
251 if (classHint
== null || classHint
.shouldProcess(ClassHint
.ResolveKind
.METHOD
)) {
252 Map
<String
, List
<CandidateInfo
>> methodsMap
= CollectClassMembersUtil
.getAllMethods(grType
, true);
253 boolean isPlaceGroovy
= place
.getLanguage() == GroovyFileType
.GROOVY_FILE_TYPE
.getLanguage();
255 for (List
<CandidateInfo
> list
: methodsMap
.values()) {
256 for (CandidateInfo info
: list
) {
257 PsiMethod method
= (PsiMethod
)info
.getElement();
258 if (!isSameDeclaration(place
, method
) &&
259 isMethodVisible(isPlaceGroovy
, method
) &&
260 !processor
.execute(method
, ResolveState
.initial().put(PsiSubstitutor
.KEY
, state
.get(PsiSubstitutor
.KEY
)))) {
267 List
<CandidateInfo
> byName
= methodsMap
.get(name
);
268 if (byName
!= null) {
269 for (CandidateInfo info
: byName
) {
270 PsiMethod method
= (PsiMethod
)info
.getElement();
271 if (!isSameDeclaration(place
, method
) &&
272 isMethodVisible(isPlaceGroovy
, method
) &&
273 !processor
.execute(method
, ResolveState
.initial().put(PsiSubstitutor
.KEY
, state
.get(PsiSubstitutor
.KEY
)))) {
281 final GrTypeDefinitionBody body
= grType
.getBody();
282 if (body
!= null && !isSuperClassReferenceResolving(grType
, lastParent
)) {
283 if (classHint
== null || classHint
.shouldProcess(ClassHint
.ResolveKind
.CLASS
)) {
284 for (CandidateInfo info
: CollectClassMembersUtil
.getAllInnerClasses(grType
, false).values()) {
285 final PsiClass innerClass
= (PsiClass
)info
.getElement();
286 assert innerClass
!= null;
287 final String innerClassName
= innerClass
.getName();
288 if (nameHint
!= null && !innerClassName
.equals(nameHint
.getName(state
))) {
292 if (!processor
.execute(innerClass
, state
)) {
303 private static boolean isSuperClassReferenceResolving(GrTypeDefinition grType
, PsiElement lastParent
) {
304 return lastParent
instanceof GrReferenceList
||
305 grType
.isAnonymous() && lastParent
== ((GrAnonymousClassDefinition
)grType
).getBaseClassReferenceGroovy();
309 private static boolean isSameDeclaration(PsiElement place
, PsiElement element
) {
310 if (element
instanceof GrAccessorMethod
) element
= ((GrAccessorMethod
)element
).getProperty();
312 if (!(element
instanceof GrField
)) return false;
313 while (place
!= null) {
314 place
= place
.getParent();
315 if (place
== element
) return true;
316 if (place
instanceof GrClosableBlock
) return false;
321 private static boolean isMethodVisible(boolean isPlaceGroovy
, PsiMethod method
) {
322 return isPlaceGroovy
|| !(method
instanceof GrGdkMethod
);
325 private static boolean isPropertyReference(PsiElement place
, PsiField aField
, boolean isGetter
) {
326 //filter only in groovy, todo: analyze java place
327 if (place
.getLanguage() != GroovyFileType
.GROOVY_FILE_TYPE
.getLanguage()) return true;
329 if (place
instanceof GrReferenceExpression
) {
330 final PsiElement parent
= place
.getParent();
331 if (parent
instanceof GrMethodCallExpression
) {
332 final GrMethodCallExpression call
= (GrMethodCallExpression
)parent
;
333 if (call
.getNamedArguments().length
> 0 || call
.getClosureArguments().length
> 0) return false;
334 final GrExpression
[] args
= call
.getExpressionArguments();
336 return args
.length
== 0;
339 return args
.length
== 1 &&
341 .isAssignableByMethodCallConversion(aField
.getType(), args
[0].getType(), place
.getManager(), place
.getResolveScope());
350 public static PsiMethod
findMethodBySignature(GrTypeDefinition grType
, PsiMethod patternMethod
, boolean checkBases
) {
351 final MethodSignature patternSignature
= patternMethod
.getSignature(PsiSubstitutor
.EMPTY
);
352 for (PsiMethod method
: findMethodsByName(grType
, patternMethod
.getName(), checkBases
, false)) {
353 final PsiClass clazz
= method
.getContainingClass();
354 if (clazz
== null) continue;
355 PsiSubstitutor superSubstitutor
= TypeConversionUtil
.getClassSubstitutor(clazz
, grType
, PsiSubstitutor
.EMPTY
);
356 if (superSubstitutor
== null) continue;
357 final MethodSignature signature
= method
.getSignature(superSubstitutor
);
358 if (signature
.equals(patternSignature
)) return method
;
364 private static PsiMethod
[] findMethodsByName(GrTypeDefinition grType
,
367 boolean includeSyntheticAccessors
) {
369 List
<PsiMethod
> result
= new ArrayList
<PsiMethod
>();
370 for (PsiMethod method
: includeSyntheticAccessors ? grType
.getMethods() : grType
.getGroovyMethods()) {
371 if (name
.equals(method
.getName())) result
.add(method
);
374 return result
.toArray(new PsiMethod
[result
.size()]);
377 Map
<String
, List
<CandidateInfo
>> methodsMap
= CollectClassMembersUtil
.getAllMethods(grType
, includeSyntheticAccessors
);
378 return PsiImplUtil
.mapToMethods(methodsMap
.get(name
));
382 public static PsiMethod
[] findMethodsBySignature(GrTypeDefinition grType
, PsiMethod patternMethod
, boolean checkBases
) {
383 return findMethodsBySignature(grType
, patternMethod
, checkBases
, true);
387 public static PsiMethod
[] findCodeMethodsBySignature(GrTypeDefinition grType
, PsiMethod patternMethod
, boolean checkBases
) {
388 return findMethodsBySignature(grType
, patternMethod
, checkBases
, false);
392 public static PsiMethod
[] findMethodsByName(GrTypeDefinition grType
, @NonNls String name
, boolean checkBases
) {
393 return findMethodsByName(grType
, name
, checkBases
, true);
396 private static PsiMethod
[] findMethodsBySignature(GrTypeDefinition grType
,
397 PsiMethod patternMethod
,
399 boolean includeSynthetic
) {
400 ArrayList
<PsiMethod
> result
= new ArrayList
<PsiMethod
>();
401 final MethodSignature patternSignature
= patternMethod
.getSignature(PsiSubstitutor
.EMPTY
);
402 for (PsiMethod method
: findMethodsByName(grType
, patternMethod
.getName(), checkBases
, includeSynthetic
)) {
403 final PsiClass clazz
= method
.getContainingClass();
404 if (clazz
== null) continue;
405 PsiSubstitutor superSubstitutor
= TypeConversionUtil
.getClassSubstitutor(clazz
, grType
, PsiSubstitutor
.EMPTY
);
406 if (superSubstitutor
== null) continue;
408 final MethodSignature signature
= method
.getSignature(superSubstitutor
);
409 if (signature
.equals(patternSignature
)) //noinspection unchecked
414 return result
.toArray(new PsiMethod
[result
.size()]);
418 public static PsiMethod
[] findCodeMethodsByName(GrTypeDefinition grType
, @NonNls String name
, boolean checkBases
) {
419 return findMethodsByName(grType
, name
, checkBases
, false);
423 public static List
<Pair
<PsiMethod
, PsiSubstitutor
>> findMethodsAndTheirSubstitutorsByName(GrTypeDefinition grType
,
425 boolean checkBases
) {
426 final ArrayList
<Pair
<PsiMethod
, PsiSubstitutor
>> result
= new ArrayList
<Pair
<PsiMethod
, PsiSubstitutor
>>();
429 final PsiMethod
[] methods
= grType
.findMethodsByName( name
, false);
430 for (PsiMethod method
: methods
) {
431 result
.add(new Pair
<PsiMethod
, PsiSubstitutor
>(method
, PsiSubstitutor
.EMPTY
));
435 final Map
<String
, List
<CandidateInfo
>> map
= CollectClassMembersUtil
.getAllMethods(grType
, true);
436 final List
<CandidateInfo
> candidateInfos
= map
.get(name
);
437 if (candidateInfos
!= null) {
438 for (CandidateInfo info
: candidateInfos
) {
439 final PsiElement element
= info
.getElement();
440 result
.add(new Pair
<PsiMethod
, PsiSubstitutor
>((PsiMethod
)element
, info
.getSubstitutor()));
449 public static List
<Pair
<PsiMethod
, PsiSubstitutor
>> getAllMethodsAndTheirSubstitutors(GrTypeDefinition grType
) {
450 final Map
<String
, List
<CandidateInfo
>> allMethodsMap
= CollectClassMembersUtil
.getAllMethods(grType
, true);
451 List
<Pair
<PsiMethod
, PsiSubstitutor
>> result
= new ArrayList
<Pair
<PsiMethod
, PsiSubstitutor
>>();
452 for (List
<CandidateInfo
> infos
: allMethodsMap
.values()) {
453 for (CandidateInfo info
: infos
) {
454 result
.add(new Pair
<PsiMethod
, PsiSubstitutor
>((PsiMethod
)info
.getElement(), info
.getSubstitutor()));
462 public static PsiField
findFieldByName(GrTypeDefinition grType
, String name
, boolean checkBases
) {
464 for (GrField field
: grType
.getFields()) {
465 if (name
.equals(field
.getName())) return field
;
471 Map
<String
, CandidateInfo
> fieldsMap
= CollectClassMembersUtil
.getAllFields(grType
);
472 final CandidateInfo info
= fieldsMap
.get(name
);
473 return info
== null ?
null : (PsiField
)info
.getElement();
476 public static PsiField
[] getAllFields(GrTypeDefinition grType
) {
477 Map
<String
, CandidateInfo
> fieldsMap
= CollectClassMembersUtil
.getAllFields(grType
);
478 return ContainerUtil
.map2Array(fieldsMap
.values(), PsiField
.class, new Function
<CandidateInfo
, PsiField
>() {
479 public PsiField
fun(CandidateInfo entry
) {
480 return (PsiField
)entry
.getElement();
485 public static boolean isClassEquivalentTo(GrTypeDefinitionImpl definition
, PsiElement another
) {
486 return PsiClassImplUtil
.isClassEquivalentTo(definition
, another
);