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
;
18 import com
.intellij
.extapi
.psi
.ASTWrapperPsiElement
;
19 import com
.intellij
.lang
.ASTNode
;
20 import com
.intellij
.openapi
.util
.TextRange
;
21 import com
.intellij
.psi
.impl
.CheckUtil
;
22 import com
.intellij
.psi
.impl
.source
.codeStyle
.ReferenceAdjuster
;
23 import com
.intellij
.psi
.impl
.source
.resolve
.JavaResolveUtil
;
24 import com
.intellij
.psi
.impl
.source
.resolve
.ResolveCache
;
25 import com
.intellij
.psi
.meta
.PsiMetaData
;
26 import com
.intellij
.psi
.meta
.PsiMetaOwner
;
27 import com
.intellij
.psi
.scope
.BaseScopeProcessor
;
28 import com
.intellij
.psi
.scope
.JavaScopeProcessorEvent
;
29 import com
.intellij
.psi
.scope
.PsiScopeProcessor
;
30 import com
.intellij
.psi
.scope
.util
.PsiScopesUtil
;
31 import com
.intellij
.util
.IncorrectOperationException
;
32 import com
.intellij
.util
.ObjectUtils
;
33 import org
.jetbrains
.annotations
.NonNls
;
34 import org
.jetbrains
.annotations
.NotNull
;
35 import org
.jetbrains
.annotations
.Nullable
;
37 import java
.util
.LinkedHashSet
;
43 public abstract class AbstractQualifiedReference
<T
extends AbstractQualifiedReference
<T
>> extends ASTWrapperPsiElement
implements PsiPolyVariantReference
, PsiQualifiedReference
{
44 private static final ResolveCache
.PolyVariantResolver
<AbstractQualifiedReference
> MY_RESOLVER
= new ResolveCache
.PolyVariantResolver
<AbstractQualifiedReference
>() {
45 public ResolveResult
[] resolve(final AbstractQualifiedReference expression
, final boolean incompleteCode
) {
46 return expression
.resolveInner();
50 protected AbstractQualifiedReference(@NotNull final ASTNode node
) {
54 public final PsiReference
getReference() {
58 public final PsiElement
getElement() {
62 protected abstract ResolveResult
[] resolveInner();
65 public final ResolveResult
[] multiResolve(final boolean incompleteCode
) {
66 return getManager().getResolveCache().resolveWithCaching(this, MY_RESOLVER
, true, false);
70 public final PsiElement
resolve() {
71 final ResolveResult
[] results
= multiResolve(false);
72 return results
.length
== 1 ? results
[0].getElement() : null;
75 protected boolean processVariantsInner(PsiScopeProcessor processor
) {
76 final T qualifier
= getQualifier();
77 if (qualifier
== null) {
78 return processUnqualifiedVariants(processor
);
81 final PsiElement psiElement
= qualifier
.resolve();
82 return psiElement
== null || psiElement
.processDeclarations(processor
, ResolveState
.initial(), null, this);
85 protected boolean processUnqualifiedVariants(final PsiScopeProcessor processor
) {
86 return PsiScopesUtil
.treeWalkUp(processor
, this, null);
90 public String
getCanonicalText() {
94 @SuppressWarnings({"unchecked"})
96 public T
getQualifier() {
97 return (T
)findChildByClass(getClass());
100 public PsiElement
handleElementRename(final String newElementName
) throws IncorrectOperationException
{
101 CheckUtil
.checkWritable(this);
102 final PsiElement firstChildNode
= ObjectUtils
.assertNotNull(getFirstChild());
103 final PsiElement firstInIdentifier
= getClass().isInstance(firstChildNode
) ? ObjectUtils
.assertNotNull(firstChildNode
.getNextSibling()).getNextSibling() : firstChildNode
;
104 getNode().removeRange(firstInIdentifier
.getNode(), null);
105 final PsiElement referenceName
= ObjectUtils
.assertNotNull(parseReference(newElementName
).getReferenceNameElement());
106 getNode().addChild(referenceName
.getNode());
110 public PsiElement
bindToElement(@NotNull PsiElement element
) throws IncorrectOperationException
{
111 CheckUtil
.checkWritable(this);
112 if (isReferenceTo(element
)) return this;
114 if (element
instanceof PsiMethod
) {
115 final PsiMethod method
= (PsiMethod
)element
;
116 final String methodName
= method
.getName();
117 if (isDirectlyVisible(method
)) return replaceReference(methodName
);
119 final AbstractQualifiedReference result
= replaceReference(method
.getContainingClass().getQualifiedName() + "." + methodName
);
120 final AbstractQualifiedReference qualifier
= result
.getQualifier();
121 assert qualifier
!= null;
122 qualifier
.shortenReferences();
125 if (element
instanceof PsiClass
) {
126 return replaceReference(((PsiClass
)element
).getQualifiedName()).shortenReferences();
128 if (element
instanceof PsiPackage
) {
129 return replaceReference(((PsiPackage
)element
).getQualifiedName());
131 if (element
instanceof PsiMetaOwner
) {
132 final PsiMetaData metaData
= ((PsiMetaOwner
)element
).getMetaData();
133 if (metaData
!= null) {
134 final String name
= metaData
.getName(this);
136 return replaceReference(name
);
143 private boolean isDirectlyVisible(final PsiMethod method
) {
144 final AbstractQualifiedReferenceResolvingProcessor processor
= new AbstractQualifiedReferenceResolvingProcessor() {
145 protected void process(final PsiElement element
) {
146 if (getManager().areElementsEquivalent(element
, method
) && isAccessible(element
)) {
151 processUnqualifiedVariants(processor
);
152 return processor
.isFound();
155 protected final AbstractQualifiedReference
replaceReference(final String newText
) {
156 final ASTNode newNode
= parseReference(newText
).getNode();
157 getNode().getTreeParent().replaceChild(getNode(), newNode
);
158 return (AbstractQualifiedReference
)newNode
.getPsi();
162 protected abstract T
parseReference(String newText
);
164 protected boolean isAccessible(final PsiElement element
) {
165 if (element
instanceof PsiMember
) {
166 final PsiMember member
= (PsiMember
)element
;
167 return JavaResolveUtil
.isAccessible(member
, member
.getContainingClass(), member
.getModifierList(), this, null, null);
173 protected AbstractQualifiedReference
shortenReferences() {
174 final PsiElement refElement
= resolve();
175 if (refElement
instanceof PsiClass
) {
176 final PsiQualifiedReference reference
= ReferenceAdjuster
.getClassReferenceToShorten((PsiClass
)refElement
, false, this);
177 if (reference
instanceof AbstractQualifiedReference
) {
178 ((AbstractQualifiedReference
)reference
).dequalify();
184 private void dequalify() {
185 final AbstractQualifiedReference qualifier
= getQualifier();
186 if (qualifier
!= null) {
187 getNode().removeChild(qualifier
.getNode());
188 final PsiElement separator
= getSeparator();
189 if (separator
!= null) {
190 final ASTNode separatorNode
= separator
.getNode();
191 if (separatorNode
!= null) {
192 getNode().removeChild(separatorNode
);
198 public boolean isReferenceTo(final PsiElement element
) {
199 final PsiManager manager
= getManager();
200 for (final ResolveResult result
: multiResolve(false)) {
201 if (manager
.areElementsEquivalent(result
.getElement(), element
)) return true;
207 protected abstract PsiElement
getSeparator();
210 protected abstract PsiElement
getReferenceNameElement();
212 public TextRange
getRangeInElement() {
213 final PsiElement element
= getSeparator();
214 final int length
= getTextLength();
215 return element
== null ? TextRange
.from(0, length
) : new TextRange(element
.getStartOffsetInParent() + element
.getTextLength(), length
);
220 public String
getReferenceName() {
221 final PsiElement element
= getReferenceNameElement();
222 return element
== null ?
null : element
.getText().trim();
225 public final boolean isSoft() {
229 protected abstract static class AbstractQualifiedReferenceResolvingProcessor
extends BaseScopeProcessor
{
230 private boolean myFound
= false;
231 private final Set
<ResolveResult
> myResults
= new LinkedHashSet
<ResolveResult
>();
233 public boolean execute(final PsiElement element
, final ResolveState state
) {
234 if (isFound()) return false;
239 protected final void addResult(ResolveResult resolveResult
) {
240 myResults
.add(resolveResult
);
243 private boolean isFound() {
247 public void handleEvent(final Event event
, final Object associated
) {
248 if ((event
== JavaScopeProcessorEvent
.SET_CURRENT_FILE_CONTEXT
|| event
== Event
.SET_DECLARATION_HOLDER
) && !myResults
.isEmpty()) {
251 super.handleEvent(event
, associated
);
254 protected final void setFound() {
258 protected abstract void process(PsiElement element
);
260 public Set
<ResolveResult
> getResults() {