update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / psi / impl / source / PsiJavaCodeReferenceElementImpl.java
blob20c2672f0e35fbc6c0990ff19b736916683b2fb8
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.
16 package com.intellij.psi.impl.source;
18 import com.intellij.lang.ASTNode;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.util.TextRange;
21 import com.intellij.psi.*;
22 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
23 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
24 import com.intellij.psi.filters.*;
25 import com.intellij.psi.filters.classes.AnnotationTypeFilter;
26 import com.intellij.psi.filters.element.ModifierFilter;
27 import com.intellij.psi.impl.CheckUtil;
28 import com.intellij.psi.impl.PsiImplUtil;
29 import com.intellij.psi.impl.PsiManagerEx;
30 import com.intellij.psi.impl.source.parsing.Parsing;
31 import com.intellij.psi.impl.source.resolve.ClassResolverProcessor;
32 import com.intellij.psi.impl.source.resolve.ResolveCache;
33 import com.intellij.psi.impl.source.resolve.VariableResolverProcessor;
34 import com.intellij.psi.impl.source.tree.*;
35 import com.intellij.psi.infos.CandidateInfo;
36 import com.intellij.psi.scope.ElementClassFilter;
37 import com.intellij.psi.scope.PsiScopeProcessor;
38 import com.intellij.psi.scope.processor.FilterScopeProcessor;
39 import com.intellij.psi.scope.util.PsiScopesUtil;
40 import com.intellij.psi.tree.ChildRoleBase;
41 import com.intellij.psi.tree.IElementType;
42 import com.intellij.psi.util.PsiUtil;
43 import com.intellij.util.IncorrectOperationException;
44 import org.jetbrains.annotations.NotNull;
45 import org.jetbrains.annotations.Nullable;
47 public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement implements PsiJavaCodeReferenceElement, SourceJavaCodeReference {
48 private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.PsiJavaCodeReferenceElementImpl");
50 private volatile String myCachedQName = null;
51 private volatile String myCachedTextSkipWhiteSpaceAndComments;
52 private int myKindWhenDummy = CLASS_NAME_KIND;
54 public static final int CLASS_NAME_KIND = 1;
55 public static final int PACKAGE_NAME_KIND = 2;
56 public static final int CLASS_OR_PACKAGE_NAME_KIND = 3;
57 public static final int CLASS_FQ_NAME_KIND = 4;
58 public static final int CLASS_FQ_OR_PACKAGE_NAME_KIND = 5;
59 public static final int CLASS_IN_QUALIFIED_NEW_KIND = 6;
61 public PsiJavaCodeReferenceElementImpl() {
62 super(JavaElementType.JAVA_CODE_REFERENCE);
65 public int getTextOffset() {
66 final ASTNode refName = getReferenceNameNode();
67 return refName != null ? refName.getStartOffset() : super.getTextOffset();
70 public void setKindWhenDummy(final int kind) {
71 LOG.assertTrue(getTreeParent().getElementType() == TokenType.DUMMY_HOLDER);
72 myKindWhenDummy = kind;
75 public int getKind() {
76 IElementType i = getTreeParent().getElementType();
77 if (i == TokenType.DUMMY_HOLDER) {
78 return myKindWhenDummy;
80 else if (i == JavaElementType.TYPE) {
81 return getTreeParent().getTreeParent().getPsi() instanceof PsiTypeCodeFragment ? CLASS_OR_PACKAGE_NAME_KIND : CLASS_NAME_KIND;
83 else if (i == JavaElementType.EXTENDS_LIST || i == JavaElementType.IMPLEMENTS_LIST || i == JavaElementType.EXTENDS_BOUND_LIST || i ==
84 JavaElementType.THROWS_LIST ||
85 i == JavaElementType.THIS_EXPRESSION ||
86 i == JavaElementType.SUPER_EXPRESSION ||
87 i == JavaDocElementType.DOC_METHOD_OR_FIELD_REF ||
88 i == JavaDocTokenType.DOC_TAG_VALUE_TOKEN ||
89 i == JavaElementType.REFERENCE_PARAMETER_LIST ||
90 i == JavaElementType.ANNOTATION) {
91 if (isQualified()) {
92 return CLASS_OR_PACKAGE_NAME_KIND;
95 return CLASS_NAME_KIND;
97 else if (i == JavaElementType.NEW_EXPRESSION) {
98 final ASTNode qualifier = getTreeParent().findChildByRole(ChildRole.QUALIFIER);
99 return qualifier != null ? CLASS_IN_QUALIFIED_NEW_KIND : CLASS_NAME_KIND;
101 else if (i == JavaElementType.ANONYMOUS_CLASS) {
102 if (getTreeParent().getChildRole(this) == ChildRole.BASE_CLASS_REFERENCE) {
103 LOG.assertTrue(getTreeParent().getTreeParent().getElementType() == JavaElementType.NEW_EXPRESSION);
104 final ASTNode qualifier = getTreeParent().getTreeParent().findChildByRole(ChildRole.QUALIFIER);
105 return qualifier != null ? CLASS_IN_QUALIFIED_NEW_KIND : CLASS_NAME_KIND;
107 else {
108 return CLASS_OR_PACKAGE_NAME_KIND; // uncomplete code
111 else if (i == JavaElementType.PACKAGE_STATEMENT) {
112 return PACKAGE_NAME_KIND;
114 else if (i == JavaElementType.IMPORT_STATEMENT) {
115 final boolean isOnDemand = ((PsiImportStatement)SourceTreeToPsiMap.treeElementToPsi(getTreeParent())).isOnDemand();
116 return isOnDemand ? CLASS_FQ_OR_PACKAGE_NAME_KIND : CLASS_FQ_NAME_KIND;
118 else if (i == JavaElementType.IMPORT_STATIC_STATEMENT) {
119 return CLASS_FQ_OR_PACKAGE_NAME_KIND;
121 else if (i == JavaElementType.JAVA_CODE_REFERENCE) {
122 final int parentKind = ((PsiJavaCodeReferenceElementImpl)getTreeParent()).getKind();
123 switch (parentKind) {
124 case CLASS_NAME_KIND:
125 return CLASS_OR_PACKAGE_NAME_KIND;
127 case PACKAGE_NAME_KIND:
128 return PACKAGE_NAME_KIND;
130 case CLASS_OR_PACKAGE_NAME_KIND:
131 return CLASS_OR_PACKAGE_NAME_KIND;
133 case CLASS_FQ_NAME_KIND:
134 return CLASS_FQ_OR_PACKAGE_NAME_KIND;
136 case CLASS_FQ_OR_PACKAGE_NAME_KIND:
137 return CLASS_FQ_OR_PACKAGE_NAME_KIND;
139 case CLASS_IN_QUALIFIED_NEW_KIND:
140 return CLASS_IN_QUALIFIED_NEW_KIND; //??
142 default:
143 LOG.assertTrue(false);
144 return -1;
147 else if (i == JavaElementType.CLASS || i == JavaElementType.PARAMETER_LIST || i == TokenType.ERROR_ELEMENT) {
148 return CLASS_OR_PACKAGE_NAME_KIND;
150 else if (i == JavaElementType.IMPORT_STATIC_REFERENCE) {
151 return CLASS_FQ_OR_PACKAGE_NAME_KIND;
153 else if (i == JavaDocElementType.DOC_TAG || i == JavaDocElementType.DOC_INLINE_TAG || i == JavaDocElementType.DOC_REFERENCE_HOLDER || i ==
154 JavaDocElementType.DOC_TYPE_HOLDER) {
155 return CLASS_OR_PACKAGE_NAME_KIND;
157 else if (isCodeFragmentType(i)) {
158 PsiJavaCodeReferenceCodeFragment fragment = (PsiJavaCodeReferenceCodeFragment)getTreeParent().getPsi();
159 return fragment.isClassesAccepted() ? CLASS_FQ_OR_PACKAGE_NAME_KIND : PACKAGE_NAME_KIND;
161 else {
162 LOG.error("Unknown parent for java code reference:" + getTreeParent());
163 return CLASS_NAME_KIND;
167 private static boolean isCodeFragmentType(IElementType type) {
168 return type == TokenType.CODE_FRAGMENT || type instanceof ICodeFragmentElementType;
171 public void deleteChildInternal(@NotNull final ASTNode child) {
172 if (getChildRole(child) == ChildRole.QUALIFIER) {
173 final ASTNode dot = findChildByRole(ChildRole.DOT);
174 super.deleteChildInternal(child);
175 deleteChildInternal(dot);
177 else {
178 super.deleteChildInternal(child);
182 public final ASTNode findChildByRole(final int role) {
183 LOG.assertTrue(ChildRole.isUnique(role));
184 switch (role) {
185 default:
186 return null;
188 case ChildRole.REFERENCE_NAME:
189 if (getLastChildNode().getElementType() == JavaTokenType.IDENTIFIER) {
190 return getLastChildNode();
192 else {
193 if (getLastChildNode().getElementType() == JavaElementType.REFERENCE_PARAMETER_LIST) {
194 ASTNode current = getLastChildNode().getTreePrev();
195 while (current != null && StdTokenSets.WHITE_SPACE_OR_COMMENT_BIT_SET.contains(current.getElementType())) {
196 current = current.getTreePrev();
198 if (current != null && current.getElementType() == JavaTokenType.IDENTIFIER) {
199 return current;
202 return null;
205 case ChildRole.REFERENCE_PARAMETER_LIST:
206 if (getLastChildNode().getElementType() == JavaElementType.REFERENCE_PARAMETER_LIST) {
207 return getLastChildNode();
209 else {
210 return null;
213 case ChildRole.QUALIFIER:
214 if (getFirstChildNode().getElementType() == JavaElementType.JAVA_CODE_REFERENCE) {
215 return getFirstChildNode();
217 else {
218 return null;
221 case ChildRole.DOT:
222 return findChildByType(JavaTokenType.DOT);
226 public final int getChildRole(final ASTNode child) {
227 LOG.assertTrue(child.getTreeParent() == this);
228 final IElementType i = child.getElementType();
229 if (i == JavaElementType.REFERENCE_PARAMETER_LIST) {
230 return ChildRole.REFERENCE_PARAMETER_LIST;
232 else if (i == JavaElementType.JAVA_CODE_REFERENCE) {
233 return ChildRole.QUALIFIER;
235 else if (i == JavaTokenType.DOT) {
236 return ChildRole.DOT;
238 else if (i == JavaTokenType.IDENTIFIER) {
239 return ChildRole.REFERENCE_NAME;
241 else {
242 return ChildRoleBase.NONE;
246 public String getCanonicalText() {
247 switch (getKind()) {
248 case CLASS_NAME_KIND:
249 case CLASS_OR_PACKAGE_NAME_KIND:
250 case CLASS_IN_QUALIFIED_NEW_KIND:
251 final PsiElement target = resolve();
252 if (target instanceof PsiClass) {
253 final PsiClass aClass = (PsiClass)target;
254 String name = aClass.getQualifiedName();
255 if (name == null) {
256 name = aClass.getName(); //?
258 final PsiType[] types = getTypeParameters();
259 if (types.length == 0) return name;
261 final StringBuilder buf = new StringBuilder();
262 buf.append(name);
263 buf.append('<');
264 for (int i = 0; i < types.length; i++) {
265 if (i > 0) buf.append(',');
266 buf.append(types[i].getCanonicalText());
268 buf.append('>');
270 return buf.toString();
272 else if (target instanceof PsiPackage) {
273 return ((PsiPackage)target).getQualifiedName();
275 else {
276 LOG.assertTrue(target == null);
277 return getTextSkipWhiteSpaceAndComments();
279 case PACKAGE_NAME_KIND:
280 case CLASS_FQ_NAME_KIND:
281 case CLASS_FQ_OR_PACKAGE_NAME_KIND:
282 return getTextSkipWhiteSpaceAndComments();
284 default:
285 LOG.assertTrue(false);
286 return null;
290 public PsiReference getReference() {
291 return this;
294 public final PsiElement resolve() {
295 return advancedResolve(false).getElement();
298 private static final class OurGenericsResolver implements ResolveCache.PolyVariantResolver<PsiJavaReference> {
299 private static final OurGenericsResolver INSTANCE = new OurGenericsResolver();
301 public static JavaResolveResult[] _resolve(final PsiJavaReference ref, final boolean incompleteCode) {
302 final PsiJavaCodeReferenceElementImpl referenceElement = (PsiJavaCodeReferenceElementImpl)ref;
303 final int kind = referenceElement.getKind();
304 JavaResolveResult[] result = referenceElement.resolve(kind);
305 if (incompleteCode && result.length == 0 && kind != CLASS_FQ_NAME_KIND && kind != CLASS_FQ_OR_PACKAGE_NAME_KIND) {
306 final VariableResolverProcessor processor = new VariableResolverProcessor(referenceElement);
307 PsiScopesUtil.resolveAndWalk(processor, referenceElement, null, incompleteCode);
308 result = processor.getResult();
309 if (result.length > 0) {
310 return result;
312 if (kind == CLASS_NAME_KIND) {
313 return referenceElement.resolve(PACKAGE_NAME_KIND);
316 return result;
319 public JavaResolveResult[] resolve(final PsiJavaReference ref, final boolean incompleteCode) {
320 final JavaResolveResult[] result = _resolve(ref, incompleteCode);
321 if (result.length > 0 && result[0].getElement() instanceof PsiClass) {
322 final PsiType[] parameters = ((PsiJavaCodeReferenceElement)ref).getTypeParameters();
323 final JavaResolveResult[] newResult = new JavaResolveResult[result.length];
324 for (int i = 0; i < result.length; i++) {
325 final CandidateInfo resolveResult = (CandidateInfo)result[i];
326 final PsiClass aClass = (PsiClass)resolveResult.getElement();
327 assert aClass != null;
328 newResult[i] = !aClass.hasTypeParameters() ? resolveResult : new CandidateInfo(resolveResult, resolveResult.getSubstitutor().putAll(aClass, parameters));
330 return newResult;
332 return result;
336 @NotNull
337 public JavaResolveResult advancedResolve(final boolean incompleteCode) {
338 final JavaResolveResult[] results = multiResolve(incompleteCode);
339 if (results.length == 1) return results[0];
340 return JavaResolveResult.EMPTY;
343 @NotNull
344 public JavaResolveResult[] multiResolve(final boolean incompleteCode) {
345 final PsiManagerEx manager = getManager();
346 if (manager == null) {
347 LOG.assertTrue(false, "getManager() == null!");
348 return JavaResolveResult.EMPTY_ARRAY;
351 final ResolveCache resolveCache = manager.getResolveCache();
352 final ResolveResult[] results = resolveCache.resolveWithCaching(this, OurGenericsResolver.INSTANCE, true, incompleteCode);
353 return results.length == 0 ? JavaResolveResult.EMPTY_ARRAY : (JavaResolveResult[])results;
356 private PsiSubstitutor updateSubstitutor(PsiSubstitutor subst, final PsiClass psiClass) {
357 final PsiType[] parameters = getTypeParameters();
358 if (psiClass != null) {
359 subst = subst.putAll(psiClass, parameters);
361 return subst;
364 private JavaResolveResult[] resolve(final int kind) {
365 switch (kind) {
366 case CLASS_FQ_NAME_KIND:
367 // TODO: support type parameters in FQ names
368 final String textSkipWhiteSpaceAndComments = getTextSkipWhiteSpaceAndComments();
369 if (textSkipWhiteSpaceAndComments == null || textSkipWhiteSpaceAndComments.length() == 0) return JavaResolveResult.EMPTY_ARRAY;
370 final PsiClass aClass =
371 JavaPsiFacade.getInstance(getProject()).findClass(textSkipWhiteSpaceAndComments, getResolveScope());
372 if (aClass == null) return JavaResolveResult.EMPTY_ARRAY;
373 return new JavaResolveResult[]{new CandidateInfo(aClass, updateSubstitutor(PsiSubstitutor.EMPTY, aClass), this, false)};
375 case CLASS_IN_QUALIFIED_NEW_KIND: {
376 PsiElement parent = getParent();
377 if (parent instanceof JavaDummyHolder) {
378 parent = parent.getContext();
381 if (parent instanceof PsiAnonymousClass) {
382 parent = parent.getParent();
384 final PsiExpression qualifier;
385 if (parent instanceof PsiNewExpression) {
386 qualifier = ((PsiNewExpression)parent).getQualifier();
387 LOG.assertTrue(qualifier != null);
389 else if (parent instanceof PsiJavaCodeReferenceElement) {
390 return JavaResolveResult.EMPTY_ARRAY;
392 else {
393 LOG.assertTrue(false, "Invalid java reference!");
394 return JavaResolveResult.EMPTY_ARRAY;
397 final PsiType qualifierType = qualifier.getType();
398 if (qualifierType == null) return JavaResolveResult.EMPTY_ARRAY;
399 if (!(qualifierType instanceof PsiClassType)) return JavaResolveResult.EMPTY_ARRAY;
400 final JavaResolveResult result = PsiUtil.resolveGenericsClassInType(qualifierType);
401 if (result.getElement() == null) return JavaResolveResult.EMPTY_ARRAY;
402 final PsiElement classNameElement = getReferenceNameElement();
403 if (!(classNameElement instanceof PsiIdentifier)) return JavaResolveResult.EMPTY_ARRAY;
404 final String className = classNameElement.getText();
406 final ClassResolverProcessor processor = new ClassResolverProcessor(className, this);
407 result.getElement().processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, result.getSubstitutor()), this, this);
408 return processor.getResult();
410 case CLASS_NAME_KIND: {
411 final PsiElement classNameElement = getReferenceNameElement();
412 if (!(classNameElement instanceof PsiIdentifier)) return JavaResolveResult.EMPTY_ARRAY;
413 final String className = classNameElement.getText();
415 final ClassResolverProcessor processor = new ClassResolverProcessor(className, this);
416 PsiScopesUtil.resolveAndWalk(processor, this, null);
418 return processor.getResult();
421 case PACKAGE_NAME_KIND:
422 final String packageName = getTextSkipWhiteSpaceAndComments();
423 final PsiManager manager = getManager();
424 final PsiPackage aPackage = JavaPsiFacade.getInstance(manager.getProject()).findPackage(packageName);
425 if (aPackage == null || !aPackage.isValid()) {
426 return JavaPsiFacade.getInstance(manager.getProject()).isPartOfPackagePrefix(packageName)
427 ? CandidateInfo.RESOLVE_RESULT_FOR_PACKAGE_PREFIX_PACKAGE
428 : JavaResolveResult.EMPTY_ARRAY;
430 return new JavaResolveResult[]{new CandidateInfo(aPackage, PsiSubstitutor.EMPTY)};
432 case CLASS_FQ_OR_PACKAGE_NAME_KIND: {
433 final JavaResolveResult[] result = resolve(CLASS_FQ_NAME_KIND);
434 if (result.length == 0) {
435 return resolve(PACKAGE_NAME_KIND);
437 return result;
439 case CLASS_OR_PACKAGE_NAME_KIND:
440 final JavaResolveResult[] classResolveResult = resolve(CLASS_NAME_KIND);
441 // [dsl]todo[ik]: review this change I guess ResolveInfo should be merged if both
442 // class and package resolve failed.
443 if (classResolveResult.length == 0) {
444 final JavaResolveResult[] packageResolveResult = resolve(PACKAGE_NAME_KIND);
445 if (packageResolveResult.length > 0) return packageResolveResult;
447 return classResolveResult;
448 default:
449 LOG.assertTrue(false);
451 return JavaResolveResult.EMPTY_ARRAY;
454 public final PsiElement handleElementRename(final String newElementName) throws IncorrectOperationException {
455 final PsiElement oldIdentifier = getReferenceNameElement();
456 if (oldIdentifier == null) {
457 throw new IncorrectOperationException();
459 final PsiElement identifier = JavaPsiFacade.getInstance(getProject()).getElementFactory().createIdentifier(newElementName);
460 oldIdentifier.replace(identifier);
461 return this;
465 public PsiElement bindToElement(@NotNull final PsiElement element) throws IncorrectOperationException {
466 CheckUtil.checkWritable(this);
467 if (isReferenceTo(element)) return this;
469 switch (getKind()) {
470 case CLASS_NAME_KIND:
471 case CLASS_FQ_NAME_KIND:
472 if (!(element instanceof PsiClass)) {
473 throw cannotBindError(element);
475 return bindToClass((PsiClass)element);
477 case PACKAGE_NAME_KIND:
478 if (!(element instanceof PsiPackage)) {
479 throw cannotBindError(element);
481 return bindToPackage((PsiPackage)element);
483 case CLASS_OR_PACKAGE_NAME_KIND:
484 case CLASS_FQ_OR_PACKAGE_NAME_KIND:
485 if (element instanceof PsiClass) {
486 return bindToClass((PsiClass)element);
488 else if (element instanceof PsiPackage) {
489 return bindToPackage((PsiPackage)element);
491 else {
492 throw cannotBindError(element);
494 case CLASS_IN_QUALIFIED_NEW_KIND:
495 if (element instanceof PsiClass) {
496 final PsiClass aClass = (PsiClass)element;
497 final String name = aClass.getName();
498 if (name == null) {
499 throw new IncorrectOperationException(aClass.toString());
501 final TreeElement ref =
502 Parsing.parseJavaCodeReferenceText(aClass.getManager(), name, SharedImplUtil.findCharTableByTree(this));
503 getTreeParent().replaceChildInternal(this, ref);
504 return SourceTreeToPsiMap.treeElementToPsi(ref);
506 else {
507 throw cannotBindError(element);
510 default:
511 LOG.assertTrue(false);
512 return null;
516 private static IncorrectOperationException cannotBindError(final PsiElement element) {
517 return new IncorrectOperationException("Cannot bind to "+element);
520 private PsiElement bindToClass(final PsiClass aClass) throws IncorrectOperationException {
521 String qName = aClass.getQualifiedName();
522 final JavaPsiFacade facade = JavaPsiFacade.getInstance(getProject());
523 if (qName == null) {
524 qName = aClass.getName();
525 final PsiClass psiClass = facade.getResolveHelper().resolveReferencedClass(qName, this);
526 if (!getManager().areElementsEquivalent(psiClass, aClass)) {
527 throw cannotBindError(aClass);
530 else {
531 if (facade.findClass(qName, getResolveScope()) == null) {
532 return this;
536 final boolean preserveQualification = CodeStyleSettingsManager.getSettings(getProject()).USE_FQ_CLASS_NAMES && isFullyQualified();
537 final PsiManager manager = aClass.getManager();
538 String text = qName + getParameterList().getText();
539 ASTNode ref = Parsing.parseJavaCodeReferenceText(manager, text, SharedImplUtil.findCharTableByTree(this));
540 LOG.assertTrue(ref != null, "Failed to parse reference from text '" + text + "'");
541 getTreeParent().replaceChildInternal(this, (TreeElement)ref);
542 if (!preserveQualification /*&& (TreeUtil.findParent(ref, ElementType.DOC_COMMENT) == null)*/) {
543 final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(aClass.getProject());
544 ref = SourceTreeToPsiMap.psiElementToTree(
545 codeStyleManager.shortenClassReferences(SourceTreeToPsiMap.treeElementToPsi(ref), JavaCodeStyleManager.UNCOMPLETE_CODE)
548 return SourceTreeToPsiMap.treeElementToPsi(ref);
551 private boolean isFullyQualified() {
552 switch (getKind()) {
553 case CLASS_OR_PACKAGE_NAME_KIND:
554 if (resolve() instanceof PsiPackage) return true;
555 case CLASS_NAME_KIND:
556 break;
558 case PACKAGE_NAME_KIND:
559 case CLASS_FQ_NAME_KIND:
560 case CLASS_FQ_OR_PACKAGE_NAME_KIND:
561 return true;
563 default:
564 LOG.assertTrue(false);
565 return true;
568 final ASTNode qualifier = findChildByRole(ChildRole.QUALIFIER);
569 if (qualifier == null) return false;
571 LOG.assertTrue(qualifier.getElementType() == JavaElementType.JAVA_CODE_REFERENCE);
572 final PsiElement refElement = ((PsiJavaCodeReferenceElement)SourceTreeToPsiMap.treeElementToPsi(qualifier)).resolve();
573 if (refElement instanceof PsiPackage) return true;
575 return ((PsiJavaCodeReferenceElementImpl)SourceTreeToPsiMap.treeElementToPsi(qualifier)).isFullyQualified();
578 private PsiElement bindToPackage(final PsiPackage aPackage) throws IncorrectOperationException {
579 final String qName = aPackage.getQualifiedName();
580 if (qName.length() == 0) {
581 throw new IncorrectOperationException("Cannot bind to default package: "+aPackage);
583 final TreeElement ref = Parsing.parseJavaCodeReferenceText(getManager(), qName, SharedImplUtil.findCharTableByTree(this));
584 getTreeParent().replaceChildInternal(this, ref);
585 return SourceTreeToPsiMap.treeElementToPsi(ref);
588 public boolean isReferenceTo(final PsiElement element) {
589 switch (getKind()) {
590 case CLASS_NAME_KIND:
591 case CLASS_IN_QUALIFIED_NEW_KIND:
592 if (!(element instanceof PsiClass)) return false;
593 break;
595 case CLASS_FQ_NAME_KIND: {
596 if (!(element instanceof PsiClass)) return false;
597 final String qName = ((PsiClass)element).getQualifiedName();
598 return qName != null && qName.equals(getCanonicalText());
601 case PACKAGE_NAME_KIND: {
602 if (!(element instanceof PsiPackage)) return false;
603 final String qName = ((PsiPackage)element).getQualifiedName();
604 return qName.equals(getCanonicalText());
607 case CLASS_OR_PACKAGE_NAME_KIND:
608 // if (lastChild.type != IDENTIFIER) return false;
609 if (element instanceof PsiPackage) {
610 final String qName = ((PsiPackage)element).getQualifiedName();
611 return qName.equals(getCanonicalText());
613 else if (element instanceof PsiClass) {
614 final PsiIdentifier nameIdentifier = ((PsiClass)element).getNameIdentifier();
615 if (nameIdentifier == null) return false;
616 PsiElement nameElement = getReferenceNameElement();
617 return nameElement != null && nameElement.textMatches(nameIdentifier) &&
618 element.getManager().areElementsEquivalent(resolve(), element);
620 else {
621 return false;
624 case CLASS_FQ_OR_PACKAGE_NAME_KIND:
625 if (element instanceof PsiClass) {
626 final String qName = ((PsiClass)element).getQualifiedName();
627 return qName != null && qName.equals(getCanonicalText());
629 else if (element instanceof PsiPackage) {
630 final String qName = ((PsiPackage)element).getQualifiedName();
631 return qName.equals(getCanonicalText());
633 else {
634 return false;
636 default:
637 LOG.assertTrue(false);
638 return true;
641 final ASTNode referenceNameElement = getReferenceNameNode();
642 if (referenceNameElement == null || referenceNameElement.getElementType() != JavaTokenType.IDENTIFIER) return false;
643 final String name = ((PsiClass)element).getName();
644 return name != null && referenceNameElement.getText().equals(name) && element.getManager().areElementsEquivalent(resolve(), element);
647 private String getTextSkipWhiteSpaceAndComments() {
648 String whiteSpaceAndComments = myCachedTextSkipWhiteSpaceAndComments;
649 if (whiteSpaceAndComments == null) {
650 myCachedTextSkipWhiteSpaceAndComments = whiteSpaceAndComments = SourceUtil.getTextSkipWhiteSpaceAndComments(this);
652 return whiteSpaceAndComments;
655 public String getClassNameText() {
656 String cachedQName = myCachedQName;
657 if (cachedQName == null) {
658 myCachedQName = cachedQName = PsiNameHelper.getQualifiedClassName(getTextSkipWhiteSpaceAndComments(), false);
660 return cachedQName;
663 public void fullyQualify(final PsiClass targetClass) {
664 final int kind = getKind();
665 if (kind != CLASS_NAME_KIND && kind != CLASS_OR_PACKAGE_NAME_KIND && kind != CLASS_IN_QUALIFIED_NEW_KIND) {
666 LOG.error("Wrong kind " + kind);
667 return;
669 JavaSourceUtil.fullyQualifyReference(this, targetClass);
672 public boolean isQualified() {
673 return getChildRole(getFirstChildNode()) != ChildRole.REFERENCE_NAME;
676 public PsiElement getQualifier() {
677 return SourceTreeToPsiMap.treeElementToPsi(findChildByRole(ChildRole.QUALIFIER));
680 public void clearCaches() {
681 super.clearCaches();
682 myCachedQName = null;
683 myCachedTextSkipWhiteSpaceAndComments = null;
686 public Object[] getVariants() {
687 final ElementFilter filter;
688 switch (getKind()) {
690 case CLASS_OR_PACKAGE_NAME_KIND:
691 filter = new OrFilter();
692 ((OrFilter)filter).addFilter(ElementClassFilter.CLASS);
693 ((OrFilter)filter).addFilter(ElementClassFilter.PACKAGE_FILTER);
694 break;
695 case CLASS_NAME_KIND:
696 filter = ElementClassFilter.CLASS;
697 break;
698 case PACKAGE_NAME_KIND:
699 filter = ElementClassFilter.PACKAGE_FILTER;
700 break;
701 case CLASS_FQ_NAME_KIND:
702 case CLASS_FQ_OR_PACKAGE_NAME_KIND:
703 filter = new OrFilter();
704 ((OrFilter)filter).addFilter(ElementClassFilter.PACKAGE_FILTER);
705 if (isQualified()) {
706 ((OrFilter)filter).addFilter(ElementClassFilter.CLASS);
708 break;
709 case CLASS_IN_QUALIFIED_NEW_KIND:
710 filter = ElementClassFilter.CLASS;
711 break;
712 default:
713 throw new RuntimeException("Unknown reference type");
716 return PsiImplUtil.getReferenceVariantsByFilter(this, filter);
719 public boolean isSoft() {
720 return false;
723 public void processVariants(final PsiScopeProcessor processor) {
724 final OrFilter filter = new OrFilter();
725 PsiElement superParent = getParent();
726 boolean smartCompletion = true;
727 if (isQualified()) {
728 smartCompletion = false;
730 else {
731 while (superParent != null) {
732 if (superParent instanceof PsiCodeBlock || superParent instanceof PsiVariable) {
733 smartCompletion = false;
734 break;
736 superParent = superParent.getParent();
739 if (!smartCompletion && !isCodeFragmentType(getTreeParent().getElementType()) && !(getParent() instanceof PsiAnnotation)) {
740 /*filter.addFilter(ElementClassFilter.CLASS);
741 filter.addFilter(ElementClassFilter.PACKAGE);*/
742 filter.addFilter(new AndFilter(ElementClassFilter.METHOD, new NotFilter(new ConstructorFilter())));
743 filter.addFilter(ElementClassFilter.VARIABLE);
745 switch (getKind()) {
746 case CLASS_OR_PACKAGE_NAME_KIND:
747 addClassFilter(filter);
748 filter.addFilter(ElementClassFilter.PACKAGE_FILTER);
749 break;
750 case CLASS_NAME_KIND:
751 addClassFilter(filter);
752 break;
753 case PACKAGE_NAME_KIND:
754 filter.addFilter(ElementClassFilter.PACKAGE_FILTER);
755 break;
756 case CLASS_FQ_NAME_KIND:
757 case CLASS_FQ_OR_PACKAGE_NAME_KIND:
758 filter.addFilter(ElementClassFilter.PACKAGE_FILTER);
759 if (isQualified()) {
760 filter.addFilter(ElementClassFilter.CLASS);
762 break;
763 case CLASS_IN_QUALIFIED_NEW_KIND:
764 final PsiElement parent = getParent();
765 if (parent instanceof PsiNewExpression) {
766 final PsiNewExpression newExpr = (PsiNewExpression)parent;
767 final PsiType type = newExpr.getQualifier().getType();
768 final PsiClass aClass = PsiUtil.resolveClassInType(type);
769 if (aClass != null) {
770 aClass.processDeclarations(new FilterScopeProcessor(new AndFilter(ElementClassFilter.CLASS, new ModifierFilter(PsiModifier.STATIC, false)),
771 processor), ResolveState.initial(), null, this);
773 // else{
774 // throw new RuntimeException("Qualified new is not allowed for primitives");
775 // }
777 // else{
778 // throw new RuntimeException("Reference type is qualified new, but parent expression is: " + getParent());
779 // }
781 return;
782 default:
783 throw new RuntimeException("Unknown reference type");
785 final FilterScopeProcessor proc = new FilterScopeProcessor(filter, processor);
786 PsiScopesUtil.resolveAndWalk(proc, this, null, true);
789 private void addClassFilter(final OrFilter filter) {
790 if (getParent() instanceof PsiAnnotation) {
791 filter.addFilter(new AnnotationTypeFilter());
793 else {
794 filter.addFilter(ElementClassFilter.CLASS);
798 public PsiElement getReferenceNameElement() {
799 return SourceTreeToPsiMap.treeElementToPsi(getReferenceNameNode());
802 @Nullable
803 private ASTNode getReferenceNameNode() {
804 return findChildByRole(ChildRole.REFERENCE_NAME);
808 public PsiReferenceParameterList getParameterList() {
809 return (PsiReferenceParameterList)findChildByRoleAsPsiElement(ChildRole.REFERENCE_PARAMETER_LIST);
812 public String getQualifiedName() {
813 switch (getKind()) {
814 case CLASS_NAME_KIND:
815 case CLASS_OR_PACKAGE_NAME_KIND:
816 case CLASS_IN_QUALIFIED_NEW_KIND:
817 final PsiElement target = resolve();
818 if (target instanceof PsiClass) {
819 final PsiClass aClass = (PsiClass)target;
820 String name = aClass.getQualifiedName();
821 if (name == null) {
822 name = aClass.getName(); //?
824 return name;
826 else if (target instanceof PsiPackage) {
827 return ((PsiPackage)target).getQualifiedName();
829 else {
830 LOG.assertTrue(target == null);
831 return getClassNameText();
834 case PACKAGE_NAME_KIND:
835 case CLASS_FQ_NAME_KIND:
836 case CLASS_FQ_OR_PACKAGE_NAME_KIND:
837 return getTextSkipWhiteSpaceAndComments(); // there cannot be any <...>
839 default:
840 LOG.assertTrue(false);
841 return null;
846 public String getReferenceName() {
847 final ASTNode childByRole = getReferenceNameNode();
848 if (childByRole == null) return null;
849 return childByRole.getText();
852 public final TextRange getRangeInElement() {
853 final TreeElement nameChild = (TreeElement)getReferenceNameNode();
854 if (nameChild == null) return new TextRange(0, getTextLength());
855 final int startOffset = nameChild.getStartOffsetInParent();
856 return new TextRange(startOffset, startOffset + nameChild.getTextLength());
859 @NotNull
860 public PsiType[] getTypeParameters() {
861 final PsiReferenceParameterList parameterList = getParameterList();
862 if (parameterList == null) return PsiType.EMPTY_ARRAY;
863 return parameterList.getTypeArguments();
867 public final PsiElement getElement() {
868 return this;
871 public final void accept(@NotNull final PsiElementVisitor visitor) {
872 if (visitor instanceof JavaElementVisitor) {
873 ((JavaElementVisitor)visitor).visitReferenceElement(this);
875 else {
876 visitor.visitElement(this);
880 public final String toString() {
881 return "PsiJavaCodeReferenceElement:" + getText();