blob: 483527c8fce564803b9daece73175f0dc83abc7a [file] [log] [blame]
/*
* Copyright 2000-2014 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 org.intellij.plugins.relaxNG.model.resolve;
import com.intellij.ide.highlighter.XmlFileType;
import com.intellij.navigation.ColoredItemPresentation;
import com.intellij.navigation.ItemPresentation;
import com.intellij.navigation.NavigationItem;
import com.intellij.navigation.PsiElementNavigationItem;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.meta.PsiMetaData;
import com.intellij.psi.meta.PsiMetaOwner;
import com.intellij.psi.meta.PsiPresentableMetaData;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.xml.XmlFile;
import com.intellij.util.indexing.*;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.KeyDescriptor;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.xml.NanoXmlUtil;
import org.intellij.plugins.relaxNG.ApplicationLoader;
import org.intellij.plugins.relaxNG.compact.RncFileType;
import org.intellij.plugins.relaxNG.model.CommonElement;
import org.intellij.plugins.relaxNG.model.Define;
import org.intellij.plugins.relaxNG.model.Grammar;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
/*
* Created by IntelliJ IDEA.
* User: sweinreuter
* Date: 09.06.2010
*/
public class RelaxSymbolIndex extends ScalarIndexExtension<String> {
@NonNls
public static final ID<String, Void> NAME = ID.create("RelaxSymbolIndex");
public static Collection<String> getSymbolNames(Project project) {
return FileBasedIndex.getInstance().getAllKeys(NAME, project);
}
public static NavigationItem[] getSymbolsByName(final String name, Project project, boolean includeNonProjectItems) {
final GlobalSearchScope scope = includeNonProjectItems ? GlobalSearchScope.allScope(project) : GlobalSearchScope.projectScope(project);
final SymbolCollector processor = new SymbolCollector(name, project, scope);
FileBasedIndex.getInstance().processValues(NAME, name, null, processor, scope);
return processor.getResult();
}
@NotNull
@Override
public ID<String, Void> getName() {
return NAME;
}
@NotNull
@Override
public DataIndexer<String, Void, FileContent> getIndexer() {
return new DataIndexer<String, Void, FileContent>() {
@Override
@NotNull
public Map<String, Void> map(@NotNull FileContent inputData) {
final HashMap<String, Void> map = new HashMap<String, Void>();
if (inputData.getFileType() == XmlFileType.INSTANCE) {
CharSequence inputDataContentAsText = inputData.getContentAsText();
if (CharArrayUtil.indexOf(inputDataContentAsText, ApplicationLoader.RNG_NAMESPACE, 0) == -1) return Collections.emptyMap();
NanoXmlUtil.parse(CharArrayUtil.readerFromCharSequence(inputData.getContentAsText()), new NanoXmlUtil.IXMLBuilderAdapter() {
NanoXmlUtil.IXMLBuilderAdapter attributeHandler;
int depth;
@Override
public void addAttribute(String key, String nsPrefix, String nsURI, String value, String type) throws Exception {
if (attributeHandler != null) {
attributeHandler.addAttribute(key, nsPrefix, nsURI, value, type);
}
}
@Override
public void startElement(String name, String nsPrefix, String nsURI, String systemID, int lineNr) throws Exception {
attributeHandler = null;
if (depth == 1 && ApplicationLoader.RNG_NAMESPACE.equals(nsURI)) {
if ("define".equals(name)) {
attributeHandler = new NanoXmlUtil.IXMLBuilderAdapter() {
@Override
public void addAttribute(String key, String nsPrefix, String nsURI, String value, String type) throws Exception {
if ("name".equals(key) && (nsURI == null || nsURI.length() == 0) && value != null) {
map.put(value, null);
}
}
};
}
}
depth++;
}
@Override
public void endElement(String name, String nsPrefix, String nsURI) throws Exception {
attributeHandler = null;
depth--;
}
});
} else if (inputData.getFileType() == RncFileType.getInstance()) {
final PsiFile file = inputData.getPsiFile();
if (file instanceof XmlFile) {
final Grammar grammar = GrammarFactory.getGrammar((XmlFile)file);
if (grammar != null) {
grammar.acceptChildren(new CommonElement.Visitor() {
@Override
public void visitDefine(Define define) {
final String name = define.getName();
if (name != null) {
map.put(name, null);
}
}
});
}
}
}
return map;
}
};
}
@NotNull
@Override
public KeyDescriptor<String> getKeyDescriptor() {
return new EnumeratorStringDescriptor();
}
@NotNull
@Override
public FileBasedIndex.InputFilter getInputFilter() {
return new DefaultFileTypeSpecificInputFilter(StdFileTypes.XML, RncFileType.getInstance()) {
@Override
public boolean acceptInput(@NotNull VirtualFile file) {
return !(file.getFileSystem() instanceof JarFileSystem);
}
};
}
@Override
public boolean dependsOnFileContent() {
return true;
}
@Override
public int getVersion() {
return 0;
}
private static class SymbolCollector implements FileBasedIndex.ValueProcessor<Void> {
private final GlobalSearchScope myScope;
private final PsiManager myMgr;
private final String myName;
private final Collection<NavigationItem> myResult = new ArrayList<NavigationItem>();
public SymbolCollector(String name, Project project, GlobalSearchScope scope) {
myMgr = PsiManager.getInstance(project);
myScope = scope;
myName = name;
}
@Override
public boolean process(VirtualFile file, Void kind) {
if (myScope.contains(file)) {
final PsiFile psiFile = myMgr.findFile(file);
if (psiFile instanceof XmlFile) {
final Grammar grammar = GrammarFactory.getGrammar((XmlFile)psiFile);
if (grammar != null) {
grammar.acceptChildren(new CommonElement.Visitor() {
@Override
public void visitDefine(Define define) {
if (myName.equals(define.getName())) {
final PsiElement psi = define.getPsiElement();
if (psi != null) {
MyNavigationItem.add((NavigationItem)define.getPsiElement(), myResult);
}
}
}
});
}
}
}
return true;
}
public NavigationItem[] getResult() {
return myResult.toArray(new NavigationItem[myResult.size()]);
}
}
private static class MyNavigationItem implements PsiElementNavigationItem, ItemPresentation {
private final NavigationItem myItem;
private final ItemPresentation myPresentation;
private MyNavigationItem(NavigationItem item, @NotNull final ItemPresentation presentation) {
myItem = item;
myPresentation = presentation;
}
@Override
public String getPresentableText() {
return myPresentation.getPresentableText();
}
@Override
@Nullable
public String getLocationString() {
return getLocationString((PsiElement)myItem);
}
private static String getLocationString(PsiElement element) {
return "(in " + element.getContainingFile().getName() + ")";
}
@Override
@Nullable
public Icon getIcon(boolean open) {
return myPresentation.getIcon(open);
}
@Nullable
public TextAttributesKey getTextAttributesKey() {
return myPresentation instanceof ColoredItemPresentation ? ((ColoredItemPresentation) myPresentation).getTextAttributesKey() : null;
}
@Override
public String getName() {
return myItem.getName();
}
@Override
public ItemPresentation getPresentation() {
return this;
}
@Override
public PsiElement getTargetElement() {
return (PsiElement) myItem;
}
@Override
public void navigate(boolean requestFocus) {
myItem.navigate(requestFocus);
}
@Override
public boolean canNavigate() {
return myItem.canNavigate();
}
@Override
public boolean canNavigateToSource() {
return myItem.canNavigateToSource();
}
public static void add(final NavigationItem item, Collection<NavigationItem> symbolNavItems) {
final ItemPresentation presentation;
if (item instanceof PsiMetaOwner) {
final PsiMetaData data = ((PsiMetaOwner)item).getMetaData();
if (data instanceof PsiPresentableMetaData) {
final PsiPresentableMetaData metaData = (PsiPresentableMetaData)data;
presentation = new ColoredItemPresentation() {
@Override
public String getPresentableText() {
return metaData.getName();
}
@Override
@Nullable
public String getLocationString() {
return MyNavigationItem.getLocationString((PsiElement)item);
}
@Override
@Nullable
public Icon getIcon(boolean open) {
return metaData.getIcon();
}
@Nullable
@Override
public TextAttributesKey getTextAttributesKey() {
final ItemPresentation p = item.getPresentation();
return p instanceof ColoredItemPresentation ? ((ColoredItemPresentation) p).getTextAttributesKey() : null;
}
};
} else {
presentation = item.getPresentation();
}
} else {
presentation = item.getPresentation();
}
if (presentation != null) {
symbolNavItems.add(new MyNavigationItem(item, presentation));
}
}
}
}