blob: dd1d9869db05e946353be95221684e146be2d0ef [file] [log] [blame]
/*
* Copyright 2005-2008 Sascha Weinreuter
*
* 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.lang.xpath.xslt.impl;
import com.intellij.navigation.NavigationItem;
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.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ArrayUtil;
import com.intellij.util.indexing.*;
import com.intellij.util.io.*;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.util.xml.NanoXmlUtil;
import org.intellij.lang.xpath.xslt.XsltSupport;
import org.intellij.lang.xpath.xslt.psi.*;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/*
* Created by IntelliJ IDEA.
* User: sweinreuter
* Date: 15.12.2008
*/
public class XsltSymbolIndex extends FileBasedIndexExtension<String, XsltSymbolIndex.Kind> {
@NonNls
public static final ID<String, Kind> NAME = ID.create("XsltSymbolIndex");
enum Kind {
PARAM(XsltParameter.class), VARIABLE(XsltVariable.class), TEMPLATE(XsltTemplate.class), ANYTHING(null);
final Class<? extends XsltElement> myClazz;
Kind(Class<? extends XsltElement> clazz) {
myClazz = clazz;
}
@Nullable
public XsltElement wrap(XmlTag tag) {
final Class<? extends XsltElement> clazz;
if (myClazz != null) {
if (!name().toLowerCase().equals(tag.getLocalName())) {
return null;
}
clazz = myClazz;
} else {
try {
clazz = valueOf(tag.getLocalName().toUpperCase()).myClazz;
} catch (IllegalArgumentException e) {
return null;
}
}
return XsltElementFactory.getInstance().wrapElement(tag, clazz);
}
}
@Override
@NotNull
public ID<String, Kind> getName() {
return NAME;
}
@Override
@NotNull
public DataIndexer<String, Kind, FileContent> getIndexer() {
return new DataIndexer<String, Kind, FileContent>() {
@Override
@NotNull
public Map<String, Kind> map(@NotNull FileContent inputData) {
CharSequence inputDataContentAsText = inputData.getContentAsText();
if (CharArrayUtil.indexOf(inputDataContentAsText, XsltSupport.XSLT_NS, 0) == -1) {
return Collections.emptyMap();
}
final HashMap<String, Kind> map = new HashMap<String, Kind>();
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 && XsltSupport.XSLT_NS.equals(nsURI)) {
if ("template".equals(name)) {
attributeHandler = new MyAttributeHandler(map, Kind.TEMPLATE);
} else if ("variable".equals(name)) {
attributeHandler = new MyAttributeHandler(map, Kind.VARIABLE);
} else if ("param".equals(name)) {
attributeHandler = new MyAttributeHandler(map, Kind.PARAM);
}
}
depth++;
}
@Override
public void endElement(String name, String nsPrefix, String nsURI) throws Exception {
attributeHandler = null;
depth--;
}
});
return map;
}
};
}
@NotNull
@Override
public DataExternalizer<Kind> getValueExternalizer() {
return new EnumDataDescriptor<Kind>(Kind.class);
}
@NotNull
@Override
public KeyDescriptor<String> getKeyDescriptor() {
return new EnumeratorStringDescriptor();
}
@NotNull
@Override
public FileBasedIndex.InputFilter getInputFilter() {
return new DefaultFileTypeSpecificInputFilter(StdFileTypes.XML) {
@Override
public boolean acceptInput(@NotNull VirtualFile file) {
return !(file.getFileSystem() instanceof JarFileSystem);
}
};
}
@Override
public boolean dependsOnFileContent() {
return true;
}
@Override
public int getVersion() {
return 0;
}
@SuppressWarnings({ "UnusedDeclaration" })
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 collector = new SymbolCollector(name, project, scope);
FileBasedIndex.getInstance().processValues(NAME, name, null, collector, scope);
return collector.getResult();
}
private static class MyAttributeHandler extends NanoXmlUtil.IXMLBuilderAdapter {
private final HashMap<String, Kind> myMap;
private final Kind myKind;
public MyAttributeHandler(HashMap<String, Kind> map, Kind k) {
myMap = map;
myKind = k;
}
@Override
public void addAttribute(String key, String nsPrefix, String nsURI, String value, String type) throws Exception {
if (key.equals("name") && (nsURI == null || nsURI.length() == 0) && value != null) {
if (myMap.put(value, myKind) != null) {
myMap.put(value, Kind.ANYTHING);
}
}
}
}
private static class SymbolCollector implements FileBasedIndex.ValueProcessor<Kind> {
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, Kind kind) {
if (myScope.contains(file)) {
final PsiFile psiFile = myMgr.findFile(file);
if (XsltSupport.isXsltFile(psiFile)) {
final XmlTag[] tags;
try {
final XmlTag root = ((XmlFile)psiFile).getDocument().getRootTag();
if (kind == Kind.ANYTHING) {
final XmlTag[] v = root.findSubTags("variable", XsltSupport.XSLT_NS);
final XmlTag[] p = root.findSubTags("param", XsltSupport.XSLT_NS);
final XmlTag[] t = root.findSubTags("template", XsltSupport.XSLT_NS);
tags = ArrayUtil.mergeArrays(ArrayUtil.mergeArrays(v, p), t);
} else {
tags = root.findSubTags(kind.name().toLowerCase(), XsltSupport.XSLT_NS);
}
} catch (NullPointerException e) {
// something is null, don't bother
return true;
}
for (XmlTag tag : tags) {
assert XsltSupport.isXsltTag(tag);
final XsltElement el = kind.wrap(tag);
if (el instanceof PsiNamedElement && el instanceof NavigationItem) {
if (myName.equals(((PsiNamedElement)el).getName())) {
myResult.add((NavigationItem)el);
}
}
}
}
}
return true;
}
public NavigationItem[] getResult() {
return myResult.toArray(new NavigationItem[myResult.size()]);
}
}
}