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.util.Pair;
21 import com.intellij.psi.*;
22 import com.intellij.psi.scope.JavaScopeProcessorEvent;
23 import com.intellij.psi.scope.NameHint;
24 import com.intellij.psi.scope.PsiScopeProcessor;
25 import com.intellij.psi.search.GlobalSearchScope;
26 import com.intellij.psi.util.MethodSignature;
27 import com.intellij.psi.util.MethodSignatureUtil;
28 import com.intellij.psi.util.TypeConversionUtil;
29 import org.jetbrains.annotations.NotNull;
30 import org.jetbrains.annotations.Nullable;
31 import org.jetbrains.plugins.groovy.annotator.intentions.dynamic.DynamicManager;
32 import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
33 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
34 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
35 import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
36 import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
37 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
38 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
39 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
40 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
41 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
42 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
43 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember;
44 import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager;
45 import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassResolverProcessor;
46 import org.jetbrains.plugins.groovy.lang.resolve.processors.PropertyResolverProcessor;
47 import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor;
54 @SuppressWarnings({"StringBufferReplaceableByString"})
55 public class ResolveUtil {
56 public static final PsiScopeProcessor.Event DECLARATION_SCOPE_PASSED = new PsiScopeProcessor.Event() {
59 private ResolveUtil() {
62 public static boolean treeWalkUp(PsiElement place, PsiScopeProcessor processor, boolean processNonCodeMethods) {
63 PsiElement lastParent = null;
64 PsiElement run = place;
66 final Project project = place.getProject();
67 PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
70 if (!run.processDeclarations(processor, ResolveState.initial(), lastParent, place)) return false;
71 if (processNonCodeMethods) {
72 if (run instanceof GrTypeDefinition) {
73 processNonCodeMethods(factory.createType(((GrTypeDefinition)run)), processor, project, place, false);
75 else if ((run instanceof GroovyFile) && ((GroovyFile)run).isScript()) {
76 processNonCodeMethods(factory.createType(((GroovyFile)run).getScriptClass()), processor, project, place, false);
80 run = run.getContext();
81 processor.handleEvent(JavaScopeProcessorEvent.CHANGE_LEVEL, null);
87 public static boolean processChildren(PsiElement element,
88 PsiScopeProcessor processor,
89 ResolveState substitutor,
90 PsiElement lastParent,
92 PsiElement run = lastParent == null ? element.getLastChild() : lastParent.getPrevSibling();
94 if (!run.processDeclarations(processor, substitutor, null, place)) return false;
95 run = run.getPrevSibling();
101 public static boolean processElement(PsiScopeProcessor processor, PsiNamedElement namedElement) {
102 NameHint nameHint = processor.getHint(NameHint.KEY);
103 //todo [DIANA] look more carefully
104 String name = nameHint == null ? null : nameHint.getName(ResolveState.initial());
105 if (name == null || name.equals(namedElement.getName())) {
106 return processor.execute(namedElement, ResolveState.initial());
112 public static boolean processNonCodeMethods(PsiType type,
113 PsiScopeProcessor processor,
116 boolean forCompletion) {
117 return processNonCodeMethods(type, processor, project, new HashSet<String>(), place, forCompletion);
120 private static boolean processNonCodeMethods(PsiType type,
121 PsiScopeProcessor processor,
125 boolean forCompletion) {
126 String qName = rawCanonicalText(type);
129 if (visited.contains(qName)) return true;
131 for (PsiMethod defaultMethod : GroovyPsiManager.getInstance(project).getDefaultMethods(qName)) {
132 if (!processElement(processor, defaultMethod)) return false;
135 for (PsiMethod method : DynamicManager.getInstance(project).getMethods(qName)) {
136 if (!processElement(processor, method)) return false;
139 for (PsiVariable var : DynamicManager.getInstance(project).getProperties(qName)) {
140 if (!processElement(processor, var)) return false;
143 for (NonCodeMembersProcessor membersProcessor : NonCodeMembersProcessor.EP_NAME.getExtensions()) {
144 if (!membersProcessor.processNonCodeMembers(type, processor, place, forCompletion)) return false;
147 if (type instanceof PsiArrayType && visited.size() == 1) {
148 //implicit super types
149 PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
150 PsiClassType t = factory.createTypeByFQClassName("java.lang.Object", GlobalSearchScope.allScope(project));
151 if (!processNonCodeMethods(t, processor, project, visited, place, forCompletion)) return false;
152 t = factory.createTypeByFQClassName("java.lang.Comparable", GlobalSearchScope.allScope(project));
153 if (!processNonCodeMethods(t, processor, project, visited, place, forCompletion)) return false;
154 t = factory.createTypeByFQClassName("java.io.Serializable", GlobalSearchScope.allScope(project));
155 if (!processNonCodeMethods(t, processor, project, visited, place, forCompletion)) return false;
157 for (PsiType superType : type.getSuperTypes()) {
158 if (!processNonCodeMethods(TypeConversionUtil.erasure(superType), processor, project, visited, place, forCompletion)) {
168 private static String rawCanonicalText(PsiType type) {
169 final String result = type.getCanonicalText();
170 if (result == null) return null;
171 final int i = result.indexOf('<');
172 if (i > 0) return result.substring(0, i);
177 public static PsiType getListTypeForSpreadOperator(GrReferenceExpression refExpr, PsiType componentType) {
178 PsiClass clazz = findListClass(refExpr.getManager(), refExpr.getResolveScope());
180 PsiTypeParameter[] typeParameters = clazz.getTypeParameters();
181 if (typeParameters.length == 1) {
182 PsiSubstitutor substitutor = PsiSubstitutor.EMPTY.put(typeParameters[0], componentType);
183 return JavaPsiFacade.getInstance(refExpr.getProject()).getElementFactory().createType(clazz, substitutor);
191 public static PsiClass findListClass(PsiManager manager, GlobalSearchScope resolveScope) {
192 return JavaPsiFacade.getInstance(manager.getProject()).findClass("java.util.List", resolveScope);
195 public static GroovyPsiElement resolveProperty(GroovyPsiElement place, String name) {
196 PropertyResolverProcessor processor = new PropertyResolverProcessor(name, place);
197 return resolveExistingElement(place, processor, GrVariable.class, GrReferenceExpression.class);
201 public static PsiClass resolveClass(GroovyPsiElement place, String name) {
202 ClassResolverProcessor processor = new ClassResolverProcessor(name, place);
203 return resolveExistingElement(place, processor, PsiClass.class);
207 public static <T> T resolveExistingElement(GroovyPsiElement place, ResolverProcessor processor, Class<? extends T>... classes) {
208 treeWalkUp(place, processor, true);
209 final GroovyResolveResult[] candidates = processor.getCandidates();
210 for (GroovyResolveResult candidate : candidates) {
211 final PsiElement element = candidate.getElement();
212 if (element == place) continue;
213 for (Class<? extends T> clazz : classes) {
214 if (clazz.isInstance(element)) return (T)element;
222 public static Pair<GrStatement, GrLabeledStatement> resolveLabelTargets(@Nullable String labelName,
223 @Nullable PsiElement element,
225 if (element == null) return new Pair<GrStatement, GrLabeledStatement>(null, null);
227 if (labelName == null) {
229 element = element.getParent();
230 if (element == null || element instanceof GrClosableBlock || element instanceof GrMember || element instanceof GroovyFile) {
231 return new Pair<GrStatement, GrLabeledStatement>(null, null);
234 while (!(element instanceof GrLoopStatement) && !(isBreak && element instanceof GrSwitchStatement));
235 return new Pair<GrStatement, GrLabeledStatement>(((GrStatement)element), null);
238 GrStatement statement = null;
240 PsiElement last = element;
241 element = element.getParent();
242 if (element == null || element instanceof GrMember || element instanceof GroovyFile) break;
243 if (element instanceof GrStatement && !(element instanceof GrClosableBlock)) {
244 statement = (GrStatement)element;
246 PsiElement sibling = element;
247 while (sibling != null) {
248 final GrLabeledStatement labelStatement = findLabelStatementIn(sibling, last, labelName);
249 if (labelStatement != null) {
250 return new Pair<GrStatement, GrLabeledStatement>(statement, labelStatement);
252 sibling = sibling.getPrevSibling();
254 if (element instanceof GrClosableBlock) break;
257 return new Pair<GrStatement, GrLabeledStatement>(null, null);
260 private static boolean isApplicableLabelStatement(PsiElement element, String labelName) {
261 return ((element instanceof GrLabeledStatement && labelName.equals(((GrLabeledStatement)element).getLabelName())));
265 private static GrLabeledStatement findLabelStatementIn(PsiElement element, PsiElement lastChild, String labelName) {
266 if (isApplicableLabelStatement(element, labelName)) {
267 return (GrLabeledStatement)element;
269 for (PsiElement child = element.getFirstChild(); child != null && child != lastChild; child = child.getNextSibling()) {
270 final GrLabeledStatement statement = findLabelStatementIn(child, child, labelName);
271 if (statement != null) return statement;
277 public static GrLabeledStatement resolveLabeledStatement(@Nullable String labelName, @Nullable PsiElement element, boolean isBreak) {
278 return resolveLabelTargets(labelName, element, isBreak).second;
282 public static GrStatement resolveLabelTargetStatement(@Nullable String labelName, @Nullable PsiElement element, boolean isBreak) {
283 return resolveLabelTargets(labelName, element, isBreak).first;
286 public static boolean processCategoryMembers(PsiElement place, ResolverProcessor processor, PsiClassType thisType) {
287 PsiElement prev = null;
288 while (place != null) {
289 if (place instanceof GrMember) break;
291 if (place instanceof GrMethodCallExpression) {
292 final GrMethodCallExpression call = (GrMethodCallExpression)place;
293 final GrExpression invoked = call.getInvokedExpression();
294 if (invoked instanceof GrReferenceExpression && "use".equals(((GrReferenceExpression)invoked).getReferenceName())) {
295 final GrClosableBlock[] closures = call.getClosureArguments();
296 if (closures.length == 1 && closures[0].equals(prev)) {
297 if (useCategoryClass(call)) {
298 final GrArgumentList argList = call.getArgumentList();
299 if (argList != null) {
300 final GrExpression[] args = argList.getExpressionArguments();
301 if (args.length == 1 && args[0] instanceof GrReferenceExpression) {
302 final PsiElement resolved = ((GrReferenceExpression)args[0]).resolve();
303 if (resolved instanceof PsiClass) {
305 processor.setCurrentFileResolveContext(call);
306 if (!resolved.processDeclarations(processor, ResolveState.initial(), null, place)) return false;
309 processor.setCurrentFileResolveContext(null);
320 place = place.getContext();
326 private static boolean useCategoryClass(GrMethodCallExpression call) {
328 final PsiMethod resolved = call.resolveMethod();
329 if (resolved instanceof GrGdkMethod) {
330 final PsiElementFactory factory = JavaPsiFacade.getInstance(call.getProject()).getElementFactory();
331 final GlobalSearchScope scope = call.getResolveScope();
332 final PsiType[] parametersType =
333 {factory.createTypeByFQClassName("java.lang.Class", scope), factory.createTypeByFQClassName("groovy.lang.Closure", scope)};
334 final MethodSignature pattern =
335 MethodSignatureUtil.createMethodSignature("use", parametersType, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY);
336 return resolved.getSignature(PsiSubstitutor.EMPTY).equals(pattern);
342 public static PsiElement[] mapToElements(GroovyResolveResult[] candidates) {
343 PsiElement[] elements = new PsiElement[candidates.length];
344 for (int i = 0; i < elements.length; i++) {
345 elements[i] = candidates[i].getElement();
351 public static GroovyResolveResult[] filterSameSignatureCandidates(Collection<GroovyResolveResult> candidates, int argumentCount) {
352 GroovyResolveResult[] array = candidates.toArray(new GroovyResolveResult[candidates.size()]);
353 if (array.length == 1) return array;
355 List<GroovyResolveResult> result = new ArrayList<GroovyResolveResult>();
356 result.add(array[0]);
359 for (int i = 1; i < array.length; i++) {
360 PsiElement currentElement = array[i].getElement();
361 if (currentElement instanceof PsiMethod) {
362 PsiMethod currentMethod = (PsiMethod)currentElement;
363 for (Iterator<GroovyResolveResult> iterator = result.iterator(); iterator.hasNext();) {
364 final GroovyResolveResult otherResolveResult = iterator.next();
365 PsiElement element = otherResolveResult.getElement();
366 if (element instanceof PsiMethod) {
367 PsiMethod method = (PsiMethod)element;
368 if (dominated(currentMethod, array[i].getSubstitutor(), method, otherResolveResult.getSubstitutor(), argumentCount)) {
371 else if (dominated(method, otherResolveResult.getSubstitutor(), currentMethod, array[i].getSubstitutor(), argumentCount)) {
378 result.add(array[i]);
381 return result.toArray(new GroovyResolveResult[result.size()]);
384 public static boolean dominated(PsiMethod method1,
385 PsiSubstitutor substitutor1,
387 PsiSubstitutor substitutor2,
388 int argumentCount) { //method1 has more general parameter types then method2
389 if (!method1.getName().equals(method2.getName())) return false;
391 PsiParameter[] params1 = method1.getParameterList().getParameters();
392 PsiParameter[] params2 = method2.getParameterList().getParameters();
393 if (argumentCount != params1.length && argumentCount == params2.length) return true;
395 if (params1.length != params2.length) return false;
397 for (int i = 0; i < params2.length; i++) {
398 PsiType type1 = substitutor1.substitute(params1[i].getType());
399 PsiType type2 = substitutor2.substitute(params2[i].getType());
400 if (!type1.equals(type2)) return false;