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.impl.types;
19 import com.intellij.lang.ASTNode;
20 import com.intellij.psi.*;
21 import com.intellij.psi.impl.source.resolve.ResolveCache;
22 import com.intellij.psi.search.GlobalSearchScope;
23 import com.intellij.psi.util.PsiTreeUtil;
24 import com.intellij.util.ArrayUtil;
25 import com.intellij.util.Consumer;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
28 import org.jetbrains.plugins.groovy.lang.completion.GroovyCompletionUtil;
29 import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocReferenceElement;
30 import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
31 import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
32 import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
33 import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
34 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
35 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
36 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
37 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression;
38 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition;
39 import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
40 import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDefinition;
41 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
42 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeArgumentList;
43 import org.jetbrains.plugins.groovy.lang.psi.impl.GrReferenceElementImpl;
44 import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
45 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
46 import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
47 import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassHint;
48 import org.jetbrains.plugins.groovy.lang.resolve.processors.ClassResolverProcessor;
49 import org.jetbrains.plugins.groovy.lang.resolve.processors.CompletionProcessor;
50 import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor;
52 import java.util.ArrayList;
53 import java.util.EnumSet;
54 import java.util.List;
56 import static org.jetbrains.plugins.groovy.lang.psi.impl.types.GrCodeReferenceElementImpl.ReferenceKind.*;
59 * @author: Dmitry.Krasilschikov
62 public class GrCodeReferenceElementImpl extends GrReferenceElementImpl implements GrCodeReferenceElement {
63 public GrCodeReferenceElementImpl(@NotNull ASTNode node) {
68 protected PsiElement bindWithQualifiedRef(String qName) {
69 final GrTypeArgumentList list = getTypeArgumentList();
70 final String typeArgs = (list != null) ? list.getText() : "";
71 final String text = qName + typeArgs;
72 final GrCodeReferenceElement qualifiedRef = GroovyPsiElementFactory.getInstance(getProject()).createTypeOrPackageReference(text);
73 getNode().getTreeParent().replaceChild(getNode(), qualifiedRef.getNode());
74 PsiUtil.shortenReference(qualifiedRef);
78 public void accept(GroovyElementVisitor visitor) {
79 visitor.visitCodeReferenceElement(this);
82 public String toString() {
83 return "Reference element";
86 public GrCodeReferenceElement getQualifier() {
87 return (GrCodeReferenceElement) findChildByType(GroovyElementTypes.REFERENCE_ELEMENT);
90 public void setQualifier(@Nullable GrCodeReferenceElement newQualifier) {
91 final GrCodeReferenceElement qualifier = getQualifier();
92 if (newQualifier == null) {
93 if (qualifier == null) return;
94 getNode().removeRange(getNode().getFirstChildNode(), getReferenceNameElement().getNode());
96 if (qualifier == null) {
97 final ASTNode refNameNode = getReferenceNameElement().getNode();
98 getNode().addChild(newQualifier.getNode(), refNameNode);
99 getNode().addLeaf(GroovyTokenTypes.mDOT, ".", refNameNode);
101 getNode().replaceChild(qualifier.getNode(), newQualifier.getNode());
113 CLASS_IN_QUALIFIED_NEW
117 public PsiElement resolve() {
118 ResolveResult[] results = getManager().getResolveCache().resolveWithCaching(this, RESOLVER, true, false);
119 return results.length == 1 ? results[0].getElement() : null;
122 private ReferenceKind getKind(boolean forCompletion) {
123 if (isClassReferenceForNew()) {
124 return CLASS_OR_PACKAGE;
127 PsiElement parent = getParent();
128 if (parent instanceof GrCodeReferenceElement) {
129 ReferenceKind parentKind = ((GrCodeReferenceElementImpl) parent).getKind(forCompletion);
130 if (parentKind == CLASS) return CLASS_OR_PACKAGE;
131 else if (parentKind == STATIC_MEMBER_FQ) return CLASS_FQ;
132 else if (parentKind == CLASS_FQ) return CLASS_OR_PACKAGE_FQ;
134 } else if (parent instanceof GrPackageDefinition) {
136 } else if (parent instanceof GrDocReferenceElement) {
137 return CLASS_OR_PACKAGE;
138 } else if (parent instanceof GrImportStatement) {
139 final GrImportStatement importStatement = (GrImportStatement) parent;
140 if (importStatement.isStatic()) {
141 return importStatement.isOnDemand() ? CLASS_FQ : STATIC_MEMBER_FQ;
144 return forCompletion || importStatement.isOnDemand() ? CLASS_OR_PACKAGE_FQ : CLASS_FQ;
147 else if (parent instanceof GrNewExpression || parent instanceof GrAnonymousClassDefinition) {
148 if (parent instanceof GrAnonymousClassDefinition) {
149 parent = parent.getParent();
151 assert parent instanceof GrNewExpression;
152 final GrNewExpression newExpression = (GrNewExpression)parent;
153 if (newExpression.getQualifier() != null) return CLASS_IN_QUALIFIED_NEW;
160 public String getCanonicalText() {
161 PsiElement resolved = resolve();
162 if (resolved instanceof PsiClass) {
163 return ((PsiClass) resolved).getQualifiedName();
165 if (resolved instanceof PsiPackage) {
166 return ((PsiPackage) resolved).getQualifiedName();
168 if (getKind(false) == STATIC_MEMBER_FQ) {
169 final GrCodeReferenceElement qualifier = getQualifier();
170 if (qualifier != null) {
171 final String qualifierText = qualifier.getCanonicalText();
172 if (qualifierText != null) {
173 return qualifierText + "." + getReferenceName();
181 protected boolean bindsCorrectly(PsiElement element) {
182 if (super.bindsCorrectly(element)) return true;
183 if (element instanceof PsiClass) {
184 final PsiElement resolved = resolve();
185 if (resolved instanceof PsiMethod) {
186 final PsiMethod method = (PsiMethod) resolved;
187 if (method.isConstructor() && getManager().areElementsEquivalent(element, method.getContainingClass())) {
196 public boolean isReferenceTo(PsiElement element) {
197 return getManager().areElementsEquivalent(element, resolve());
201 public Object[] getVariants() {
202 return ArrayUtil.EMPTY_OBJECT_ARRAY;
205 private boolean isClassReferenceForNew() {
206 PsiElement parent = getParent();
207 while (parent instanceof GrCodeReferenceElement) parent = parent.getParent();
208 return parent instanceof GrNewExpression;
211 private void processVariantsImpl(ReferenceKind kind, Consumer<Object> consumer) {
213 case STATIC_MEMBER_FQ: {
214 final GrCodeReferenceElement qualifier = getQualifier();
215 if (qualifier != null) {
216 final PsiElement resolve = qualifier.resolve();
217 if (resolve instanceof PsiClass) {
218 final PsiClass clazz = (PsiClass) resolve;
220 for (PsiField field : clazz.getFields()) {
221 if (field.hasModifierProperty(PsiModifier.STATIC)) {
222 consumer.consume(field);
226 for (PsiMethod method : clazz.getMethods()) {
227 if (method.hasModifierProperty(PsiModifier.STATIC)) {
228 consumer.consume(method);
239 case CLASS_OR_PACKAGE_FQ: {
240 final String refText = PsiUtil.getQualifiedReferenceText(this);
241 final int lastDot = refText.lastIndexOf(".");
242 String parentPackageFQName = lastDot > 0 ? refText.substring(0, lastDot) : "";
243 final PsiPackage parentPackage = JavaPsiFacade.getInstance(getProject()).findPackage(parentPackageFQName);
244 if (parentPackage != null) {
245 final GlobalSearchScope scope = getResolveScope();
246 if (kind == PACKAGE_FQ) {
247 for (PsiPackage aPackage : parentPackage.getSubPackages(scope)) {
248 consumer.consume(aPackage);
252 if (kind == CLASS_FQ) {
253 for (PsiClass aClass : parentPackage.getClasses(scope)) {
254 consumer.consume(aClass);
258 final PsiPackage[] subpackages = parentPackage.getSubPackages(scope);
259 final PsiClass[] classes = parentPackage.getClasses(scope);
260 for (PsiPackage aPackage : subpackages) {
261 consumer.consume(aPackage);
263 for (PsiClass aClass : classes) {
264 consumer.consume(aClass);
272 case CLASS_OR_PACKAGE:
273 case CLASS_IN_QUALIFIED_NEW:
275 GrCodeReferenceElement qualifier = getQualifier();
276 if (qualifier != null) {
277 PsiElement qualifierResolved = qualifier.resolve();
278 if (qualifierResolved instanceof PsiPackage) {
279 PsiPackage aPackage = (PsiPackage) qualifierResolved;
280 PsiClass[] classes = aPackage.getClasses(getResolveScope());
282 for (PsiClass aClass : classes) {
283 consumer.consume(aClass);
285 if (kind == CLASS) return;
287 PsiPackage[] subpackages = aPackage.getSubPackages();
288 for (PsiPackage subpackage : subpackages) {
289 consumer.consume(subpackage);
291 } else if (qualifierResolved instanceof PsiClass) {
292 for (PsiClass aClass : ((PsiClass)qualifierResolved).getInnerClasses()) {
293 consumer.consume(aClass);
297 ResolverProcessor classProcessor = CompletionProcessor.createClassCompletionProcessor(this);
298 ResolveUtil.treeWalkUp(this, classProcessor, false);
300 for (Object o : GroovyCompletionUtil.getCompletionVariants(classProcessor.getCandidates())) {
308 public boolean isSoft() {
312 private static class OurResolver implements ResolveCache.PolyVariantResolver<GrCodeReferenceElementImpl> {
314 public GroovyResolveResult[] resolve(GrCodeReferenceElementImpl reference, boolean incompleteCode) {
315 if (reference.getReferenceName() == null) return GroovyResolveResult.EMPTY_ARRAY;
316 final GroovyResolveResult[] results = _resolve(reference, reference.getManager(), reference.getKind(false));
317 final PsiType[] args = reference.getTypeArguments();
318 for (int i = 0; i < results.length; i++) {
319 GroovyResolveResult result = results[i];
320 final PsiElement element = result.getElement();
321 if (element instanceof PsiClass) {
322 final PsiSubstitutor substitutor = result.getSubstitutor();
323 final PsiSubstitutor newSubstitutor = substitutor.putAll((PsiClass) element, args);
324 results[i] = new GroovyResolveResultImpl(element, result.getCurrentFileResolveContext(), newSubstitutor, result.isAccessible(), result.isStaticsOK());
330 private static GroovyResolveResult[] _resolve(GrCodeReferenceElementImpl ref, PsiManager manager,
331 ReferenceKind kind) {
332 final String refName = ref.getReferenceName();
333 if (refName == null) {
334 return GroovyResolveResult.EMPTY_ARRAY;
338 case CLASS_OR_PACKAGE_FQ:
341 String qName = PsiUtil.getQualifiedReferenceText(ref);
343 JavaPsiFacade facade = JavaPsiFacade.getInstance(manager.getProject());
344 if (kind == CLASS_OR_PACKAGE_FQ || kind == CLASS_FQ) {
345 final PsiFile file = ref.getContainingFile();
346 if (qName.indexOf('.') > 0 || file instanceof GroovyFile && ((GroovyFile)file).getPackageName().length() == 0) {
347 PsiClass aClass = facade.findClass(qName, ref.getResolveScope());
348 if (aClass != null) {
349 boolean isAccessible = PsiUtil.isAccessible(ref, aClass);
350 return new GroovyResolveResult[]{new GroovyResolveResultImpl(aClass, isAccessible)};
355 if (kind == CLASS_OR_PACKAGE_FQ || kind == PACKAGE_FQ) {
356 PsiPackage aPackage = facade.findPackage(qName);
357 if (aPackage != null) {
358 return new GroovyResolveResult[]{new GroovyResolveResultImpl(aPackage, true)};
365 case CLASS_OR_PACKAGE: {
366 GrCodeReferenceElement qualifier = ref.getQualifier();
367 if (qualifier != null) {
368 PsiElement qualifierResolved = qualifier.resolve();
369 if (qualifierResolved instanceof PsiPackage) {
370 for (final PsiClass aClass : ((PsiPackage) qualifierResolved).getClasses(ref.getResolveScope())) {
371 if (refName.equals(aClass.getName())) {
372 boolean isAccessible = PsiUtil.isAccessible(ref, aClass);
373 return new GroovyResolveResult[]{new GroovyResolveResultImpl(aClass, isAccessible)};
377 if (kind == CLASS_OR_PACKAGE) {
378 for (final PsiPackage subpackage : ((PsiPackage) qualifierResolved).getSubPackages()) {
379 if (refName.equals(subpackage.getName()))
380 return new GroovyResolveResult[]{new GroovyResolveResultImpl(subpackage, true)};
383 } else if ((kind == CLASS || kind == CLASS_OR_PACKAGE) && qualifierResolved instanceof PsiClass) {
384 PsiClass[] classes = ((PsiClass) qualifierResolved).getAllInnerClasses();
385 for (final PsiClass aClass : classes) {
386 if (refName.equals(aClass.getName())) {
387 boolean isAccessible = PsiUtil.isAccessible(ref, aClass);
388 return new GroovyResolveResult[]{new GroovyResolveResultImpl(aClass, isAccessible)};
393 EnumSet<ClassHint.ResolveKind> kinds = kind == CLASS ? EnumSet.of(ClassHint.ResolveKind.CLASS) :
394 EnumSet.of(ClassHint.ResolveKind.PACKAGE, ClassHint.ResolveKind.CLASS);
395 ResolverProcessor processor = new ClassResolverProcessor(refName, ref, kinds);
396 ResolveUtil.treeWalkUp(ref, processor, false);
397 GroovyResolveResult[] candidates = processor.getCandidates();
398 if (candidates.length > 0) return candidates;
400 if (kind == CLASS_OR_PACKAGE) {
401 PsiPackage defaultPackage = JavaPsiFacade.getInstance(ref.getProject()).findPackage("");
402 if (defaultPackage != null) {
403 for (final PsiPackage subpackage : defaultPackage.getSubPackages()) {
404 if (refName.equals(subpackage.getName()))
405 return new GroovyResolveResult[]{new GroovyResolveResultImpl(subpackage, true)};
414 case STATIC_MEMBER_FQ: {
415 final GrCodeReferenceElement qualifier = ref.getQualifier();
416 if (qualifier != null) {
417 final PsiElement resolve = qualifier.resolve();
418 if (resolve instanceof PsiClass) {
419 final PsiClass clazz = (PsiClass) resolve;
420 PsiResolveHelper helper = JavaPsiFacade.getInstance(clazz.getProject()).getResolveHelper();
421 List<GroovyResolveResult> result = new ArrayList<GroovyResolveResult>();
422 final PsiField field = clazz.findFieldByName(refName, false);
423 if (field != null && field.hasModifierProperty(PsiModifier.STATIC)) {
424 result.add(new GroovyResolveResultImpl(field, helper.isAccessible(field, ref, null)));
427 final PsiMethod[] methods = clazz.findMethodsByName(refName, false);
428 for (PsiMethod method : methods) {
429 result.add(new GroovyResolveResultImpl(method, helper.isAccessible(method, ref, null)));
432 return result.toArray(new GroovyResolveResult[result.size()]);
437 case CLASS_IN_QUALIFIED_NEW: {
438 if (ref.getParent() instanceof GrCodeReferenceElement) return GroovyResolveResult.EMPTY_ARRAY;
439 final GrNewExpression newExpression = PsiTreeUtil.getParentOfType(ref, GrNewExpression.class);
440 assert newExpression != null;
441 final GrExpression qualifier = newExpression.getQualifier();
442 assert qualifier != null;
444 final PsiType type = qualifier.getType();
445 if (!(type instanceof PsiClassType)) break;
447 final PsiClassType classType = (PsiClassType)type;
448 final PsiClass psiClass = classType.resolve();
449 if (psiClass == null) break;
451 final PsiClass[] allInnerClasses = psiClass.getAllInnerClasses();
452 ArrayList<GroovyResolveResult> result = new ArrayList<GroovyResolveResult>();
453 PsiResolveHelper helper = JavaPsiFacade.getInstance(ref.getProject()).getResolveHelper();
455 for (final PsiClass innerClass : allInnerClasses) {
456 if (refName.equals(innerClass.getName())) {
457 result.add(new GroovyResolveResultImpl(innerClass, helper.isAccessible(innerClass, ref, null)));
460 return result.toArray(new GroovyResolveResult[result.size()]);
464 return GroovyResolveResult.EMPTY_ARRAY;
468 private static final OurResolver RESOLVER = new OurResolver();
470 public GroovyResolveResult advancedResolve() {
471 ResolveResult[] results = getManager().getResolveCache().resolveWithCaching(this, RESOLVER, true, false);
472 return results.length == 1 ? (GroovyResolveResult) results[0] : GroovyResolveResult.EMPTY_RESULT;
476 public GroovyResolveResult[] multiResolve(boolean incompleteCode) {
477 final ResolveResult[] results = getManager().getResolveCache().resolveWithCaching(this, RESOLVER, true, incompleteCode);
478 if (results.length == 0) {
479 return GroovyResolveResult.EMPTY_ARRAY;
482 return (GroovyResolveResult[])results;
485 public void processVariants(Consumer<Object> consumer) {
486 processVariantsImpl(getKind(true), consumer);