IDEA-52183: If groovy library added to a project 'Method getMetaClass is not implemen...
[fedora-idea.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / psi / util / GrClassImplUtil.java
blobf6dba4f5577616266c1527e30cdfc2313ef64269
1 /*
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;
56 import java.util.*;
58 /**
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() {
72 @Nullable
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);
80 @Nullable
81 public static PsiClass getBaseClass(GrTypeDefinition grType) {
82 if (grType.isEnum()) {
83 return JavaPsiFacade.getInstance(grType.getProject()).findClass(CommonClassNames.JAVA_LANG_ENUM, grType.getResolveScope());
85 else {
86 return JavaPsiFacade.getInstance(grType.getProject()).findClass(CommonClassNames.JAVA_LANG_OBJECT, grType.getResolveScope());
90 @NotNull
91 public static PsiClassType[] getExtendsListTypes(GrTypeDefinition grType) {
92 final List<PsiClassType> extendsTypes = getReferenceListTypes(grType.getExtendsClause());
93 return extendsTypes.toArray(new PsiClassType[extendsTypes.size()]);
96 @NotNull
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());
112 @NotNull
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());
127 else {
128 return JavaPsiFacade.getInstance(grType.getProject()).getElementFactory()
129 .createTypeByFQClassName(CommonClassNames.JAVA_LANG_OBJECT, grType.getResolveScope());
133 @NotNull
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;
143 visited.add(clazz);
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>();
184 if (list != null) {
185 for (GrCodeReferenceElement ref : list.getReferenceElements()) {
186 types.add(new GrClassReferenceType(ref));
189 return types;
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()]);
203 @NotNull
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);
232 if (name != null) {
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;
241 else {
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();
254 if (name == null) {
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)))) {
261 return false;
266 else {
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)))) {
274 return false;
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))) {
289 continue;
292 if (!processor.execute(innerClass, state)) {
293 return false;
300 return true;
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;
318 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();
335 if (isGetter) {
336 return args.length == 0;
338 else {
339 return args.length == 1 &&
340 TypesUtil
341 .isAssignableByMethodCallConversion(aField.getType(), args[0].getType(), place.getManager(), place.getResolveScope());
346 return false;
349 @Nullable
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;
361 return null;
364 private static PsiMethod[] findMethodsByName(GrTypeDefinition grType,
365 String name,
366 boolean checkBases,
367 boolean includeSyntheticAccessors) {
368 if (!checkBases) {
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));
381 @NotNull
382 public static PsiMethod[] findMethodsBySignature(GrTypeDefinition grType, PsiMethod patternMethod, boolean checkBases) {
383 return findMethodsBySignature(grType, patternMethod, checkBases, true);
386 @NotNull
387 public static PsiMethod[] findCodeMethodsBySignature(GrTypeDefinition grType, PsiMethod patternMethod, boolean checkBases) {
388 return findMethodsBySignature(grType, patternMethod, checkBases, false);
391 @NotNull
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,
398 boolean checkBases,
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
411 result.add(method);
414 return result.toArray(new PsiMethod[result.size()]);
417 @NotNull
418 public static PsiMethod[] findCodeMethodsByName(GrTypeDefinition grType, @NonNls String name, boolean checkBases) {
419 return findMethodsByName(grType, name, checkBases, false);
422 @NotNull
423 public static List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(GrTypeDefinition grType,
424 String name,
425 boolean checkBases) {
426 final ArrayList<Pair<PsiMethod, PsiSubstitutor>> result = new ArrayList<Pair<PsiMethod, PsiSubstitutor>>();
428 if (!checkBases) {
429 final PsiMethod[] methods = grType.findMethodsByName( name, false);
430 for (PsiMethod method : methods) {
431 result.add(new Pair<PsiMethod, PsiSubstitutor>(method, PsiSubstitutor.EMPTY));
434 else {
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()));
445 return result;
448 @NotNull
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()));
458 return result;
461 @Nullable
462 public static PsiField findFieldByName(GrTypeDefinition grType, String name, boolean checkBases) {
463 if (!checkBases) {
464 for (GrField field : grType.getFields()) {
465 if (name.equals(field.getName())) return field;
468 return null;
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);