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.resolve;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.roots.ProjectRootManager;
21 import com.intellij.openapi.util.Key;
22 import com.intellij.openapi.util.Pair;
23 import com.intellij.openapi.util.Ref;
24 import com.intellij.psi.*;
25 import com.intellij.psi.scope.JavaScopeProcessorEvent;
26 import com.intellij.psi.scope.NameHint;
27 import com.intellij.psi.scope.PsiScopeProcessor;
28 import com.intellij.psi.util.*;
29 import org.jetbrains.annotations.NotNull;
30 import org.jetbrains.annotations.Nullable;
31 import org.jetbrains.plugins.groovy.dsl.GroovyDslFileIndex;
32 import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
33 import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
34 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
35 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
36 import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
37 import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
38 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
39 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrApplicationStatement;
40 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
41 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall;
42 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
43 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrCallExpression;
44 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
45 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition;
46 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
47 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
48 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember;
49 import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
50 import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassResolverProcessor;
51 import org.jetbrains.plugins.groovy.lang.resolve.processors.MethodResolverProcessor;
52 import org.jetbrains.plugins.groovy.lang.resolve.processors.PropertyResolverProcessor;
53 import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor;
56 import java.util.concurrent.ConcurrentHashMap;
61 @SuppressWarnings({"StringBufferReplaceableByString"})
62 public class ResolveUtil {
63 public static final PsiScopeProcessor.Event DECLARATION_SCOPE_PASSED = new PsiScopeProcessor.Event() {};
65 private ResolveUtil() {
68 public static boolean treeWalkUp(@NotNull GroovyPsiElement place, PsiScopeProcessor processor, boolean processNonCodeMethods) {
69 PsiElement lastParent = null;
70 PsiElement run = place;
72 final Project project = place.getProject();
73 PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
76 if (!run.processDeclarations(processor, ResolveState.initial(), lastParent, place)) return false;
77 if (processNonCodeMethods) {
78 if (run instanceof GrTypeDefinition) {
79 processNonCodeMethods(factory.createType(((GrTypeDefinition)run)), processor, place);
81 else if ((run instanceof GroovyFileBase) && ((GroovyFileBase)run).isScript()) {
82 final PsiClass psiClass = ((GroovyFileBase)run).getScriptClass();
83 if (psiClass != null) {
84 processNonCodeMethods(factory.createType(psiClass), processor, place);
89 run = run.getContext();
90 processor.handleEvent(JavaScopeProcessorEvent.CHANGE_LEVEL, null);
96 public static boolean processChildren(PsiElement element,
97 PsiScopeProcessor processor,
98 ResolveState substitutor,
99 PsiElement lastParent,
101 PsiElement run = lastParent == null ? element.getLastChild() : lastParent.getPrevSibling();
102 while (run != null) {
103 if (!run.processDeclarations(processor, substitutor, null, place)) return false;
104 run = run.getPrevSibling();
111 public static String getNameHint(PsiScopeProcessor processor) {
112 NameHint nameHint = processor.getHint(NameHint.KEY);
113 if (nameHint == null) {
117 return nameHint.getName(ResolveState.initial());
120 public static boolean processElement(PsiScopeProcessor processor, PsiNamedElement namedElement, ResolveState state) {
121 NameHint nameHint = processor.getHint(NameHint.KEY);
122 //todo [DIANA] look more carefully
123 String name = nameHint == null ? null : nameHint.getName(state);
124 if (name == null || name.equals(namedElement.getName())) {
125 return processor.execute(namedElement, state);
131 public static boolean processAllDeclarations(@NotNull PsiType type,
132 @NotNull PsiScopeProcessor processor,
133 @NotNull ResolveState state,
134 @NotNull GroovyPsiElement place) {
135 if (type instanceof PsiClassType) {
136 final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics();
137 final PsiClass psiClass = resolveResult.getElement();
138 if (psiClass != null) {
139 if (!psiClass.processDeclarations(processor, state, null, place)) return false;
142 return processNonCodeMethods(type, processor, place, state);
145 public static boolean processNonCodeMethods(PsiType type,
146 PsiScopeProcessor processor,
147 GroovyPsiElement place) {
148 return processNonCodeMethods(type, processor, place, ResolveState.initial());
151 public static boolean processNonCodeMethods(PsiType type,
152 PsiScopeProcessor processor,
153 GroovyPsiElement place,
154 ResolveState state) {
155 if (!NonCodeMembersContributor.runContributors(type, processor, place, state)) {
159 if (!GroovyDslFileIndex.processExecutors(type, place, processor, state)) {
166 private static final Key<PsiType> COMPARABLE = Key.create(CommonClassNames.JAVA_LANG_COMPARABLE);
167 private static final Key<PsiType> SERIALIZABLE = Key.create(CommonClassNames.JAVA_IO_SERIALIZABLE);
169 private static void collectSuperTypes(PsiType type, Map<String, PsiType> visited, Project project) {
170 String qName = rawCanonicalText(type);
172 if (visited.put(qName, type) != null) {
176 final PsiType[] superTypes = type.getSuperTypes();
177 for (PsiType superType : superTypes) {
178 collectSuperTypes(TypeConversionUtil.erasure(superType), visited, project);
181 if (type instanceof PsiArrayType && superTypes.length == 0) {
182 PsiType comparable = createTypeFromText(project, COMPARABLE, CommonClassNames.JAVA_LANG_COMPARABLE);
183 PsiType serializable = createTypeFromText(project, SERIALIZABLE, CommonClassNames.JAVA_IO_SERIALIZABLE);
184 collectSuperTypes(comparable, visited, project);
185 collectSuperTypes(serializable, visited, project);
190 public static PsiType createTypeFromText(Project project, Key<PsiType> key, String text) {
191 final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
192 PsiType type = project.getUserData(key);
194 type = factory.createTypeFromText(text, null);
195 project.putUserData(key, type);
200 public static Map<String, PsiType> getAllSuperTypes(@NotNull PsiType base, final Project project) {
201 final Map<String, Map<String, PsiType>> cache =
202 CachedValuesManager.getManager(project).getCachedValue(project, new CachedValueProvider<Map<String, Map<String, PsiType>>>() {
204 public Result<Map<String, Map<String, PsiType>>> compute() {
205 final Map<String, Map<String, PsiType>> result = new ConcurrentHashMap<String, Map<String, PsiType>>();
206 return Result.create(result, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, ProjectRootManager.getInstance(project));
210 final PsiClass cls = PsiUtil.resolveClassInType(base);
211 //noinspection ConstantConditions
213 if (cls instanceof PsiTypeParameter) {
214 final PsiClass superClass = cls.getSuperClass();
215 key = cls.getName() + (superClass == null ? CommonClassNames.JAVA_LANG_OBJECT : superClass.getName());
218 key = TypeConversionUtil.erasure(base).getCanonicalText();
220 if (key == null) key = "";
221 Map<String, PsiType> result = cache.get(key);
222 if (result == null) {
223 result = new HashMap<String, PsiType>();
224 collectSuperTypes(base, result, project);
225 cache.put(key, result);
230 public static boolean isInheritor(PsiType type, @NotNull String baseClass, Project project) {
231 return getAllSuperTypes(type, project).keySet().contains(baseClass);
236 private static String rawCanonicalText(@NotNull PsiType type) {
237 final String result = type.getCanonicalText();
238 if (result == null) return "";
239 final int i = result.indexOf('<');
240 if (i > 0) return result.substring(0, i);
245 public static PsiType getListTypeForSpreadOperator(GrReferenceExpression refExpr, PsiType componentType) {
246 PsiClass clazz = JavaPsiFacade.getInstance(refExpr.getManager().getProject())
247 .findClass(CommonClassNames.JAVA_UTIL_LIST, refExpr.getResolveScope());
249 PsiTypeParameter[] typeParameters = clazz.getTypeParameters();
250 if (typeParameters.length == 1) {
251 PsiSubstitutor substitutor = PsiSubstitutor.EMPTY.put(typeParameters[0], componentType);
252 return JavaPsiFacade.getInstance(refExpr.getProject()).getElementFactory().createType(clazz, substitutor);
259 public static GroovyPsiElement resolveProperty(GroovyPsiElement place, String name) {
260 PropertyResolverProcessor processor = new PropertyResolverProcessor(name, place);
261 return resolveExistingElement(place, processor, GrVariable.class, GrReferenceExpression.class);
265 public static PsiClass resolveClass(GroovyPsiElement place, String name) {
266 ClassResolverProcessor processor = new ClassResolverProcessor(name, place);
267 return resolveExistingElement(place, processor, PsiClass.class);
271 public static <T> T resolveExistingElement(GroovyPsiElement place, ResolverProcessor processor, Class<? extends T>... classes) {
272 treeWalkUp(place, processor, true);
273 final GroovyResolveResult[] candidates = processor.getCandidates();
274 for (GroovyResolveResult candidate : candidates) {
275 final PsiElement element = candidate.getElement();
276 if (element == place) continue;
277 for (Class<? extends T> clazz : classes) {
278 if (clazz.isInstance(element)) //noinspection unchecked
287 public static Pair<GrStatement, GrLabeledStatement> resolveLabelTargets(@Nullable String labelName,
288 @Nullable PsiElement element,
290 if (element == null) return new Pair<GrStatement, GrLabeledStatement>(null, null);
292 if (labelName == null) {
294 element = element.getParent();
295 if (element == null || element instanceof GrClosableBlock || element instanceof GrMember || element instanceof GroovyFile) {
296 return new Pair<GrStatement, GrLabeledStatement>(null, null);
299 while (!(element instanceof GrLoopStatement) && !(isBreak && element instanceof GrSwitchStatement));
300 return new Pair<GrStatement, GrLabeledStatement>(((GrStatement)element), null);
303 GrStatement statement = null;
305 PsiElement last = element;
306 element = element.getParent();
307 if (element == null || element instanceof GrMember || element instanceof GroovyFile) break;
308 if (element instanceof GrStatement && !(element instanceof GrClosableBlock)) {
309 statement = (GrStatement)element;
311 PsiElement sibling = element;
312 while (sibling != null) {
313 final GrLabeledStatement labelStatement = findLabelStatementIn(sibling, last, labelName);
314 if (labelStatement != null) {
315 return new Pair<GrStatement, GrLabeledStatement>(statement, labelStatement);
317 sibling = sibling.getPrevSibling();
319 if (element instanceof GrClosableBlock) break;
322 return new Pair<GrStatement, GrLabeledStatement>(null, null);
325 private static boolean isApplicableLabelStatement(PsiElement element, String labelName) {
326 return ((element instanceof GrLabeledStatement && labelName.equals(((GrLabeledStatement)element).getLabelName())));
330 private static GrLabeledStatement findLabelStatementIn(PsiElement element, PsiElement lastChild, String labelName) {
331 if (isApplicableLabelStatement(element, labelName)) {
332 return (GrLabeledStatement)element;
334 for (PsiElement child = element.getFirstChild(); child != null && child != lastChild; child = child.getNextSibling()) {
335 final GrLabeledStatement statement = findLabelStatementIn(child, child, labelName);
336 if (statement != null) return statement;
342 public static GrLabeledStatement resolveLabeledStatement(@Nullable String labelName, @Nullable PsiElement element, boolean isBreak) {
343 return resolveLabelTargets(labelName, element, isBreak).second;
347 public static GrStatement resolveLabelTargetStatement(@Nullable String labelName, @Nullable PsiElement element, boolean isBreak) {
348 return resolveLabelTargets(labelName, element, isBreak).first;
351 public static boolean processCategoryMembers(PsiElement place, ResolverProcessor processor) {
352 PsiElement prev = null;
353 Ref<Boolean> result = new Ref<Boolean>(null);
354 while (place != null) {
355 if (place instanceof GrMember) break;
356 if (categoryIteration(place, processor, prev, result)) return result.get();
359 place = place.getContext();
365 private static boolean categoryIteration(PsiElement place, ResolverProcessor processor, PsiElement prev, Ref<Boolean> result) {
366 if (!(place instanceof GrMethodCallExpression)) return false;
368 final GrMethodCallExpression call = (GrMethodCallExpression)place;
369 final GrExpression invoked = call.getInvokedExpression();
370 if (!(invoked instanceof GrReferenceExpression) || !"use".equals(((GrReferenceExpression)invoked).getReferenceName())) return false;
372 final GrClosableBlock[] closures = call.getClosureArguments();
373 if (closures.length != 1 || !closures[0].equals(prev)) return false;
375 if (!useCategoryClass(call)) return false;
377 final GrArgumentList argList = call.getArgumentList();
378 if (argList == null) return false;
380 result.set(Boolean.TRUE);
381 final GrExpression[] args = argList.getExpressionArguments();
382 for (GrExpression arg : args) {
383 if (arg instanceof GrReferenceExpression) {
384 final PsiElement resolved = ((GrReferenceExpression)arg).resolve();
385 if (resolved instanceof PsiClass) {
386 if (!resolved.processDeclarations(processor, ResolveState.initial().put(ResolverProcessor.RESOLVE_CONTEXT, call), null, place)) {
387 result.set(Boolean.FALSE);
395 private static boolean useCategoryClass(GrMethodCallExpression call) {
396 return call.resolveMethod() instanceof GrGdkMethod;
399 public static PsiElement[] mapToElements(GroovyResolveResult[] candidates) {
400 PsiElement[] elements = new PsiElement[candidates.length];
401 for (int i = 0; i < elements.length; i++) {
402 elements[i] = candidates[i].getElement();
408 public static GroovyResolveResult[] filterSameSignatureCandidates(Collection<GroovyResolveResult> candidates, int argumentCount) {
409 GroovyResolveResult[] array = candidates.toArray(new GroovyResolveResult[candidates.size()]);
410 if (array.length == 1) return array;
412 List<GroovyResolveResult> result = new ArrayList<GroovyResolveResult>();
413 result.add(array[0]);
416 for (int i = 1; i < array.length; i++) {
417 PsiElement currentElement = array[i].getElement();
418 if (currentElement instanceof PsiMethod) {
419 PsiMethod currentMethod = (PsiMethod)currentElement;
420 for (Iterator<GroovyResolveResult> iterator = result.iterator(); iterator.hasNext();) {
421 final GroovyResolveResult otherResolveResult = iterator.next();
422 PsiElement element = otherResolveResult.getElement();
423 if (element instanceof PsiMethod) {
424 PsiMethod method = (PsiMethod)element;
425 if (dominated(currentMethod, array[i].getSubstitutor(), method, otherResolveResult.getSubstitutor())) {
428 else if (dominated(method, otherResolveResult.getSubstitutor(), currentMethod, array[i].getSubstitutor())) {
435 result.add(array[i]);
438 return result.toArray(new GroovyResolveResult[result.size()]);
441 public static boolean dominated(PsiMethod method1,
442 PsiSubstitutor substitutor1,
444 PsiSubstitutor substitutor2) { //method1 has more general parameter types then method2
445 if (!method1.getName().equals(method2.getName())) return false;
447 PsiParameter[] params1 = method1.getParameterList().getParameters();
448 PsiParameter[] params2 = method2.getParameterList().getParameters();
450 if (params1.length != params2.length) return false;
452 for (int i = 0; i < params2.length; i++) {
453 PsiType type1 = substitutor1.substitute(params1[i].getType());
454 PsiType type2 = substitutor2.substitute(params2[i].getType());
455 if (!type1.equals(type2)) return false;
461 public static GroovyResolveResult[] getCallVariants(GroovyPsiElement place) {
462 final PsiElement parent = place.getParent();
463 GroovyResolveResult[] variants = GroovyResolveResult.EMPTY_ARRAY;
464 if (parent instanceof GrCallExpression) {
465 variants = ((GrCallExpression) parent).getCallVariants(place instanceof GrExpression ? (GrExpression)place : null);
466 } else if (parent instanceof GrConstructorInvocation) {
467 final PsiClass clazz = ((GrConstructorInvocation) parent).getDelegatedClass();
469 final PsiMethod[] constructors = clazz.getConstructors();
470 variants = getConstructorResolveResult(constructors, place);
472 } else if (parent instanceof GrAnonymousClassDefinition) {
473 final PsiElement element = ((GrAnonymousClassDefinition)parent).getBaseClassReferenceGroovy().resolve();
474 if (element instanceof PsiClass) {
475 final PsiMethod[] constructors = ((PsiClass)element).getConstructors();
476 variants = getConstructorResolveResult(constructors, place);
479 else if (parent instanceof GrApplicationStatement) {
480 final GrExpression funExpr = ((GrApplicationStatement) parent).getInvokedExpression();
481 if (funExpr instanceof GrReferenceExpression) {
482 variants = ((GrReferenceExpression) funExpr).getSameNameVariants();
484 } else if (place instanceof GrReferenceExpression) {
485 variants = ((GrReferenceExpression) place).getSameNameVariants();
490 public static GroovyResolveResult[] getConstructorResolveResult(PsiMethod[] constructors, PsiElement place) {
491 GroovyResolveResult[] variants = new GroovyResolveResult[constructors.length];
492 for (int i = 0; i < constructors.length; i++) {
493 final boolean isAccessible = PsiUtil.isAccessible(constructors[i], place, null);
494 variants[i] = new GroovyResolveResultImpl(constructors[i], isAccessible);
499 public static GroovyResolveResult[] getNonCodeConstructors(PsiClass psiClass, GroovyPsiElement place, PsiSubstitutor substitutor) {
500 final PsiClassType qualifierType = JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass);
501 final MethodResolverProcessor processor = new MethodResolverProcessor(psiClass.getName(), place, true, null, null, PsiType.EMPTY_ARRAY);
502 NonCodeMembersContributor
503 .runContributors(qualifierType, processor, place, ResolveState.initial().put(PsiSubstitutor.KEY, substitutor));
504 return processor.getCandidates();
507 public static PsiMethod[] getAllClassConstructors(PsiClass psiClass, GroovyPsiElement place, PsiSubstitutor substitutor) {
508 final PsiMethod[] realConstructors = psiClass.getConstructors();
509 final GroovyResolveResult[] nonCodeConstructors = getNonCodeConstructors(psiClass, place, substitutor);
510 PsiMethod[] constructors = new PsiMethod[realConstructors.length + nonCodeConstructors.length];
511 System.arraycopy(realConstructors, 0, constructors, 0, realConstructors.length);
512 for (int i = 0; i < nonCodeConstructors.length; i++) {
513 GroovyResolveResult nonCodeConstructor = nonCodeConstructors[i];
514 final PsiElement element = nonCodeConstructor.getElement();
515 if (element instanceof PsiMethod) {
516 constructors[i + realConstructors.length] = (PsiMethod)element;
522 public static boolean isInUseScope(GroovyResolveResult resolveResult) {
523 return isInUseScope(resolveResult.getCurrentFileResolveContext());
526 public static boolean isInUseScope(GroovyPsiElement context) {
527 if (context instanceof GrMethodCall) {
528 final GrExpression expression = ((GrMethodCall)context).getInvokedExpression();
529 if (expression instanceof GrReferenceExpression) {
530 final PsiElement resolved = ((GrReferenceExpression)expression).resolve();
531 if (resolved instanceof GrGdkMethod && "use".equals(((GrGdkMethod)resolved).getStaticMethod().getName())) {
539 public static boolean isInWithContext(GroovyPsiElement context) {
540 if (context instanceof GrExpression) {
541 final PsiElement parent = context.getParent();
542 if (parent instanceof GrReferenceExpression && ((GrReferenceExpression)parent).getQualifier() == context) {
543 final PsiElement pparent = parent.getParent();
544 if (pparent instanceof GrMethodCall) {
545 final PsiMethod method = ((GrMethodCall)pparent).resolveMethod();
546 if (method instanceof GrGdkMethod && "with".equals(method.getName())) {