| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.intellij.openapi.paths; |
| |
| import com.intellij.codeInsight.daemon.EmptyResolveMessageProvider; |
| import com.intellij.codeInspection.LocalQuickFix; |
| import com.intellij.codeInspection.LocalQuickFixProvider; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference; |
| import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceOwner; |
| import com.intellij.psi.impl.source.resolve.reference.impl.providers.PsiFileReference; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.containers.ContainerUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| |
| /** |
| * @author Dmitry Avdeev |
| */ |
| public class PsiDynaReference<T extends PsiElement> extends PsiReferenceBase<T> |
| implements FileReferenceOwner, PsiPolyVariantReference, LocalQuickFixProvider, EmptyResolveMessageProvider { |
| |
| private final List<PsiReference> myReferences = new ArrayList<PsiReference>(); |
| private int myChosenOne = -1; |
| private ResolveResult[] myCachedResult; |
| |
| public PsiDynaReference(final T psiElement) { |
| super(psiElement, true); |
| } |
| |
| public void addReferences(Collection<PsiReference> references) { |
| myReferences.addAll(references); |
| for (PsiReference reference : references) { |
| if (!reference.isSoft()) mySoft = false; |
| } |
| } |
| |
| public List<PsiReference> getReferences() { |
| return myReferences; |
| } |
| |
| public void addReference(PsiReference reference) { |
| myReferences.add(reference); |
| if (!reference.isSoft()) mySoft = false; |
| } |
| |
| @Override |
| public TextRange getRangeInElement() { |
| |
| PsiReference resolved = null; |
| PsiReference reference = myReferences.get(0); |
| |
| if (reference.resolve() != null) { |
| resolved = reference; |
| } |
| |
| final TextRange range = reference.getRangeInElement(); |
| int start = range.getStartOffset(); |
| int end = range.getEndOffset(); |
| for (int i = 1; i < myReferences.size(); i++) { |
| reference = myReferences.get(i); |
| final TextRange textRange = getRange(reference); |
| start = Math.min(start, textRange.getStartOffset()); |
| if (resolved == null) { |
| end = Math.max(end, textRange.getEndOffset()); |
| } |
| } |
| return new TextRange(start, end); |
| } |
| |
| private TextRange getRange(PsiReference reference) { |
| TextRange rangeInElement = reference.getRangeInElement(); |
| PsiElement element = reference.getElement(); |
| while(element != myElement) { |
| rangeInElement = rangeInElement.shiftRight(element.getStartOffsetInParent()); |
| element = element.getParent(); |
| if (element instanceof PsiFile) break; |
| } |
| return rangeInElement; |
| } |
| |
| @Override |
| public PsiElement resolve(){ |
| final ResolveResult[] resolveResults = multiResolve(false); |
| return resolveResults.length == 1 ? resolveResults[0].getElement() : null; |
| } |
| |
| @Override |
| @NotNull |
| public String getCanonicalText(){ |
| final PsiReference reference = chooseReference(); |
| return reference == null ? myReferences.get(0).getCanonicalText() : reference.getCanonicalText(); |
| } |
| |
| @Override |
| public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException{ |
| final PsiReference reference = chooseReference(); |
| if (reference != null) { |
| return reference.handleElementRename(newElementName); |
| } |
| return myElement; |
| } |
| |
| @Override |
| public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { |
| for (PsiReference reference : myReferences) { |
| if (reference instanceof FileReference) { |
| return reference.bindToElement(element); |
| } |
| } |
| return myElement; |
| } |
| |
| @Override |
| public boolean isReferenceTo(PsiElement element){ |
| for (PsiReference reference : myReferences) { |
| if (reference.isReferenceTo(element)) return true; |
| } |
| return false; |
| } |
| |
| |
| @Override |
| @NotNull |
| public Object[] getVariants() { |
| return ArrayUtil.EMPTY_OBJECT_ARRAY; |
| } |
| |
| @Override |
| @NotNull |
| public ResolveResult[] multiResolve(final boolean incompleteCode) { |
| if (myCachedResult == null) { |
| myCachedResult = innerResolve(incompleteCode); |
| } |
| return myCachedResult; |
| } |
| |
| protected ResolveResult[] innerResolve(final boolean incompleteCode) { |
| List<ResolveResult> result = new ArrayList<ResolveResult>(); |
| for (PsiReference reference : myReferences) { |
| if (reference instanceof PsiPolyVariantReference) { |
| for (ResolveResult rr: ((PsiPolyVariantReference)reference).multiResolve(incompleteCode)) { |
| if (rr.isValidResult()) { |
| result.add(rr); |
| } |
| } |
| } |
| else { |
| final PsiElement resolved = reference.resolve(); |
| if (resolved != null) { |
| result.add(new PsiElementResolveResult(resolved)); |
| } |
| } |
| } |
| |
| return result.toArray(new ResolveResult[result.size()]); |
| } |
| |
| @Nullable |
| private PsiReference chooseReference(){ |
| if(myChosenOne != -1){ |
| return myReferences.get(myChosenOne); |
| } |
| boolean flag = false; |
| for(int i = 0; i < myReferences.size(); i++){ |
| final PsiReference reference = myReferences.get(i); |
| if(reference.isSoft() && flag) continue; |
| if(!reference.isSoft() && !flag){ |
| myChosenOne = i; |
| flag = true; |
| continue; |
| } |
| if(reference.resolve() != null){ |
| myChosenOne = i; |
| } |
| } |
| return myChosenOne >= 0 ? myReferences.get(myChosenOne) : null; |
| } |
| |
| @NotNull |
| @Override |
| @SuppressWarnings({"UnresolvedPropertyKey"}) |
| public String getUnresolvedMessagePattern() { |
| final PsiReference reference = chooseReference(); |
| |
| return reference instanceof EmptyResolveMessageProvider ? |
| ((EmptyResolveMessageProvider)reference).getUnresolvedMessagePattern() : |
| PsiBundle.message("cannot.resolve.symbol"); |
| } |
| |
| @Override |
| public LocalQuickFix[] getQuickFixes() { |
| final ArrayList<LocalQuickFix> list = new ArrayList<LocalQuickFix>(); |
| for (Object ref: myReferences) { |
| if (ref instanceof LocalQuickFixProvider) { |
| ContainerUtil.addAll(list, ((LocalQuickFixProvider)ref).getQuickFixes()); |
| } |
| } |
| return list.toArray(new LocalQuickFix[list.size()]); |
| } |
| |
| public String toString() { |
| //noinspection HardCodedStringLiteral |
| return "PsiDynaReference containing " + myReferences.toString(); |
| } |
| |
| @Override |
| public PsiFileReference getLastFileReference() { |
| for (PsiReference reference : myReferences) { |
| if (reference instanceof FileReferenceOwner) { |
| return ((FileReferenceOwner)reference).getLastFileReference(); |
| } |
| } |
| return null; |
| } |
| } |