introduce parameter object: ui clarification; visibility can be fixed for writable...
[fedora-idea.git] / java / java-impl / src / com / intellij / psi / AbstractQualifiedReference.java
blob863b93555417b1f4189b40895c4945b002d5d0d0
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;
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;
38 import java.util.Set;
40 /**
41 * @author peter
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) {
51 super(node);
54 public final PsiReference getReference() {
55 return this;
58 public final PsiElement getElement() {
59 return this;
62 protected abstract ResolveResult[] resolveInner();
64 @NotNull
65 public final ResolveResult[] multiResolve(final boolean incompleteCode) {
66 return getManager().getResolveCache().resolveWithCaching(this, MY_RESOLVER, true, false);
69 @Nullable
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() {
91 return getText();
94 @SuppressWarnings({"unchecked"})
95 @Nullable
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());
107 return this;
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();
123 return result;
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);
135 if (name != null) {
136 return replaceReference(name);
140 return this;
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)) {
147 setFound();
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();
161 @NotNull
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);
169 return true;
172 @NotNull
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();
181 return this;
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;
203 return false;
206 @Nullable
207 protected abstract PsiElement getSeparator();
209 @Nullable
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);
218 @Nullable
219 @NonNls
220 public String getReferenceName() {
221 final PsiElement element = getReferenceNameElement();
222 return element == null ? null : element.getText().trim();
225 public final boolean isSoft() {
226 return false;
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;
235 process(element);
236 return true;
239 protected final void addResult(ResolveResult resolveResult) {
240 myResults.add(resolveResult);
243 private boolean isFound() {
244 return myFound;
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()) {
249 setFound();
251 super.handleEvent(event, associated);
254 protected final void setFound() {
255 myFound = true;
258 protected abstract void process(PsiElement element);
260 public Set<ResolveResult> getResults() {
261 return myResults;