blob: 77d4dde83f20c0e8d83310aea433880edb8b74ce [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.psi.impl.source.resolve.reference.impl;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* Created by IntelliJ IDEA.
* User: ik
* Date: 02.04.2003
* Time: 12:22:03
* To change this template use Options | File Templates.
*/
public class PsiMultiReference implements PsiPolyVariantReference {
public static final Comparator<PsiReference> COMPARATOR = new Comparator<PsiReference>() {
@Override
public int compare(final PsiReference ref1, final PsiReference ref2) {
if (ref1.isSoft() && !ref2.isSoft()) return 1;
if (!ref1.isSoft() && ref2.isSoft()) return -1;
boolean resolves1 = resolves(ref1);
boolean resolves2 = resolves(ref2);
if (resolves1 && !resolves2) return -1;
if (!resolves1 && resolves2) return 1;
final TextRange range1 = ref1.getRangeInElement();
final TextRange range2 = ref2.getRangeInElement();
if(TextRange.areSegmentsEqual(range1, range2)) return 0;
if(range1.getStartOffset() >= range2.getStartOffset() && range1.getEndOffset() <= range2.getEndOffset()) return -1;
if(range2.getStartOffset() >= range1.getStartOffset() && range2.getEndOffset() <= range1.getEndOffset()) return 1;
return 0;
}
};
private static boolean resolves(final PsiReference ref1) {
return ref1 instanceof PsiPolyVariantReference && ((PsiPolyVariantReference)ref1).multiResolve(false).length > 0 || ref1.resolve() != null;
}
private final PsiReference[] myReferences;
private final PsiElement myElement;
private boolean mySorted;
public PsiMultiReference(@NotNull PsiReference[] references, PsiElement element){
assert references.length > 0;
myReferences = references;
myElement = element;
}
public PsiReference[] getReferences() {
return myReferences;
}
private synchronized PsiReference chooseReference(){
if (!mySorted) {
Arrays.sort(myReferences, COMPARATOR);
mySorted = true;
}
return myReferences[0];
}
@Override
public PsiElement getElement(){
return myElement;
}
@Override
public TextRange getRangeInElement(){
final PsiReference chosenRef = chooseReference();
TextRange rangeInElement = chosenRef.getRangeInElement();
PsiElement element = chosenRef.getElement();
while(element != myElement) {
rangeInElement = rangeInElement.shiftRight(element.getStartOffsetInParent());
element = element.getParent();
if (element instanceof PsiFile) break;
}
return rangeInElement;
}
@Override
public PsiElement resolve(){
final PsiReference reference = chooseReference();
if (cannotChoose()) {
final ResolveResult[] results = multiResolve(false);
return results.length == 1 ? results[0].getElement() : null;
}
return reference.resolve();
}
private boolean cannotChoose() {
return myReferences.length > 1 && COMPARATOR.compare(myReferences[0], myReferences[1]) == 0;
}
@Override
@NotNull
public String getCanonicalText(){
return chooseReference().getCanonicalText();
}
@Override
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException{
return chooseReference().handleElementRename(newElementName);
}
@Override
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException{
return chooseReference().bindToElement(element);
}
@Override
public boolean isReferenceTo(PsiElement element){
for (PsiReference reference : myReferences) {
if (reference.isReferenceTo(element)) return true;
}
return false;
}
@Override
@NotNull
public Object[] getVariants() {
Set<Object> variants = new HashSet<Object>();
for(PsiReference ref: myReferences) {
Object[] refVariants = ref.getVariants();
ContainerUtil.addAll(variants, refVariants);
}
return variants.toArray();
}
@Override
public boolean isSoft(){
for (PsiReference reference : getReferences()) {
if (!reference.isSoft()) {
return false;
}
}
return true;
}
@Override
@NotNull
public ResolveResult[] multiResolve(final boolean incompleteCode) {
final PsiReference[] refs = getReferences();
Collection<ResolveResult> result = new LinkedHashSet<ResolveResult>(refs.length);
PsiElementResolveResult selfReference = null;
for (PsiReference reference : refs) {
if (reference instanceof PsiPolyVariantReference) {
ContainerUtil.addAll(result, ((PsiPolyVariantReference)reference).multiResolve(incompleteCode));
}
else {
final PsiElement resolved = reference.resolve();
if (resolved != null) {
final PsiElementResolveResult rresult = new PsiElementResolveResult(resolved);
if (getElement() == resolved) {
selfReference = rresult;
} else {
result.add(rresult);
}
}
}
}
if (result.isEmpty() && selfReference != null) {
result.add(selfReference); // if i the only one starring at the sun
}
return result.toArray(new ResolveResult[result.size()]);
}
@Override
public String toString() {
return "PsiMultiReference{myReferences=" + Arrays.toString(myReferences) + '}';
}
}