package org.dlangplugin.psi.impl;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.ResolveCache;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import gnu.trove.THashSet;
import org.dlangplugin.ide.DLangLookupElement;
import org.dlangplugin.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Set;
public class DLangReferenceImpl extends DLangCompositeElementImpl implements DLangReference, PsiPolyVariantReference {
public DLangReferenceImpl(ASTNode node) {
super(node);
}
@Override
public PsiElement getElement() {
return this;
}
@Override
public PsiReference getReference() {
return this;
}
@Override
public TextRange getRangeInElement() {
final TextRange textRange = getTextRange();
return new TextRange(0, textRange.getEndOffset() - textRange.getStartOffset());
}
@NotNull
@Override
public String getCanonicalText() {
return getText();
}
@Override
public boolean isReferenceTo(PsiElement element) {
final PsiElement resolve = resolve();
if (element instanceof DFile &&
resolve instanceof DLangComponentName &&
resolve.getParent() instanceof DLangClassDeclaration) {
return element == resolve.getContainingFile();
}
final DLangReference[] references = PsiTreeUtil.getChildrenOfType(this, DLangReference.class);
final boolean chain = references != null && references.length == 2;
return !chain && resolve == element;
}
@NotNull
@Override
public Object[] getVariants() {
final Set<DLangComponentName> availableNamespaces = new THashSet<DLangComponentName>();
final DLangReference[] childReferences = PsiTreeUtil.getChildrenOfType(this, DLangReference.class);
final boolean isChain = childReferences != null && childReferences.length == 2;
if (!isChain) {
PsiTreeUtil.treeWalkUp(new ComponentNameScopeProcessor(availableNamespaces), this, null, new ResolveState());
addNamespaceVariants(availableNamespaces, PsiTreeUtil.getParentOfType(this, DLangClassDeclaration.class));
}
addNamespaceVariants(availableNamespaces, PsiTreeUtil.getParentOfType(this, DFile.class));
Object[] variants = DLangLookupElement.convert(availableNamespaces).toArray();
PsiPackage rootPackage = JavaPsiFacade.getInstance(getElement().getProject()).findPackage("");
return rootPackage == null ? variants : ArrayUtil.mergeArrays(variants, rootPackage.getSubPackages());
}
//Look for DLangNamedComponent children of namespaceElement and get its' names.
private static void addNamespaceVariants(Set<DLangComponentName> suggestedVariants, @Nullable PsiElement namespaceElement) {
if (namespaceElement == null) {
return;
}
for (DLangNamedComponent namedComponent : DLangResolveUtil.findNamedSubComponents(namespaceElement)) {
if ( namedComponent.getComponentName() != null) {
suggestedVariants.add(namedComponent.getComponentName());
}
}
}
@Override
public boolean isSoft() {
return false;
}
@Override
public PsiElement resolve() {
final ResolveResult[] resolveResults = multiResolve(true);
return resolveResults.length == 0 ||
resolveResults.length > 1 ||
!resolveResults[0].isValidResult() ? null : resolveResults[0].getElement();
}
@NotNull
@Override
public ResolveResult[] multiResolve(boolean incompleteCode) {
final List<? extends PsiElement> elements =
ResolveCache.getInstance(getProject()).resolveWithCaching(this, DLangResolver.INSTANCE, true, incompleteCode);
return toCandidateInfoArray(elements);
}
@NotNull
private static ResolveResult[] toCandidateInfoArray(List<? extends PsiElement> elements) {
final ResolveResult[] result = new ResolveResult[elements.size()];
for (int i = 0, size = elements.size(); i < size; i++) {
result[i] = new CandidateInfo(elements.get(i), EmptySubstitutor.getInstance());
}
return result;
}
@Override
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
PsiElement element = this;
if (getText().indexOf('.') != -1) {
// qName
final PsiElement lastChild = getLastChild();
element = lastChild == null ? this : lastChild;
}
final DLangIdentifier identifier = PsiTreeUtil.getChildOfType(element, DLangIdentifier.class);
final DLangIdentifier identifierNew = DLangElementGenerator.createIdentifierFromText(getProject(), newElementName);
if (identifier != null && identifierNew != null) {
element.getNode().replaceChild(identifier.getNode(), identifierNew.getNode());
}
return this;
}
@Override
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
if (element instanceof DFile) {
bindToFile(element);
}
else if (element instanceof PsiPackage) {
bindToPackage((PsiPackage)element);
}
return this;
}
private void bindToFile(PsiElement element) {
//TODO
}
private void bindToPackage(PsiPackage element) {
//TODO
}
}