package org.fandev.lang.fan.psi.impl.types;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.roots.ContentIterator;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.ResolveCache;
import com.intellij.psi.stubs.IStubElementType;
import com.intellij.psi.stubs.StubElement;
import org.fandev.lang.fan.FanFileType;
import org.fandev.lang.fan.psi.api.FanResolveResult;
import org.fandev.lang.fan.psi.api.types.FanCodeReferenceElement;
import org.fandev.lang.fan.psi.impl.FanReferenceElementImpl;
import org.fandev.lang.fan.psi.impl.FanResolveResultImpl;
import org.fandev.lang.fan.psi.FanFile;
import org.fandev.utils.PsiUtil;
import org.fandev.index.FanIndex;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.ArrayList;
/**
* @author Dror Bereznitsky
* @date Feb 19, 2009 11:32:13 PM
*/
public class FanCodeReferenceElementImpl extends FanReferenceElementImpl implements FanCodeReferenceElement {
private static final FanResolver RESOLVER = new FanResolver();
public FanCodeReferenceElementImpl(final StubElement stubElement, @NotNull final IStubElementType iStubElementType) {
super(stubElement, iStubElementType);
}
public FanCodeReferenceElementImpl(final ASTNode astNode) {
super(astNode);
}
public String toString() {
return "Reference element";
}
public PsiElement getQualifier() {
return this;
}
public PsiElement resolve() {
final ResolveResult[] results = getManager().getResolveCache().resolveWithCaching(this, RESOLVER, false, false);
return results.length == 1 ? results[0].getElement() : null;
}
public String getCanonicalText() {
final PsiElement resolved = resolve();
if (resolved instanceof PsiClass) {
return ((PsiClass) resolved).getQualifiedName();
}
if (resolved instanceof PsiPackage) {
return ((PsiPackage) resolved).getQualifiedName();
}
return null;
}
public boolean isReferenceTo(final PsiElement psiElement) {
return getManager().areElementsEquivalent(psiElement, resolve());
}
public Object[] getVariants() {
final FanCodeReferenceElement qualifier = (FanCodeReferenceElement) getQualifier();
if (qualifier != null) {
final PsiElement resolve = qualifier.resolve();
if (resolve instanceof PsiClass) {
final PsiClass clazz = (PsiClass) resolve;
final List<PsiElement> result = new ArrayList<PsiElement>();
for (final PsiField field : clazz.getFields()) {
if (field.hasModifierProperty(PsiModifier.STATIC)) {
result.add(field);
}
}
for (final PsiMethod method : clazz.getMethods()) {
if (method.hasModifierProperty(PsiModifier.STATIC)) {
result.add(method);
}
}
return result.toArray(new PsiElement[0]);
}
}
return new Object[]{};
}
public boolean isSoft() {
return false;
}
@NotNull
public ResolveResult[] multiResolve(final boolean incompleteCode) {
return getManager().getResolveCache().resolveWithCaching(this, RESOLVER, false, incompleteCode);
}
private static class FanResolver implements ResolveCache.PolyVariantResolver<FanCodeReferenceElementImpl> {
public ResolveResult[] resolve(final FanCodeReferenceElementImpl fanCodeReferenceElement, final boolean incompleteCode) {
if (fanCodeReferenceElement.getReferenceName() == null) {
return FanResolveResult.EMPTY_ARRAY;
}
final FanResolveResult[] results = _resolve(fanCodeReferenceElement, fanCodeReferenceElement.getManager());
return results;
}
//TODO handle other possbile reference types: Enum, Mixin
private FanResolveResult[] _resolve(final FanCodeReferenceElementImpl ref, final PsiManager manager) {
final String refName = ref.getReferenceName();
final FanCodeReferenceElement qualifier = (FanCodeReferenceElement) ref.getQualifier();
if (qualifier != null) {
final List<FanResolveResult> results = new ArrayList<FanResolveResult>();
ProjectRootManager.getInstance(manager.getProject()).getFileIndex().iterateContent(new ContentIterator() {
public boolean processFile(final VirtualFile virtualFile) {
if (FanFileType.FAN_FILE_TYPE == virtualFile.getFileType()) {
final FanFile psiFile = (FanFile) manager.findFile(virtualFile);
final PsiClass[] classes = psiFile.getClasses();
for (final PsiClass aClass : classes) {
if (refName.equals(aClass.getName())) {
final boolean isAccessible = PsiUtil.isAccessible(ref, aClass);
results.add(new FanResolveResultImpl(aClass, isAccessible));
}
}
}
return true;
}
});
if (results.size() > 0) {
return results.toArray(new FanResolveResult[0]);
}
final FanIndex fanIndex = (FanIndex) manager.getProject().getComponent(FanIndex.COMPONENT_NAME);
final PsiFile typeFile = fanIndex.getFanFileByTypeName(refName);
if (typeFile != null) {
final FanFile psiFile = (FanFile) typeFile;
final PsiClass[] classes = psiFile.getClasses();
for (final PsiClass aClass : classes) {
try {
if (refName.equals(aClass.getName())) {
final boolean isAccessible = PsiUtil.isAccessible(ref, aClass);
results.add(new FanResolveResultImpl(aClass, isAccessible));
}
} catch (Exception e) {
continue;
}
}
}
return results.toArray(new FanResolveResult[0]);
}
return FanResolveResult.EMPTY_ARRAY;
}
}
}