2cb7806cd3a2f6969f6de1a4d9858f0233553dfd
[idea/community.git] / plugins / groovy / groovy-psi / src / org / jetbrains / plugins / groovy / lang / psi / util / GrClassImplUtil.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package org.jetbrains.plugins.groovy.lang.psi.util;
18
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.util.Computable;
21 import com.intellij.openapi.util.Condition;
22 import com.intellij.openapi.util.Pair;
23 import com.intellij.openapi.util.RecursionManager;
24 import com.intellij.pom.java.LanguageLevel;
25 import com.intellij.psi.*;
26 import com.intellij.psi.impl.PsiClassImplUtil;
27 import com.intellij.psi.infos.CandidateInfo;
28 import com.intellij.psi.scope.ElementClassHint;
29 import com.intellij.psi.scope.NameHint;
30 import com.intellij.psi.scope.PsiScopeProcessor;
31 import com.intellij.psi.util.*;
32 import com.intellij.psi.util.PsiUtil;
33 import com.intellij.util.ArrayUtil;
34 import com.intellij.util.Function;
35 import com.intellij.util.containers.ContainerUtil;
36 import com.intellij.util.containers.MostlySingularMultiMap;
37 import com.intellij.util.containers.hash.HashSet;
38 import gnu.trove.TObjectIntHashMap;
39 import org.jetbrains.annotations.NonNls;
40 import org.jetbrains.annotations.NotNull;
41 import org.jetbrains.annotations.Nullable;
42 import org.jetbrains.plugins.groovy.GroovyLanguage;
43 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
44 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
45 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
46 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
47 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrEnumConstantInitializer;
48 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrReferenceList;
49 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
50 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinitionBody;
51 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod;
52 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
53 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
54 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrReflectedMethod;
55 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
56 import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager;
57 import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
58 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
59 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.typedef.GrTypeDefinitionImpl;
60 import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrScriptField;
61 import org.jetbrains.plugins.groovy.lang.resolve.CollectClassMembersUtil;
62 import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
63
64 import java.util.*;
65
66 /**
67  * @author Maxim.Medvedev
68  */
69 public class GrClassImplUtil {
70   private static final Logger LOG = Logger.getInstance(GrClassImplUtil.class);
71
72   private static final Condition<PsiClassType> IS_GROOVY_OBJECT = new Condition<PsiClassType>() {
73     @Override
74     public boolean value(PsiClassType psiClassType) {
75       return TypesUtil.isClassType(psiClassType, GroovyCommonClassNames.DEFAULT_BASE_CLASS_NAME);
76     }
77   };
78
79   private GrClassImplUtil() {
80   }
81
82   private static final Condition<PsiMethod> CONSTRUCTOR_CONDITION = PsiMethod::isConstructor;
83
84   @NotNull
85   public static GrTypeDefinition[] getBodyCodeInnerClasses(@NotNull GrTypeDefinition definition) {
86     GrTypeDefinitionBody body = definition.getBody();
87     return body != null ? body.getInnerClasses() : GrTypeDefinition.EMPTY_ARRAY;
88   }
89
90   @NotNull
91   public static GrMethod[] getCodeConstructors(@NotNull GrTypeDefinition definition) {
92     GrMethod[] methods = definition.getCodeMethods();
93     List<GrMethod> result = ContainerUtil.filter(methods, CONSTRUCTOR_CONDITION);
94     return result.toArray(GrMethod.EMPTY_ARRAY);
95   }
96
97   @NotNull
98   public static GrMethod[] getBodyCodeMethods(@NotNull GrTypeDefinition definition) {
99     GrTypeDefinitionBody body = definition.getBody();
100     return body != null ? body.getMethods() : GrMethod.EMPTY_ARRAY;
101   }
102
103   @NotNull
104   public static GrField[] getBodyCodeFields(@NotNull GrTypeDefinition definition) {
105     GrTypeDefinitionBody body = definition.getBody();
106     return body != null ? body.getFields() : GrField.EMPTY_ARRAY;
107   }
108
109   @NotNull
110   public static PsiMethod[] getConstructors(@NotNull GrTypeDefinition definition) {
111     PsiMethod[] methods = definition.getMethods();
112     List<PsiMethod> result = ContainerUtil.filter(methods, CONSTRUCTOR_CONDITION);
113     return result.toArray(PsiMethod.EMPTY_ARRAY);
114   }
115
116   @Nullable
117   public static PsiClass findInnerClassByName(GrTypeDefinition grType, String name, boolean checkBases) {
118     if (!checkBases) {
119       for (PsiClass inner : grType.getInnerClasses()) {
120         if (name.equals(inner.getName())) return inner;
121       }
122       return null;
123     }
124     else {
125       Map<String, CandidateInfo> innerClasses = CollectClassMembersUtil.getAllInnerClasses(grType, true);
126       final CandidateInfo info = innerClasses.get(name);
127       return info == null ? null : (PsiClass)info.getElement();
128     }
129   }
130
131   @Nullable
132   public static PsiClass getSuperClass(@NotNull GrTypeDefinition grType) {
133     return getSuperClass(grType, grType.getExtendsListTypes());
134   }
135
136   @Nullable
137   public static PsiClass getSuperClass(@NotNull GrTypeDefinition grType, @NotNull PsiClassType[] extendsListTypes) {
138     if (extendsListTypes.length == 0) return getBaseClass(grType);
139     final PsiClass superClass = extendsListTypes[0].resolve();
140     return superClass != null ? superClass : getBaseClass(grType);
141   }
142
143   @Nullable
144   public static PsiClass getBaseClass(GrTypeDefinition grType) {
145     if (grType.isEnum()) {
146       return JavaPsiFacade.getInstance(grType.getProject()).findClass(CommonClassNames.JAVA_LANG_ENUM, grType.getResolveScope());
147     }
148     else {
149       return JavaPsiFacade.getInstance(grType.getProject()).findClass(CommonClassNames.JAVA_LANG_OBJECT, grType.getResolveScope());
150     }
151   }
152
153   @NotNull
154   public static PsiClassType[] getExtendsListTypes(@NotNull GrTypeDefinition grType, @NotNull PsiClassType[] extendsTypes) {
155     if (grType.isInterface()) {
156       return extendsTypes;
157     }
158
159     for (PsiClassType type : extendsTypes) {
160       final PsiClass superClass = type.resolve();
161       if (superClass instanceof GrTypeDefinition && !superClass.isInterface() ||
162           superClass != null && GroovyCommonClassNames.GROOVY_OBJECT_SUPPORT.equals(superClass.getQualifiedName())) {
163         return extendsTypes;
164       }
165     }
166
167     PsiClass grObSupport = GroovyPsiManager.getInstance(grType.getProject())
168       .findClassWithCache(GroovyCommonClassNames.GROOVY_OBJECT_SUPPORT, grType.getResolveScope());
169     if (grObSupport != null) {
170       final PsiClassType type = JavaPsiFacade.getInstance(grType.getProject()).getElementFactory().createType(grObSupport);
171       return ArrayUtil.append(extendsTypes, type, PsiClassType.ARRAY_FACTORY);
172     }
173     return extendsTypes;
174   }
175
176   @NotNull
177   public static PsiClassType[] getImplementsListTypes(@NotNull GrTypeDefinition grType, @NotNull PsiClassType[] implementsTypes) {
178     final Collection<PsiClassType> result = ContainerUtil.newLinkedHashSet();
179     final PsiClassType[] extendsTypes = getReferenceListTypes(grType.getExtendsClause());
180     result.addAll(Arrays.asList(implementsTypes));
181     if (!grType.isInterface() && !ContainerUtil.or(implementsTypes, IS_GROOVY_OBJECT) && !ContainerUtil.or(extendsTypes, IS_GROOVY_OBJECT)) {
182       result.add(getGroovyObjectType(grType));
183     }
184     return result.toArray(new PsiClassType[result.size()]);
185   }
186
187   public static PsiClassType getGroovyObjectType(@NotNull PsiElement context) {
188     return TypesUtil.createTypeByFQClassName(GroovyCommonClassNames.DEFAULT_BASE_CLASS_NAME, context);
189   }
190
191   @NotNull
192   public static PsiClassType[] getSuperTypes(GrTypeDefinition grType, boolean includeSynthetic) {
193     PsiClassType[] extendsList = grType.getExtendsListTypes(includeSynthetic);
194     if (extendsList.length == 0) {
195       extendsList = new PsiClassType[]{createBaseClassType(grType)};
196     }
197
198     return ArrayUtil.mergeArrays(extendsList, grType.getImplementsListTypes(includeSynthetic), PsiClassType.ARRAY_FACTORY);
199   }
200
201   public static PsiClassType createBaseClassType(GrTypeDefinition grType) {
202     if (grType.isEnum()) {
203       return TypesUtil.createTypeByFQClassName(CommonClassNames.JAVA_LANG_ENUM, grType);
204     }
205     return TypesUtil.getJavaLangObject(grType);
206   }
207
208   @NotNull
209   public static PsiMethod[] getAllMethods(final GrTypeDefinition grType) {
210     return CachedValuesManager.getCachedValue(grType, new CachedValueProvider<PsiMethod[]>() {
211       @Nullable
212       @Override
213       public Result<PsiMethod[]> compute() {
214         List<PsiMethod> list = ContainerUtil.newArrayList();
215         getAllMethodsInner(grType, list, new HashSet<PsiClass>());
216         return Result.create(list.toArray(new PsiMethod[list.size()]), PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, grType);
217       }
218     });
219   }
220
221   @NotNull
222   public static List<PsiMethod> getAllMethods(Collection<? extends PsiClass> classes) {
223     List<PsiMethod> allMethods = new ArrayList<PsiMethod>();
224     HashSet<PsiClass> visited = new HashSet<PsiClass>();
225
226     for (PsiClass psiClass : classes) {
227       getAllMethodsInner(psiClass, allMethods, visited);
228     }
229
230     return allMethods;
231   }
232
233   private static void getAllMethodsInner(PsiClass clazz, List<PsiMethod> allMethods, HashSet<PsiClass> visited) {
234     if (visited.contains(clazz)) return;
235     visited.add(clazz);
236
237     ContainerUtil.addAll(allMethods, clazz.getMethods());
238
239     final PsiClass[] supers = clazz.getSupers();
240     for (PsiClass aSuper : supers) {
241       getAllMethodsInner(aSuper, allMethods, visited);
242     }
243   }
244
245   public static PsiClassType[] getReferenceListTypes(@Nullable GrReferenceList list) {
246     if (list == null) return PsiClassType.EMPTY_ARRAY;
247     return list.getReferencedTypes();
248   }
249
250
251   public static PsiClass[] getInterfaces(GrTypeDefinition grType) {
252     final PsiClassType[] implementsListTypes = grType.getImplementsListTypes();
253     List<PsiClass> result = new ArrayList<PsiClass>(implementsListTypes.length);
254     for (PsiClassType type : implementsListTypes) {
255       final PsiClass psiClass = type.resolve();
256       if (psiClass != null) result.add(psiClass);
257     }
258     return result.toArray(new PsiClass[result.size()]);
259   }
260
261   @NotNull
262   public static PsiClass[] getSupers(GrTypeDefinition grType, boolean includeSynthetic) {
263     PsiClassType[] superTypes = grType.getSuperTypes(includeSynthetic);
264     List<PsiClass> result = new ArrayList<PsiClass>();
265     for (PsiClassType superType : superTypes) {
266       PsiClass superClass = superType.resolve();
267       if (superClass != null) {
268         result.add(superClass);
269       }
270     }
271
272     return result.toArray(new PsiClass[result.size()]);
273   }
274
275   public static boolean processDeclarations(@NotNull GrTypeDefinition grType,
276                                             @NotNull PsiScopeProcessor processor,
277                                             @NotNull ResolveState state,
278                                             @Nullable PsiElement lastParent,
279                                             @NotNull PsiElement place) {
280     if (place instanceof GrCodeReferenceElement && lastParent instanceof GrModifierList) {
281       final PsiElement possibleAnnotation = PsiTreeUtil.skipParentsOfType(place, GrCodeReferenceElement.class);
282       if (possibleAnnotation instanceof GrAnnotation && possibleAnnotation.getParent() == lastParent) {
283         return true; //don't process class members while resolving annotation which annotates current class
284       }
285     }
286
287     for (final PsiTypeParameter typeParameter : grType.getTypeParameters()) {
288       if (!ResolveUtil.processElement(processor, typeParameter, state)) return false;
289     }
290
291     NameHint nameHint = processor.getHint(NameHint.KEY);
292     String name = nameHint == null ? null : nameHint.getName(state);
293     ElementClassHint classHint = processor.getHint(ElementClassHint.KEY);
294     final PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
295     final PsiElementFactory factory = JavaPsiFacade.getElementFactory(place.getProject());
296
297     boolean processInstanceMethods = (ResolveUtil.shouldProcessMethods(classHint) || ResolveUtil.shouldProcessProperties(classHint)) &&
298                                      shouldProcessInstanceMembers(grType, lastParent);
299
300     LanguageLevel level = PsiUtil.getLanguageLevel(place);
301     if (ResolveUtil.shouldProcessProperties(classHint)) {
302       Map<String, CandidateInfo> fieldsMap = CollectClassMembersUtil.getAllFields(grType);
303       if (name != null) {
304         CandidateInfo fieldInfo = fieldsMap.get(name);
305         if (fieldInfo != null) {
306           if (!processField(grType, processor, state, place, processInstanceMethods, substitutor, factory, level, fieldInfo)) {
307             return false;
308           }
309         }
310         else if (grType.isTrait() && lastParent != null) {
311           PsiField field = findFieldByName(grType, name, false, true);
312           if (field != null && field.hasModifierProperty(PsiModifier.PUBLIC)) {
313             if (!processField(grType, processor, state, place, processInstanceMethods, substitutor, factory, level,
314                               new CandidateInfo(field, PsiSubstitutor.EMPTY))) {
315               return false;
316             }
317           }
318         }
319       }
320       else {
321         for (CandidateInfo info : fieldsMap.values()) {
322           if (!processField(grType, processor, state, place, processInstanceMethods, substitutor, factory, level, info)) {
323             return false;
324           }
325         }
326         if (grType.isTrait() && lastParent != null) {
327           for (PsiField field : CollectClassMembersUtil.getFields(grType, true)) {
328             if (field.hasModifierProperty(PsiModifier.PUBLIC)) {
329               if (!processField(grType, processor, state, place, processInstanceMethods, substitutor, factory, level,
330                                 new CandidateInfo(field, PsiSubstitutor.EMPTY))) {
331                 return false;
332               }
333             }
334           }
335         }
336       }
337     }
338
339     if (ResolveUtil.shouldProcessMethods(classHint)) {
340       Map<String, List<CandidateInfo>> methodsMap = CollectClassMembersUtil.getAllMethods(grType, true);
341       boolean isPlaceGroovy = place.getLanguage() == GroovyLanguage.INSTANCE;
342       if (name == null) {
343         for (List<CandidateInfo> list : methodsMap.values()) {
344           for (CandidateInfo info : list) {
345             if (!processMethod(grType, processor, state, place, processInstanceMethods, substitutor, factory, level, isPlaceGroovy, info)) {
346               return false;
347             }
348           }
349         }
350       }
351       else {
352         List<CandidateInfo> byName = methodsMap.get(name);
353         if (byName != null) {
354           for (CandidateInfo info : byName) {
355             if (!processMethod(grType, processor, state, place, processInstanceMethods, substitutor, factory, level, isPlaceGroovy, info)) {
356               return false;
357             }
358           }
359         }
360       }
361     }
362
363     final GrTypeDefinitionBody body = grType.getBody();
364     if (body != null) {
365       if (ResolveUtil.shouldProcessClasses(classHint)) {
366         for (PsiClass innerClass : getInnerClassesForResolve(grType, lastParent, place)) {
367           final String innerClassName = innerClass.getName();
368           if (nameHint != null && !innerClassName.equals(nameHint.getName(state))) {
369             continue;
370           }
371
372           if (!processor.execute(innerClass, state)) {
373             return false;
374           }
375         }
376       }
377     }
378
379
380     return true;
381   }
382
383   private static boolean processField(@NotNull GrTypeDefinition grType,
384                                       @NotNull PsiScopeProcessor processor,
385                                       @NotNull ResolveState state,
386                                       @NotNull PsiElement place,
387                                       boolean processInstanceMethods,
388                                       @NotNull PsiSubstitutor substitutor,
389                                       @NotNull PsiElementFactory factory,
390                                       @NotNull LanguageLevel level, CandidateInfo fieldInfo) {
391     final PsiField field = (PsiField)fieldInfo.getElement();
392     if (!processInstanceMember(processInstanceMethods, field) || isSameDeclaration(place, field)) {
393       return true;
394     }
395     LOG.assertTrue(field.getContainingClass() != null);
396     final PsiSubstitutor finalSubstitutor =
397       PsiClassImplUtil.obtainFinalSubstitutor(field.getContainingClass(), fieldInfo.getSubstitutor(), grType, substitutor, factory, level);
398
399     return processor.execute(field, state.put(PsiSubstitutor.KEY, finalSubstitutor));
400   }
401
402   private static boolean processMethod(@NotNull GrTypeDefinition grType,
403                                        @NotNull PsiScopeProcessor processor,
404                                        @NotNull ResolveState state,
405                                        @NotNull PsiElement place,
406                                        boolean processInstanceMethods,
407                                        @NotNull PsiSubstitutor substitutor,
408                                        @NotNull PsiElementFactory factory,
409                                        @NotNull LanguageLevel level,
410                                        boolean placeGroovy,
411                                        @NotNull CandidateInfo info) {
412     PsiMethod method = (PsiMethod)info.getElement();
413     if (!processInstanceMember(processInstanceMethods, method) ||
414         isSameDeclaration(place, method) ||
415         !isMethodVisible(placeGroovy, method)) {
416       return true;
417     }
418     LOG.assertTrue(method.getContainingClass() != null);
419     final PsiSubstitutor finalSubstitutor =
420       PsiClassImplUtil.obtainFinalSubstitutor(method.getContainingClass(), info.getSubstitutor(), grType, substitutor, factory, level);
421
422     return processor.execute(method, state.put(PsiSubstitutor.KEY, finalSubstitutor));
423   }
424
425   private static boolean shouldProcessInstanceMembers(@NotNull GrTypeDefinition grType, @Nullable PsiElement lastParent) {
426     if (lastParent != null) {
427       final GrModifierList modifierList = grType.getModifierList();
428       if (modifierList != null && modifierList.findAnnotation(GroovyCommonClassNames.GROOVY_LANG_CATEGORY) != null) {
429         return false;
430       }
431     }
432     return true;
433   }
434
435   private static boolean processInstanceMember(boolean shouldProcessInstance, @NotNull PsiMember member) {
436     if (shouldProcessInstance) return true;
437
438     if (member instanceof GrReflectedMethod) {
439       return ((GrReflectedMethod)member).getBaseMethod().hasModifierProperty(PsiModifier.STATIC);
440     }
441     else {
442       return member.hasModifierProperty(PsiModifier.STATIC);
443     }
444   }
445
446   @NotNull
447   private static List<PsiClass> getInnerClassesForResolve(@NotNull final GrTypeDefinition grType,
448                                                           @Nullable final PsiElement lastParent,
449                                                           @NotNull final PsiElement place) {
450     if (lastParent instanceof GrReferenceList || PsiTreeUtil.getParentOfType(place, GrReferenceList.class) != null) {
451       return Arrays.asList(grType.getCodeInnerClasses());
452     }
453
454     List<PsiClass> classes = RecursionManager.doPreventingRecursion(grType, true, new Computable<List<PsiClass>>() {
455       @Override
456       public List<PsiClass> compute() {
457         List<PsiClass> result = new ArrayList<PsiClass>();
458         for (CandidateInfo info : CollectClassMembersUtil.getAllInnerClasses(grType, false).values()) {
459           final PsiClass inner = (PsiClass)info.getElement();
460           final PsiClass containingClass = inner.getContainingClass();
461           assert containingClass != null;
462
463           if (lastParent == null || !containingClass.isInterface() || PsiTreeUtil.isAncestor(containingClass, place, false)) {
464             ContainerUtil.addIfNotNull(result, inner);
465           }
466         }
467         return result;
468       }
469     });
470
471     if (classes == null) {
472       return Arrays.asList(grType.getCodeInnerClasses());
473     }
474
475     return classes;
476   }
477
478   public static boolean isSameDeclaration(PsiElement place, PsiElement element) {
479     if (element instanceof GrAccessorMethod) element = ((GrAccessorMethod)element).getProperty();
480
481     if (!(element instanceof GrField)) return false;
482     if (element instanceof GrScriptField) element = ((GrScriptField)element).getOriginalVariable();
483
484     while (place != null) {
485       if (place == element) return true;
486       place = place.getParent();
487       if (place instanceof GrClosableBlock) return false;
488       if (place instanceof GrEnumConstantInitializer) return false;
489     }
490     return false;
491   }
492
493   private static boolean isMethodVisible(boolean isPlaceGroovy, PsiMethod method) {
494     return isPlaceGroovy || !(method instanceof GrGdkMethod);
495   }
496
497   @Nullable
498   public static PsiMethod findMethodBySignature(GrTypeDefinition grType, PsiMethod patternMethod, boolean checkBases) {
499     final MethodSignature patternSignature = patternMethod.getSignature(PsiSubstitutor.EMPTY);
500     for (PsiMethod method : findMethodsByName(grType, patternMethod.getName(), checkBases, false)) {
501       MethodSignature signature = getSignatureForInheritor(method, grType);
502       if (patternSignature.equals(signature)) return method;
503     }
504
505     return null;
506   }
507
508   private static PsiMethod[] findMethodsByName(GrTypeDefinition grType,
509                                                String name,
510                                                boolean checkBases,
511                                                boolean includeSyntheticAccessors) {
512     if (!checkBases) {
513       List<PsiMethod> result = new ArrayList<PsiMethod>();
514       for (PsiMethod method : CollectClassMembersUtil.getMethods(grType, includeSyntheticAccessors)) {
515         if (name.equals(method.getName())) result.add(method);
516       }
517
518       return result.toArray(new PsiMethod[result.size()]);
519     }
520
521     Map<String, List<CandidateInfo>> methodsMap = CollectClassMembersUtil.getAllMethods(grType, includeSyntheticAccessors);
522     return PsiImplUtil.mapToMethods(methodsMap.get(name));
523   }
524
525   @NotNull
526   public static PsiMethod[] findMethodsBySignature(GrTypeDefinition grType, PsiMethod patternMethod, boolean checkBases) {
527     return findMethodsBySignature(grType, patternMethod, checkBases, true);
528   }
529
530   @NotNull
531   public static PsiMethod[] findCodeMethodsBySignature(GrTypeDefinition grType, PsiMethod patternMethod, boolean checkBases) {
532     return findMethodsBySignature(grType, patternMethod, checkBases, false);
533   }
534
535   @NotNull
536   public static PsiMethod[] findMethodsByName(GrTypeDefinition grType, @NonNls String name, boolean checkBases) {
537     return findMethodsByName(grType, name, checkBases, true);
538   }
539
540   private static PsiMethod[] findMethodsBySignature(GrTypeDefinition grType,
541                                                     PsiMethod patternMethod,
542                                                     boolean checkBases,
543                                                     boolean includeSynthetic) {
544     ArrayList<PsiMethod> result = new ArrayList<PsiMethod>();
545     final MethodSignature patternSignature = patternMethod.getSignature(PsiSubstitutor.EMPTY);
546     for (PsiMethod method : findMethodsByName(grType, patternMethod.getName(), checkBases, includeSynthetic)) {
547       MethodSignature signature = getSignatureForInheritor(method, grType);
548       if (patternSignature.equals(signature)) {
549         result.add(method);
550       }
551     }
552     return result.toArray(new PsiMethod[result.size()]);
553   }
554
555   @Nullable
556   private static MethodSignature getSignatureForInheritor(@NotNull PsiMethod methodFromSuperClass, @NotNull GrTypeDefinition inheritor) {
557     final PsiClass clazz = methodFromSuperClass.getContainingClass();
558     if (clazz == null) return null;
559     PsiSubstitutor superSubstitutor = TypeConversionUtil.getClassSubstitutor(clazz, inheritor, PsiSubstitutor.EMPTY);
560     if (superSubstitutor == null) return null;
561
562     return methodFromSuperClass.getSignature(superSubstitutor);
563   }
564
565
566   @NotNull
567   public static PsiMethod[] findCodeMethodsByName(GrTypeDefinition grType, @NonNls String name, boolean checkBases) {
568     return findMethodsByName(grType, name, checkBases, false);
569   }
570
571   @NotNull
572   public static List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(GrTypeDefinition grType,
573                                                                                             String name,
574                                                                                             boolean checkBases) {
575     final ArrayList<Pair<PsiMethod, PsiSubstitutor>> result = new ArrayList<Pair<PsiMethod, PsiSubstitutor>>();
576
577     if (!checkBases) {
578       final PsiMethod[] methods = grType.findMethodsByName(name, false);
579       for (PsiMethod method : methods) {
580         result.add(Pair.create(method, PsiSubstitutor.EMPTY));
581       }
582     }
583     else {
584       final Map<String, List<CandidateInfo>> map = CollectClassMembersUtil.getAllMethods(grType, true);
585       final List<CandidateInfo> candidateInfos = map.get(name);
586       if (candidateInfos != null) {
587         for (CandidateInfo info : candidateInfos) {
588           final PsiElement element = info.getElement();
589           result.add(Pair.create((PsiMethod)element, info.getSubstitutor()));
590         }
591       }
592     }
593
594     return result;
595   }
596
597   @NotNull
598   public static List<Pair<PsiMethod, PsiSubstitutor>> getAllMethodsAndTheirSubstitutors(GrTypeDefinition grType) {
599     final Map<String, List<CandidateInfo>> allMethodsMap = CollectClassMembersUtil.getAllMethods(grType, true);
600     List<Pair<PsiMethod, PsiSubstitutor>> result = new ArrayList<Pair<PsiMethod, PsiSubstitutor>>();
601     for (List<CandidateInfo> infos : allMethodsMap.values()) {
602       for (CandidateInfo info : infos) {
603         result.add(Pair.create((PsiMethod)info.getElement(), info.getSubstitutor()));
604       }
605     }
606
607     return result;
608   }
609
610   @Nullable
611   public static PsiField findFieldByName(GrTypeDefinition grType, String name, boolean checkBases, boolean includeSynthetic) {
612     if (!checkBases) {
613       for (PsiField field : CollectClassMembersUtil.getFields(grType, includeSynthetic)) {
614         if (name.equals(field.getName())) return field;
615       }
616
617       return null;
618     }
619
620     Map<String, CandidateInfo> fieldsMap = CollectClassMembersUtil.getAllFields(grType, includeSynthetic);
621     final CandidateInfo info = fieldsMap.get(name);
622     return info == null ? null : (PsiField)info.getElement();
623   }
624
625   public static PsiField[] getAllFields(GrTypeDefinition grType) {
626     Map<String, CandidateInfo> fieldsMap = CollectClassMembersUtil.getAllFields(grType);
627     return ContainerUtil.map2Array(fieldsMap.values(), PsiField.class, new Function<CandidateInfo, PsiField>() {
628       @Override
629       public PsiField fun(CandidateInfo entry) {
630         return (PsiField)entry.getElement();
631       }
632     });
633   }
634
635   public static boolean isClassEquivalentTo(GrTypeDefinitionImpl definition, PsiElement another) {
636     return PsiClassImplUtil.isClassEquivalentTo(definition, another);
637   }
638
639   @NotNull
640   public static PsiMethod[] expandReflectedMethods(@NotNull PsiMethod method) {
641     if (method instanceof GrMethod) {
642       GrReflectedMethod[] methods = ((GrMethod)method).getReflectedMethods();
643       if (methods.length > 0) {
644         return methods;
645       }
646     }
647     return new PsiMethod[]{method};
648   }
649
650   public static void addExpandingReflectedMethods(Collection<PsiMethod> result, PsiMethod method) {
651     if (method instanceof GrMethod) {
652       final GrReflectedMethod[] reflectedMethods = ((GrMethod)method).getReflectedMethods();
653       if (reflectedMethods.length > 0) {
654         result.addAll(Arrays.asList(reflectedMethods));
655         return;
656       }
657     }
658     result.add(method);
659   }
660
661   public static void collectMethodsFromBody(@NotNull GrTypeDefinition definition, Collection<PsiMethod> result) {
662     for (GrMethod method : definition.getCodeMethods()) {
663       addExpandingReflectedMethods(result, method);
664     }
665
666     //for (GrField field : definition.getFields()) {
667     //  if (!field.isProperty()) continue;
668     //  ContainerUtil.addAll(result, field.getGetters());
669     //  ContainerUtil.addIfNotNull(result, field.getSetter());
670     //}
671   }
672
673   public static Collection<PsiMethod> filterOutAccessors(Collection<PsiMethod> result) {
674     final TObjectIntHashMap<String> map = new TObjectIntHashMap<String>();
675     for (PsiMethod method : result) {
676       if (method instanceof GrAccessorMethod || GroovyPropertyUtils.isSimplePropertyAccessor(method)) {
677         final String methodName = method.getName();
678         if (map.containsKey(methodName)) {
679           map.adjustValue(methodName, 1);
680         }
681         else {
682           map.put(methodName, 1);
683         }
684       }
685     }
686     return ContainerUtil.filter(result, new Condition<PsiMethod>() {
687       @Override
688       public boolean value(PsiMethod method) {
689         return !(method instanceof GrAccessorMethod) || map.get(method.getName()) <= 1;
690       }
691     });
692   }
693
694   @NotNull
695   public static Map<PsiMethod, MethodSignature> getDuplicatedMethods(@NotNull PsiClass clazz) {
696     return CachedValuesManager.getCachedValue(clazz, () -> {
697       PsiElementFactory factory = JavaPsiFacade.getInstance(clazz.getProject()).getElementFactory();
698
699       MostlySingularMultiMap<MethodSignature, PsiMethod> signatures = MostlySingularMultiMap.newMap();
700       for (PsiMethod method : clazz.getMethods()) {
701         MethodSignature signature = method.getSignature(factory.createRawSubstitutor(method));
702         signatures.add(signature, method);
703       }
704
705       Map<PsiMethod, MethodSignature> result = ContainerUtil.newHashMap();
706       for (MethodSignature signature : signatures.keySet()) {
707         if (signatures.valuesForKey(signature) > 1) {
708           signatures.processForKey(signature, m -> {
709             result.put(m, signature);
710             return true;
711           });
712         }
713       }
714
715       return CachedValueProvider.Result.create(result, clazz);
716     });
717   }
718
719   public static GrAccessorMethod findSetter(GrField field) {
720     return CachedValuesManager.getCachedValue(field, () -> CachedValueProvider.Result.create(
721       doGetSetter(field), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT
722     ));
723   }
724
725   @Nullable
726   private static GrAccessorMethod doGetSetter(GrField field) {
727     PsiClass containingClass = field.getContainingClass();
728     if (containingClass == null) return null;
729     PsiMethod[] setters = containingClass.findMethodsByName(GroovyPropertyUtils.getSetterName(field.getName()), false);
730     for (PsiMethod setter : setters) {
731       if (setter instanceof GrAccessorMethod) {
732         return (GrAccessorMethod)setter;
733       }
734     }
735     return null;
736   }
737
738   public static GrAccessorMethod[] findGetters(GrField field) {
739     return CachedValuesManager.getCachedValue(field, () -> CachedValueProvider.Result.create(
740       doGetGetters(field), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT
741     ));
742   }
743
744   @NotNull
745   private static GrAccessorMethod[] doGetGetters(GrField field) {
746     PsiClass containingClass = field.getContainingClass();
747     if (containingClass == null) return GrAccessorMethod.EMPTY_ARRAY;
748
749     GrAccessorMethod getter = null;
750     GrAccessorMethod booleanGetter = null;
751
752     PsiMethod[] getters = containingClass.findMethodsByName(GroovyPropertyUtils.getGetterNameNonBoolean(field.getName()), false);
753     for (PsiMethod method : getters) {
754       if (method instanceof GrAccessorMethod) {
755         getter = (GrAccessorMethod)method;
756         break;
757       }
758     }
759
760     PsiMethod[] booleanGetters = containingClass.findMethodsByName(GroovyPropertyUtils.getGetterNameBoolean(field.getName()), false);
761     for (PsiMethod method : booleanGetters) {
762       if (method instanceof GrAccessorMethod) {
763         booleanGetter = (GrAccessorMethod)method;
764         break;
765       }
766     }
767
768     if (getter != null && booleanGetter != null) {
769       return new GrAccessorMethod[]{getter, booleanGetter};
770     }
771     else if (getter != null) {
772       return new GrAccessorMethod[]{getter};
773     }
774     else {
775       return GrAccessorMethod.EMPTY_ARRAY;
776     }
777   }
778 }