b64c58e9714771edcb43b1025d90e9fd529450bd
[fedora-idea.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / psi / util / GrClassImplUtil.java
blobb64c58e9714771edcb43b1025d90e9fd529450bd
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 // 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>();
186 if (list != null) {
187 for (GrCodeReferenceElement ref : list.getReferenceElements()) {
188 types.add(new GrClassReferenceType(ref));
191 return types;
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()]);
205 @NotNull
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);
234 if (name != null) {
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;
243 else {
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();
256 if (name == null) {
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)))) {
263 return false;
268 else {
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)))) {
276 return false;
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))) {
291 continue;
294 if (!processor.execute(innerClass, state)) {
295 return false;
302 return true;
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;
320 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();
337 if (isGetter) {
338 return args.length == 0;
340 else {
341 return args.length == 1 &&
342 TypesUtil
343 .isAssignableByMethodCallConversion(aField.getType(), args[0].getType(), place.getManager(), place.getResolveScope());
348 return false;
351 @Nullable
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;
363 return null;
366 private static PsiMethod[] findMethodsByName(GrTypeDefinition grType,
367 String name,
368 boolean checkBases,
369 boolean includeSyntheticAccessors) {
370 if (!checkBases) {
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));
383 @NotNull
384 public static PsiMethod[] findMethodsBySignature(GrTypeDefinition grType, PsiMethod patternMethod, boolean checkBases) {
385 return findMethodsBySignature(grType, patternMethod, checkBases, true);
388 @NotNull
389 public static PsiMethod[] findCodeMethodsBySignature(GrTypeDefinition grType, PsiMethod patternMethod, boolean checkBases) {
390 return findMethodsBySignature(grType, patternMethod, checkBases, false);
393 @NotNull
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,
400 boolean checkBases,
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
413 result.add(method);
416 return result.toArray(new PsiMethod[result.size()]);
419 @NotNull
420 public static PsiMethod[] findCodeMethodsByName(GrTypeDefinition grType, @NonNls String name, boolean checkBases) {
421 return findMethodsByName(grType, name, checkBases, false);
424 @NotNull
425 public static List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(GrTypeDefinition grType,
426 String name,
427 boolean checkBases) {
428 final ArrayList<Pair<PsiMethod, PsiSubstitutor>> result = new ArrayList<Pair<PsiMethod, PsiSubstitutor>>();
430 if (!checkBases) {
431 final PsiMethod[] methods = grType.findMethodsByName( name, false);
432 for (PsiMethod method : methods) {
433 result.add(new Pair<PsiMethod, PsiSubstitutor>(method, PsiSubstitutor.EMPTY));
436 else {
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()));
447 return result;
450 @NotNull
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()));
460 return result;
463 @Nullable
464 public static PsiField findFieldByName(GrTypeDefinition grType, String name, boolean checkBases) {
465 if (!checkBases) {
466 for (GrField field : grType.getFields()) {
467 if (name.equals(field.getName())) return field;
470 return null;
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);