blob: b2caddbbc6cce37023fafc55f2873e49f9f0f8d5 [file] [log] [blame]
/*
* 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;
}
}