package org.fandev.index;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.roots.libraries.LibraryTable;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.stubs.StubIndex;
import fan.sys.Pod;
import org.fandev.lang.fan.FanFileType;
import org.fandev.lang.fan.PodFileType;
import org.fandev.lang.fan.psi.FanFile;
import org.fandev.lang.fan.psi.stubs.index.FanShortClassNameIndex;
import org.fandev.lang.fan.psi.api.statements.typeDefs.FanTypeDefinition;
import org.fandev.module.FanModuleType;
import org.fandev.sdk.FanSdkType;
import org.fandev.utils.FanUtil;
import org.fandev.utils.VirtualFileUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author Dror Bereznitsky
* @date Mar 4, 2009 11:37:54 PM
*/
public class FanIndex implements ProjectComponent {
private final Project project;
private final PsiManager psiManager;
private final Map<String, VirtualFile> typeToFile = new HashMap<String, VirtualFile>();
private final Map<String, Set<String>> libNameToTypesSet = new HashMap<String, Set<String>>();
private final Map<String, String> podNameToLibName = new HashMap<String, String>();
private final Map<String, VirtualFile> podToBuildFile = new HashMap<String, VirtualFile>();
private static final Logger logger = Logger.getInstance("org.fandev.index.FanIndex");
public static final String COMPONENT_NAME = "Fantom Index";
public FanIndex(final Project project) {
this.project = project;
this.psiManager = PsiManager.getInstance(project);
}
public VirtualFile getVirtualFileByTypeName(@NotNull final String typeName) {
final Collection<FanTypeDefinition> typeDefs = getProjectTypes(typeName);
if (typeDefs != null && typeDefs.size() > 0) {
return typeDefs.iterator().next().getContainingFile().getVirtualFile();
}
return typeToFile.get(typeName);
}
public VirtualFile getVirtualFileByPodName(@NotNull final String podName) {
return podToBuildFile.get(podName);
}
@Nullable
public FanFile getFanFileByTypeName(@NotNull final String typeName) {
final VirtualFile typeFile = getVirtualFileByTypeName(typeName);
if (typeFile != null) {
return (FanFile) psiManager.findFile(typeFile);
}
return null;
}
public Set<String> getLibraryTypeNames(@NotNull final String libName) {
if (libNameToTypesSet.containsKey(libName)) {
return libNameToTypesSet.get(libName);
} else {
return new HashSet<String>();
}
}
public Set<String> getPodTypeNames(@NotNull final String podName) {
final String libName = podNameToLibName.get(podName);
if (libName != null && libNameToTypesSet.containsKey(libName)) {
return libNameToTypesSet.get(libName);
} else {
return new HashSet<String>();
}
}
public Set<String> getAllPodNames() {
return podToBuildFile.keySet();
}
@Nullable
public FanFile getFanFileByPodName(@NotNull final String podName) {
final VirtualFile typeFile = getVirtualFileByPodName(podName);
if (typeFile != null) {
return (FanFile) psiManager.findFile(typeFile);
}
return null;
}
public Set<FanTypeDefinition> getPodStartingWith(@NotNull final String prefix) {
final Set<FanTypeDefinition> matching = new HashSet<FanTypeDefinition>();
for (final String podName : getAllPodNames()) {
final FanFile file = getFanFileByPodName(podName);
for (final FanTypeDefinition def : file.getTypeDefinitions()) {
if (FanUtil.isFanBuildScript(def) && def.getName().startsWith(prefix)) {
matching.add(def);
}
}
}
return matching;
}
public Set<String> getAllTypeNames() {
final Set<String> allTypes = new HashSet<String>();
for (final Set<String> libTypes : libNameToTypesSet.values()) {
allTypes.addAll(libTypes);
}
return allTypes;
}
public Set<FanTypeDefinition> getAllTypesStartingWith(@NotNull final String prefix) {
final Set<FanTypeDefinition> matching = new HashSet<FanTypeDefinition>();
for (final String name : getAllTypeNames()) {
if (name.startsWith(prefix)) {
final FanFile typeFile = getFanFileByTypeName(name);
if (typeFile != null) {
for (final FanTypeDefinition def : typeFile.getTypeDefinitions()) {
if (def.getName().startsWith(prefix)) {
matching.add(def);
}
}
}
}
}
return matching;
}
public Set<FanTypeDefinition> getPodTypesStartingWith(@NotNull final String podName, @NotNull final String prefix) {
final Set<FanTypeDefinition> matching = new HashSet<FanTypeDefinition>();
for (final String name : getPodTypeNames(podName)) {
if (name.startsWith(prefix)) {
final FanFile typeFile = getFanFileByTypeName(name);
if (typeFile != null) {
for (final FanTypeDefinition def : typeFile.getTypeDefinitions()) {
if (def.getName().startsWith(prefix)) {
matching.add(def);
}
}
}
}
}
return matching;
}
public Set<VirtualFile> getLibraryVirtualFiles(@NotNull final String libName) {
final Set<String> types = getLibraryTypeNames(libName);
final Set<VirtualFile> files = new HashSet<VirtualFile>(types.size());
for (final String type : types) {
final VirtualFile file = getVirtualFileByTypeName(type);
if (file != null) {
files.add(file);
}
}
return files;
}
public Set<PsiFile> getLibraryPsiFiles(@NotNull final String libName) {
return getPsiFiles(getLibraryVirtualFiles(libName));
}
public Set<VirtualFile> getAllVirtualFiles() {
return new HashSet<VirtualFile>(typeToFile.values());
}
public Set<PsiFile> getAllPsiFiles() {
return getPsiFiles(getAllVirtualFiles());
}
private Set<PsiFile> getPsiFiles(final Set<VirtualFile> files) {
final Set<PsiFile> psiFiles = new HashSet<PsiFile>(files.size());
for (final VirtualFile file : files) {
final PsiFile psiFile = psiManager.findFile(file);
if (psiFile != null) {
psiFiles.add(psiFile);
}
}
return psiFiles;
}
private FanTypeDefinition getProjectType(@NotNull final String podName, @NotNull final String typeName) {
final Collection<FanTypeDefinition> types = getProjectTypes(typeName);
if (types.size() > 0) {
for (final FanTypeDefinition typeDef : types) {
if (typeName.equals(typeDef.getName()) && podName.equals(typeDef.getPodName())) {
return typeDef;
}
}
}
return null;
}
private Collection<FanTypeDefinition> getProjectTypes(@NotNull final String typeName) {
return StubIndex.getInstance().get(FanShortClassNameIndex.KEY, typeName, project, null);
}
public void projectOpened() {
final Module[] modules = ModuleManager.getInstance(project).getModules();
for (final Module module : modules) {
if (module.getModuleType() == FanModuleType.getInstance()) {
final Sdk sdk = FanUtil.getSdk(module);
if (sdk == null) {
logger.warn("Module " + module.getName() + " has no valid Fantom sdk");
continue;
}
FanUtil.setFanHome(sdk);
// Scan for Pod libraries
final ModifiableRootModel modifiableRootModel = ModuleRootManager.getInstance(module).getModifiableModel();
final LibraryTable libraryTable = modifiableRootModel.getModuleLibraryTable();
final Library[] libraries = libraryTable.getLibraries();
for (final Library library : libraries) {
indexLibrary(library);
}
}
}
}
public void indexLibrary(final Library library) {
logger.debug("Indexing library " + library.getName());
final String podName = library.getName().indexOf(' ') > 0 ? library.getName().substring(0, library.getName().indexOf(' ')) : library.getName();
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
final VirtualFile[] files = library.getFiles(OrderRootType.CLASSES);
for (final VirtualFile libFile : files) {
if (libFile.isDirectory()) {
// Nothing to do
} else if (libFile.getFileType() == PodFileType.POD_FILE_TYPE) {
try {
final VirtualFile jarFile = VirtualFileManager.getInstance().findFileByUrl(
JarFileSystem.PROTOCOL_PREFIX + libFile.getPath() + JarFileSystem.JAR_SEPARATOR);
final String libName = libFile.getName();
if (!libNameToTypesSet.containsKey(libName)) {
libNameToTypesSet.put(libName, new HashSet<String>());
}
final Set<String> libTypes = libNameToTypesSet.get(libName);
String podName;
if (libName.indexOf(".pod") > 0) {
podName = libName.substring(0, libName.indexOf(".pod"));
podNameToLibName.put(podName, libName);
} else {
logger.warn("Library " + libName + " is not a pod");
continue;
}
// Index Pod types
final Pod pod = Pod.find(podName);
if (pod != null) {
final VirtualFile[] srcFiles = library.getFiles(OrderRootType.SOURCES);
if (srcFiles.length > 0) {
final VirtualFile srcRoot = srcFiles[0];
final VirtualFile podBuildFile = srcRoot.findFileByRelativePath("build.fan"); // Assuming we have only one source lib
if (podBuildFile != null) {
podToBuildFile.put(podName, podBuildFile);
}
final fan.sys.List podTypes = pod.types();
for (int i = 0; i < podTypes.size(); i++) {
final fan.sys.Type type = (fan.sys.Type) podTypes.get(i);
final VirtualFile srcFilePath = srcRoot.findFileByRelativePath(
String.format("%s%s%s.%s", FanSdkType.getFanSrcDir(), VirtualFileUtil.VFS_PATH_SEPARATOR,
type.name(), FanFileType.DEFAULT_EXTENSION));
libTypes.add(type.name());
if (srcFilePath != null) {
typeToFile.put(type.name(), srcFilePath);
}
}
}
}
} catch (Exception e) {
logger.warn("Could not index library " + libFile.getPath() + ":" + e.getMessage());
}
}
}
}
});
}
public void projectClosed() {
typeToFile.clear();
libNameToTypesSet.clear();
podNameToLibName.clear();
}
@NotNull
public String getComponentName() {
return COMPONENT_NAME;
}
public void initComponent() {
}
public void disposeComponent() {
}
}