package org.dlangplugin.ide.index;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Processor;
import com.intellij.util.indexing.*;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.KeyDescriptor;
import gnu.trove.THashMap;
import org.dlangplugin.DFileType;
import org.dlangplugin.psi.DLangClassDeclaration;
import org.dlangplugin.psi.DLangResolveUtil;
import org.dlangplugin.psi.impl.DLangClassDeclarationImpl;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/* Index stores all DLang components (classes, templates, functions, etc) found in project and libraries.
* Index actually store instances of DLangComponentInfo class.
* Lookup is implemented by IDEA Platform using Map-Reduce system.
* TODO: Supports only classes for now. Add other components indexing. */
public class DLangComponentIndex extends FileBasedIndexExtension<String, DLangComponentInfo> {
public static final ID<String, DLangComponentInfo> DLANG_COMPONENT_INDEX = ID.create("DLangComponentInfo");
private static final int INDEX_VERSION = 1;
//TODO: Do indexing of other DLang components (methods, functions, templates, etc.)
private final DataIndexer<String, DLangComponentInfo, FileContent> myIndexer = new DLangClassIndexer();
private final DataExternalizer<DLangComponentInfo> myExternalizer = new DLangComponentInfoExternalizer();
@NotNull
@Override
public ID<String, DLangComponentInfo> getName() {
return DLANG_COMPONENT_INDEX;
}
@Override
public int getVersion() {
return INDEX_VERSION;
}
@Override
public boolean dependsOnFileContent() {
return true;
}
@NotNull
@Override
public DataIndexer<String, DLangComponentInfo, FileContent> getIndexer() {
return myIndexer;
}
@NotNull
@Override
public KeyDescriptor<String> getKeyDescriptor() {
return new EnumeratorStringDescriptor();
}
@NotNull
@Override
public DataExternalizer<DLangComponentInfo> getValueExternalizer() {
return myExternalizer;
}
@NotNull
@Override
public FileBasedIndex.InputFilter getInputFilter() {
return DLangSdkInputFilter.INSTANCE;
}
@NotNull
public static List<DLangClassDeclaration> getItemsByName(String name, Project project, GlobalSearchScope searchScope) {
Collection<VirtualFile> files =
FileBasedIndex.getInstance().getContainingFiles(DLANG_COMPONENT_INDEX, name, searchScope);
final List<DLangClassDeclaration> result = new ArrayList<DLangClassDeclaration>();
for (VirtualFile vFile : files) {
PsiFile file = PsiManager.getInstance(project).findFile(vFile);
if (file == null || file.getFileType() != DFileType.INSTANCE) {
continue;
}
final DLangClassDeclarationImpl component = DLangResolveUtil.findClassDeclaration(file, name);
if (component != null) {
result.add(component);
}
}
return result;
}
@NotNull
public static List<DLangClassDeclaration> getItemsByNameModule(@NotNull String name, @NotNull String moduleName,
Project project, GlobalSearchScope searchScope)
{
final String filterModuleName = moduleName;
final String filterClassName = name;
final Project filterProject = project;
final List<DLangClassDeclaration> result = new ArrayList<DLangClassDeclaration>();
//Collect all "DLangClassDeclaration" in result collection;
Processor<VirtualFile> processor = new Processor<VirtualFile>() {
@Override
public boolean process(VirtualFile virtualFile) {
PsiFile file = PsiManager.getInstance(filterProject).findFile(virtualFile);
final DLangClassDeclarationImpl component = DLangResolveUtil.findClassDeclaration(file, filterClassName);
if (component != null) {
result.add(component);
}
return true; //Continue processing
}
};
//Filter classes by moduleName and component type;
Condition<DLangComponentInfo> valueChecker = new Condition<DLangComponentInfo>() {
@Override
public boolean value(DLangComponentInfo componentInfoInfo) {
return componentInfoInfo.getType()==DLangComponentType.CLASS
&& filterModuleName.equals(componentInfoInfo.getModuleName());
}
};
//Do actual search;
FileBasedIndex.getInstance().processFilesContainingAllKeys(DLANG_COMPONENT_INDEX, Arrays.asList(name),
searchScope, valueChecker, processor);
return result;
}
@NotNull
public static Collection<String> getNames(Project project) {
return FileBasedIndex.getInstance().getAllKeys(DLANG_COMPONENT_INDEX, project);
}
/* Indexer responsible for extracting DLangComponentInfo from DLang source code.
* TODO: Supports only classes for now. Add other components indexing.
*/
class DLangClassIndexer implements DataIndexer<String, DLangComponentInfo, FileContent> {
@Override
@NotNull
public Map<String, DLangComponentInfo> map(@NotNull final FileContent inputData) {
final PsiFile psiFile = inputData.getPsiFile();
if (psiFile.getFileType() != DFileType.INSTANCE) {
return Collections.emptyMap();
}
final String moduleName = DLangResolveUtil.findModuleName(psiFile);
final Collection<DLangClassDeclarationImpl> classes = PsiTreeUtil.findChildrenOfType(psiFile, DLangClassDeclarationImpl.class);
if (classes.isEmpty()) {
return Collections.emptyMap();
}
final Map<String, DLangComponentInfo> result = new THashMap<String, DLangComponentInfo>(classes.size());
for (DLangClassDeclaration classDecl : classes) {
if (classDecl.getName() == null) {
continue;
}
final DLangComponentInfo info = new DLangComponentInfo(DLangComponentType.CLASS, classDecl.getName(), moduleName);
result.put(classDecl.getName(), info);
}
return result;
}
}
}