package com.intellij.javascript.flex;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.XmlRecursiveElementVisitor;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.BasicAttributeValueReference;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileBasedUserDataCache;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.IdReferenceProvider;
import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class XmlIdValueReference extends BasicAttributeValueReference {
public XmlIdValueReference(final PsiElement element) {
super(element);
}
@Nullable
private static PsiElement getIdValueElement(PsiElement element) {
if (element instanceof XmlTag) {
final XmlAttribute attribute = ((XmlTag)element).getAttribute(IdReferenceProvider.ID_ATTR_NAME, null);
return attribute != null ? attribute.getValueElement() : null;
}
else {
return element;
}
}
@Nullable
protected static String getIdValue(final PsiElement element) {
if (element instanceof XmlTag) {
return ((XmlTag)element).getAttributeValue(IdReferenceProvider.ID_ATTR_NAME);
}
return null;
}
protected static boolean isAcceptableTagType(final XmlTag subTag) {
return subTag.getAttributeValue(IdReferenceProvider.ID_ATTR_NAME) != null;
}
private static final FileBasedUserDataCache<List<PsiElement>> ourCachedIdsCache = new FileBasedUserDataCache<List<PsiElement>>() {
private final Key<CachedValue<List<PsiElement>>> ourCachedIdsValueKey = Key.create("mxml.id.cached.value");
protected List<PsiElement> doCompute(PsiFile file) {
final List<PsiElement> result = new ArrayList<>();
file.accept(new XmlRecursiveElementVisitor(true) {
@Override
public void visitXmlTag(XmlTag tag) {
if (isAcceptableTagType(tag)) {
result.add(tag);
}
super.visitXmlTag(tag);
}
});
return result;
}
protected Key<CachedValue<List<PsiElement>>> getKey() {
return ourCachedIdsValueKey;
}
};
private void process(PsiElementProcessor<PsiElement> processor) {
final PsiFile psiFile = getElement().getContainingFile();
for (PsiElement e : ourCachedIdsCache.compute(psiFile)) {
if (!processor.execute(e)) return;
}
}
@Nullable
public PsiElement resolve() {
final Ref<PsiElement> result = new Ref<>();
process(new PsiElementProcessor<PsiElement>() {
final String canonicalText = getCanonicalText();
public boolean execute(@NotNull final PsiElement element) {
final String idValue = getIdValue(element);
if (idValue != null && idValue.equals(canonicalText)) {
result.set(getIdValueElement(element));
return false;
}
return true;
}
});
return result.get();
}
@NotNull
public Object[] getVariants() {
final List<String> result = new LinkedList<>();
process(new PsiElementProcessor<PsiElement>() {
public boolean execute(@NotNull final PsiElement element) {
result.add(getIdValue(element));
return true;
}
});
return ArrayUtil.toObjectArray(result);
}
public boolean isSoft() {
return false;
}
}