blob: a3c16b4f814f3bb86413d6431e40f02bc1c81f44 [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 com.intellij.xml.index;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlFile;
import com.intellij.util.NullableFunction;
import com.intellij.util.indexing.DataIndexer;
import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.indexing.FileContent;
import com.intellij.util.indexing.ID;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.text.CharArrayUtil;
import com.intellij.xml.util.XmlUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
import java.util.*;
/**
* @author Dmitry Avdeev
*/
public class XmlNamespaceIndex extends XmlIndex<XsdNamespaceBuilder> {
@Nullable
public static String getNamespace(@NotNull VirtualFile file, final Project project, PsiFile context) {
if (DumbService.isDumb(project) || (context != null && XmlUtil.isStubBuilding())) {
try {
return XsdNamespaceBuilder.computeNamespace(file.getInputStream());
}
catch (IOException e) {
return null;
}
}
final List<XsdNamespaceBuilder> list = FileBasedIndex.getInstance().getValues(NAME, file.getUrl(), createFilter(project));
return list.size() == 0 ? null : list.get(0).getNamespace();
}
public static List<IndexedRelevantResource<String, XsdNamespaceBuilder>> getResourcesByNamespace(String namespace,
@NotNull Project project,
@Nullable Module module) {
List<IndexedRelevantResource<String, XsdNamespaceBuilder>> resources =
IndexedRelevantResource.getResources(NAME, namespace, module, project, null);
Collections.sort(resources);
return resources;
}
public static List<IndexedRelevantResource<String, XsdNamespaceBuilder>> getAllResources(@Nullable final Module module,
@NotNull Project project,
@Nullable NullableFunction<List<IndexedRelevantResource<String, XsdNamespaceBuilder>>, IndexedRelevantResource<String, XsdNamespaceBuilder>> chooser) {
return IndexedRelevantResource.getAllResources(NAME, module, project, chooser);
}
public static final ID<String,XsdNamespaceBuilder> NAME = ID.create("XmlNamespaces");
@Override
@NotNull
public ID<String, XsdNamespaceBuilder> getName() {
return NAME;
}
@Override
@NotNull
public DataIndexer<String, XsdNamespaceBuilder, FileContent> getIndexer() {
return new DataIndexer<String, XsdNamespaceBuilder, FileContent>() {
@Override
@NotNull
public Map<String, XsdNamespaceBuilder> map(@NotNull final FileContent inputData) {
final XsdNamespaceBuilder builder;
if ("dtd".equals(inputData.getFile().getExtension())) {
builder = new XsdNamespaceBuilder(inputData.getFileName(), "", Collections.<String>emptyList());
}
else {
builder = XsdNamespaceBuilder.computeNamespace(CharArrayUtil.readerFromCharSequence(inputData.getContentAsText()));
}
final HashMap<String, XsdNamespaceBuilder> map = new HashMap<String, XsdNamespaceBuilder>(2);
String namespace = builder.getNamespace();
if (namespace != null) {
map.put(namespace, builder);
}
// so that we could get ns by file url (see getNamespace method above)
map.put(inputData.getFile().getUrl(), builder);
return map;
}
};
}
@NotNull
@Override
public DataExternalizer<XsdNamespaceBuilder> getValueExternalizer() {
return new DataExternalizer<XsdNamespaceBuilder>() {
@Override
public void save(@NotNull DataOutput out, XsdNamespaceBuilder value) throws IOException {
out.writeUTF(value.getNamespace() == null ? "" : value.getNamespace());
out.writeUTF(value.getVersion() == null ? "" : value.getVersion());
out.writeInt(value.getTags().size());
for (String s : value.getTags()) {
out.writeUTF(s);
}
}
@Override
public XsdNamespaceBuilder read(@NotNull DataInput in) throws IOException {
int count;
XsdNamespaceBuilder builder = new XsdNamespaceBuilder(in.readUTF(), in.readUTF(), new ArrayList<String>(count = in.readInt()));
for (int i = 0; i < count; i++) {
builder.getTags().add(in.readUTF());
}
return builder;
}
};
}
@Override
public int getVersion() {
return 2;
}
@Nullable
public static IndexedRelevantResource<String, XsdNamespaceBuilder> guessSchema(String namespace,
@Nullable final String tagName,
@Nullable final String version,
@Nullable Module module,
@NotNull Project project) {
final List<IndexedRelevantResource<String, XsdNamespaceBuilder>>
resources = getResourcesByNamespace(namespace, project, module);
if (resources.isEmpty()) return null;
return Collections
.max(resources, new Comparator<IndexedRelevantResource<String, XsdNamespaceBuilder>>() {
@Override
public int compare(IndexedRelevantResource<String, XsdNamespaceBuilder> o1,
IndexedRelevantResource<String, XsdNamespaceBuilder> o2) {
int i = o1.compareTo(o2);
return i == 0 ? o1.getValue().getRating(tagName, version) - o2.getValue().getRating(tagName, version) : i;
}
});
}
@Nullable
public static XmlFile guessSchema(String namespace,
@Nullable final String tagName,
@Nullable final String version,
@NotNull PsiFile file) {
if (DumbService.isDumb(file.getProject()) || XmlUtil.isStubBuilding()) return null;
IndexedRelevantResource<String,XsdNamespaceBuilder> resource =
guessSchema(namespace, tagName, version, ModuleUtilCore.findModuleForPsiElement(file), file.getProject());
if (resource == null) return null;
return findSchemaFile(resource.getFile(), file);
}
@Nullable
private static XmlFile findSchemaFile(VirtualFile resourceFile, PsiFile baseFile) {
PsiFile psiFile = baseFile.getManager().findFile(resourceFile);
return psiFile instanceof XmlFile ? (XmlFile)psiFile : null;
}
@Nullable
public static XmlFile guessDtd(String dtdUri, @NotNull PsiFile baseFile) {
if (!dtdUri.endsWith(".dtd") ||
DumbService.isDumb(baseFile.getProject()) ||
XmlUtil.isStubBuilding()) return null;
String dtdFileName = new File(dtdUri).getName();
List<IndexedRelevantResource<String, XsdNamespaceBuilder>>
list = getResourcesByNamespace(dtdFileName, baseFile.getProject(), ModuleUtilCore.findModuleForPsiElement(baseFile));
if (list.isEmpty()) {
return null;
}
IndexedRelevantResource<String, XsdNamespaceBuilder> resource;
if (list.size() > 1) {
final String[] split = dtdUri.split("/");
resource = Collections.max(list, new Comparator<IndexedRelevantResource<String, XsdNamespaceBuilder>>() {
@Override
public int compare(IndexedRelevantResource<String, XsdNamespaceBuilder> o1,
IndexedRelevantResource<String, XsdNamespaceBuilder> o2) {
return weight(o1) - weight(o2);
}
int weight(IndexedRelevantResource<String, XsdNamespaceBuilder> o1) {
VirtualFile file = o1.getFile();
for (int i = split.length - 1; i >= 0 && file != null; i--) {
String s = split[i];
if (!s.equals(file.getName())) {
return split.length - i;
}
file = file.getParent();
}
return 0;
}
});
}
else {
resource = list.get(0);
}
return findSchemaFile(resource.getFile(), baseFile);
}
}