blob: a55f2a1e0b00caf8a7bc6478ac16fbfb3623e89d [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.openapi.roots.impl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.FileTypeRegistry;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.roots.impl.libraries.LibraryEx;
import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.LowMemoryWatcher;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
import com.intellij.util.CollectionQuery;
import com.intellij.util.EmptyQuery;
import com.intellij.util.Query;
import com.intellij.util.containers.ConcurrentHashSet;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.module.JpsModuleSourceRootType;
import java.util.*;
public class RootIndex {
public static final Comparator<OrderEntry> BY_OWNER_MODULE = new Comparator<OrderEntry>() {
@Override
public int compare(OrderEntry o1, OrderEntry o2) {
String name1 = o1.getOwnerModule().getName();
String name2 = o2.getOwnerModule().getName();
return name1.compareTo(name2);
}
};
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.roots.impl.RootIndex");
private final Set<VirtualFile> myProjectExcludedRoots = ContainerUtil.newHashSet();
private final MultiMap<String, VirtualFile> myPackagePrefixRoots = new MultiMap<String, VirtualFile>() {
@NotNull
@Override
protected Collection<VirtualFile> createCollection() {
return ContainerUtil.newLinkedHashSet();
}
};
private final Map<String, List<VirtualFile>> myDirectoriesByPackageNameCache = ContainerUtil.newConcurrentMap();
private final Set<String> myNonExistentPackages = new ConcurrentHashSet<String>();
private final InfoCache myInfoCache;
private final List<JpsModuleSourceRootType<?>> myRootTypes = ContainerUtil.newArrayList();
private final TObjectIntHashMap<JpsModuleSourceRootType<?>> myRootTypeId = new TObjectIntHashMap<JpsModuleSourceRootType<?>>();
@NotNull private final Project myProject;
private volatile Map<VirtualFile, OrderEntry[]> myOrderEntries;
@SuppressWarnings("UnusedDeclaration")
private final LowMemoryWatcher myLowMemoryWatcher = LowMemoryWatcher.register(new Runnable() {
@Override
public void run() {
myNonExistentPackages.clear();
}
});
// made public for Upsource
public RootIndex(@NotNull Project project, @NotNull InfoCache cache) {
myProject = project;
myInfoCache = cache;
final RootInfo info = buildRootInfo(project);
Set<VirtualFile> allRoots = info.getAllRoots();
for (VirtualFile root : allRoots) {
List<VirtualFile> hierarchy = getHierarchy(root, allRoots, info);
Pair<DirectoryInfo, String> pair = hierarchy != null
? calcDirectoryInfo(root, hierarchy, info)
: new Pair<DirectoryInfo, String>(NonProjectDirectoryInfo.IGNORED, null);
cacheInfos(root, root, pair.first);
myPackagePrefixRoots.putValue(pair.second, root);
if (info.shouldMarkAsProjectExcluded(root, hierarchy)) {
myProjectExcludedRoots.add(root);
}
}
}
@NotNull
private RootInfo buildRootInfo(@NotNull Project project) {
final RootInfo info = new RootInfo();
for (final Module module : ModuleManager.getInstance(project).getModules()) {
final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
for (final VirtualFile contentRoot : moduleRootManager.getContentRoots()) {
if (!info.contentRootOf.containsKey(contentRoot)) {
info.contentRootOf.put(contentRoot, module);
}
}
for (ContentEntry contentEntry : moduleRootManager.getContentEntries()) {
if (!(contentEntry instanceof ContentEntryImpl) || !((ContentEntryImpl)contentEntry).isDisposed()) {
for (VirtualFile excludeRoot : contentEntry.getExcludeFolderFiles()) {
info.excludedFromModule.put(excludeRoot, module);
}
}
// Init module sources
for (final SourceFolder sourceFolder : contentEntry.getSourceFolders()) {
final VirtualFile sourceFolderRoot = sourceFolder.getFile();
if (sourceFolderRoot != null) {
info.rootTypeId.put(sourceFolderRoot, getRootTypeId(sourceFolder.getRootType()));
info.classAndSourceRoots.add(sourceFolderRoot);
info.sourceRootOf.putValue(sourceFolderRoot, module);
info.packagePrefix.put(sourceFolderRoot, sourceFolder.getPackagePrefix());
}
}
}
for (OrderEntry orderEntry : moduleRootManager.getOrderEntries()) {
if (orderEntry instanceof LibraryOrSdkOrderEntry) {
final LibraryOrSdkOrderEntry entry = (LibraryOrSdkOrderEntry)orderEntry;
final VirtualFile[] sourceRoots = entry.getRootFiles(OrderRootType.SOURCES);
final VirtualFile[] classRoots = entry.getRootFiles(OrderRootType.CLASSES);
// Init library sources
for (final VirtualFile sourceRoot : sourceRoots) {
info.classAndSourceRoots.add(sourceRoot);
info.libraryOrSdkSources.add(sourceRoot);
info.packagePrefix.put(sourceRoot, "");
}
// init library classes
for (final VirtualFile classRoot : classRoots) {
info.classAndSourceRoots.add(classRoot);
info.libraryOrSdkClasses.add(classRoot);
info.packagePrefix.put(classRoot, "");
}
if (orderEntry instanceof LibraryOrderEntry) {
Library library = ((LibraryOrderEntry)orderEntry).getLibrary();
if (library != null) {
for (VirtualFile root : ((LibraryEx)library).getExcludedRoots()) {
info.excludedFromLibraries.putValue(root, library);
}
for (VirtualFile root : sourceRoots) {
info.sourceOfLibraries.putValue(root, library);
}
for (VirtualFile root : classRoots) {
info.classOfLibraries.putValue(root, library);
}
}
}
}
}
}
for (DirectoryIndexExcludePolicy policy : Extensions.getExtensions(DirectoryIndexExcludePolicy.EP_NAME, project)) {
Collections.addAll(info.excludedFromProject, policy.getExcludeRootsForProject());
}
return info;
}
@NotNull
private Map<VirtualFile, OrderEntry[]> getOrderEntries() {
Map<VirtualFile, OrderEntry[]> result = myOrderEntries;
if (result != null) return result;
MultiMap<VirtualFile, OrderEntry> libClassRootEntries = MultiMap.createSmartList();
MultiMap<VirtualFile, OrderEntry> libSourceRootEntries = MultiMap.createSmartList();
MultiMap<VirtualFile, OrderEntry> depEntries = MultiMap.createSmartList();
for (final Module module : ModuleManager.getInstance(myProject).getModules()) {
final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
for (OrderEntry orderEntry : moduleRootManager.getOrderEntries()) {
if (orderEntry instanceof ModuleOrderEntry) {
final Module depModule = ((ModuleOrderEntry)orderEntry).getModule();
if (depModule != null) {
VirtualFile[] importedClassRoots =
OrderEnumerator.orderEntries(depModule).exportedOnly().recursively().classes().usingCache().getRoots();
for (VirtualFile importedClassRoot : importedClassRoots) {
depEntries.putValue(importedClassRoot, orderEntry);
}
}
for (VirtualFile sourceRoot : orderEntry.getFiles(OrderRootType.SOURCES)) {
depEntries.putValue(sourceRoot, orderEntry);
}
}
else if (orderEntry instanceof LibraryOrSdkOrderEntry) {
final LibraryOrSdkOrderEntry entry = (LibraryOrSdkOrderEntry)orderEntry;
for (final VirtualFile sourceRoot : entry.getRootFiles(OrderRootType.SOURCES)) {
libSourceRootEntries.putValue(sourceRoot, orderEntry);
}
for (final VirtualFile classRoot : entry.getRootFiles(OrderRootType.CLASSES)) {
libClassRootEntries.putValue(classRoot, orderEntry);
}
}
}
}
RootInfo rootInfo = buildRootInfo(myProject);
result = ContainerUtil.newHashMap();
Set<VirtualFile> allRoots = rootInfo.getAllRoots();
for (VirtualFile file : allRoots) {
List<VirtualFile> hierarchy = getHierarchy(file, allRoots, rootInfo);
result.put(file, hierarchy == null
? OrderEntry.EMPTY_ARRAY
: calcOrderEntries(rootInfo, depEntries, libClassRootEntries, libSourceRootEntries, hierarchy));
}
myOrderEntries = result;
return result;
}
private static OrderEntry[] calcOrderEntries(@NotNull RootInfo info,
@NotNull MultiMap<VirtualFile, OrderEntry> depEntries,
@NotNull MultiMap<VirtualFile, OrderEntry> libClassRootEntries,
@NotNull MultiMap<VirtualFile, OrderEntry> libSourceRootEntries,
@NotNull List<VirtualFile> hierarchy) {
@Nullable VirtualFile libraryClassRoot = info.findLibraryRootInfo(hierarchy, false);
@Nullable VirtualFile librarySourceRoot = info.findLibraryRootInfo(hierarchy, true);
Set<OrderEntry> orderEntries = ContainerUtil.newLinkedHashSet();
orderEntries
.addAll(info.getLibraryOrderEntries(hierarchy, libraryClassRoot, librarySourceRoot, libClassRootEntries, libSourceRootEntries));
for (VirtualFile root : hierarchy) {
orderEntries.addAll(depEntries.get(root));
}
VirtualFile moduleContentRoot = info.findModuleRootInfo(hierarchy);
if (moduleContentRoot != null) {
ContainerUtil.addIfNotNull(orderEntries, info.getModuleSourceEntry(hierarchy, moduleContentRoot, libClassRootEntries));
}
if (orderEntries.isEmpty()) {
return null;
}
OrderEntry[] array = orderEntries.toArray(new OrderEntry[orderEntries.size()]);
Arrays.sort(array, BY_OWNER_MODULE);
return array;
}
public void checkConsistency() {
for (VirtualFile file : myProjectExcludedRoots) {
assert file.exists() : file.getPath() + " does not exist";
}
for (VirtualFile file : myPackagePrefixRoots.values()) {
assert file.exists() : file.getPath() + " does not exist";
}
}
private int getRootTypeId(@NotNull JpsModuleSourceRootType<?> rootType) {
if (myRootTypeId.containsKey(rootType)) {
return myRootTypeId.get(rootType);
}
int id = myRootTypes.size();
if (id > DirectoryInfoImpl.MAX_ROOT_TYPE_ID) {
LOG.error("Too many different types of module source roots (" + id + ") registered: " + myRootTypes);
}
myRootTypes.add(rootType);
myRootTypeId.put(rootType, id);
return id;
}
@NotNull
public DirectoryInfo getInfoForFile(@NotNull VirtualFile file) {
if (!file.isValid()) {
return NonProjectDirectoryInfo.INVALID;
}
VirtualFile dir;
if (!file.isDirectory()) {
DirectoryInfo info = myInfoCache.getCachedInfo(file);
if (info != null) {
return info;
}
if (isIgnored(file)) {
return NonProjectDirectoryInfo.IGNORED;
}
dir = file.getParent();
}
else {
dir = file;
}
int count = 0;
for (VirtualFile root = dir; root != null; root = root.getParent()) {
if (++count > 1000) {
throw new IllegalStateException("Possible loop in tree, started at " + dir.getName());
}
DirectoryInfo info = myInfoCache.getCachedInfo(root);
if (info != null) {
if (!dir.equals(root)) {
cacheInfos(dir, root, info);
}
return info;
}
if (isIgnored(root)) {
return cacheInfos(dir, root, NonProjectDirectoryInfo.IGNORED);
}
}
return cacheInfos(dir, null, NonProjectDirectoryInfo.NOT_UNDER_PROJECT_ROOTS);
}
@NotNull
private DirectoryInfo cacheInfos(VirtualFile dir, @Nullable VirtualFile stopAt, @NotNull DirectoryInfo info) {
while (dir != null) {
myInfoCache.cacheInfo(dir, info);
if (dir.equals(stopAt)) {
break;
}
dir = dir.getParent();
}
return info;
}
@NotNull
public Query<VirtualFile> getDirectoriesByPackageName(@NotNull final String packageName, final boolean includeLibrarySources) {
List<VirtualFile> result = myDirectoriesByPackageNameCache.get(packageName);
if (result == null) {
if (myNonExistentPackages.contains(packageName)) return EmptyQuery.getEmptyQuery();
result = ContainerUtil.newSmartList();
if (StringUtil.isNotEmpty(packageName) && !StringUtil.startsWithChar(packageName, '.')) {
int i = packageName.lastIndexOf('.');
while (true) {
String shortName = packageName.substring(i + 1);
String parentPackage = i > 0 ? packageName.substring(0, i) : "";
for (VirtualFile parentDir : getDirectoriesByPackageName(parentPackage, true)) {
VirtualFile child = parentDir.findChild(shortName);
if (child != null && child.isDirectory() && getInfoForFile(child).isInProject()
&& packageName.equals(getPackageName(child))) {
result.add(child);
}
}
if (i < 0) break;
i = packageName.lastIndexOf('.', i - 1);
}
}
for (VirtualFile file : myPackagePrefixRoots.get(packageName)) {
if (file.isDirectory()) {
result.add(file);
}
}
if (!result.isEmpty()) {
myDirectoriesByPackageNameCache.put(packageName, result);
} else {
myNonExistentPackages.add(packageName);
}
}
if (!includeLibrarySources) {
result = ContainerUtil.filter(result, new Condition<VirtualFile>() {
@Override
public boolean value(VirtualFile file) {
DirectoryInfo info = getInfoForFile(file);
return info.isInProject() && (!info.isInLibrarySource() || info.isInModuleSource() || info.hasLibraryClassRoot());
}
});
}
return new CollectionQuery<VirtualFile>(result);
}
@Nullable
public String getPackageName(@NotNull final VirtualFile dir) {
if (dir.isDirectory()) {
if (isIgnored(dir)) {
return null;
}
for (final Map.Entry<String, Collection<VirtualFile>> entry : myPackagePrefixRoots.entrySet()) {
if (entry.getValue().contains(dir)) {
return entry.getKey();
}
}
final VirtualFile parent = dir.getParent();
if (parent != null) {
return getPackageNameForSubdir(getPackageName(parent), dir.getName());
}
}
return null;
}
@Nullable
protected static String getPackageNameForSubdir(String parentPackageName, @NotNull String subdirName) {
if (parentPackageName == null) return null;
return parentPackageName.isEmpty() ? subdirName : parentPackageName + "." + subdirName;
}
@Nullable
public JpsModuleSourceRootType<?> getSourceRootType(@NotNull DirectoryInfo directoryInfo) {
return myRootTypes.get(directoryInfo.getSourceRootTypeId());
}
boolean resetOnEvents(@NotNull List<? extends VFileEvent> events) {
for (VFileEvent event : events) {
VirtualFile file = event.getFile();
if (file == null || file.isDirectory()) {
return true;
}
}
return false;
}
@Nullable("returns null only if dir is under ignored folder")
private static List<VirtualFile> getHierarchy(VirtualFile dir, @NotNull Set<VirtualFile> allRoots, @NotNull RootInfo info) {
List<VirtualFile> hierarchy = ContainerUtil.newArrayList();
boolean hasContentRoots = false;
while (dir != null) {
hasContentRoots |= info.contentRootOf.get(dir) != null;
if (!hasContentRoots && isIgnored(dir)) {
return null;
}
if (allRoots.contains(dir)) {
hierarchy.add(dir);
}
dir = dir.getParent();
}
return hierarchy;
}
private static boolean isIgnored(@NotNull VirtualFile dir) {
return FileTypeRegistry.getInstance().isFileIgnored(dir);
}
private static class RootInfo {
// getDirectoriesByPackageName used to be in this order, some clients might rely on that
@NotNull final LinkedHashSet<VirtualFile> classAndSourceRoots = ContainerUtil.newLinkedHashSet();
@NotNull final Set<VirtualFile> libraryOrSdkSources = ContainerUtil.newHashSet();
@NotNull final Set<VirtualFile> libraryOrSdkClasses = ContainerUtil.newHashSet();
@NotNull final Map<VirtualFile, Module> contentRootOf = ContainerUtil.newHashMap();
@NotNull final MultiMap<VirtualFile, Module> sourceRootOf = MultiMap.createSet();
@NotNull final TObjectIntHashMap<VirtualFile> rootTypeId = new TObjectIntHashMap<VirtualFile>();
@NotNull final MultiMap<VirtualFile, Library> excludedFromLibraries = MultiMap.createSmartList();
@NotNull final MultiMap<VirtualFile, Library> classOfLibraries = MultiMap.createSmartList();
@NotNull final MultiMap<VirtualFile, Library> sourceOfLibraries = MultiMap.createSmartList();
@NotNull final Set<VirtualFile> excludedFromProject = ContainerUtil.newHashSet();
@NotNull final Map<VirtualFile, Module> excludedFromModule = ContainerUtil.newHashMap();
@NotNull final Map<VirtualFile, String> packagePrefix = ContainerUtil.newHashMap();
@NotNull
Set<VirtualFile> getAllRoots() {
LinkedHashSet<VirtualFile> result = ContainerUtil.newLinkedHashSet();
result.addAll(classAndSourceRoots);
result.addAll(contentRootOf.keySet());
result.addAll(excludedFromLibraries.keySet());
result.addAll(excludedFromModule.keySet());
result.addAll(excludedFromProject);
return result;
}
private boolean shouldMarkAsProjectExcluded(@NotNull VirtualFile root, @Nullable List<VirtualFile> hierarchy) {
if (hierarchy == null) return false;
if (!excludedFromProject.contains(root) && !excludedFromModule.containsKey(root)) return false;
return ContainerUtil.find(hierarchy, new Condition<VirtualFile>() {
@Override
public boolean value(VirtualFile ancestor) {
return contentRootOf.containsKey(ancestor);
}
}) == null;
}
@Nullable
private VirtualFile findModuleRootInfo(@NotNull List<VirtualFile> hierarchy) {
for (VirtualFile root : hierarchy) {
Module module = contentRootOf.get(root);
Module excludedFrom = excludedFromModule.get(root);
if (module != null && excludedFrom != module) {
return root;
}
if (excludedFrom != null || excludedFromProject.contains(root)) {
return null;
}
}
return null;
}
@Nullable
private VirtualFile findNearestContentRootForExcluded(@NotNull List<VirtualFile> hierarchy) {
for (VirtualFile root : hierarchy) {
if (contentRootOf.containsKey(root)) {
return root;
}
}
return null;
}
@Nullable
private VirtualFile findLibraryRootInfo(@NotNull List<VirtualFile> hierarchy, boolean source) {
Set<Library> librariesToIgnore = ContainerUtil.newHashSet();
for (VirtualFile root : hierarchy) {
librariesToIgnore.addAll(excludedFromLibraries.get(root));
if (source && libraryOrSdkSources.contains(root) &&
(!sourceOfLibraries.containsKey(root) || !librariesToIgnore.containsAll(sourceOfLibraries.get(root)))) {
return root;
}
else if (!source && libraryOrSdkClasses.contains(root) &&
(!classOfLibraries.containsKey(root) || !librariesToIgnore.containsAll(classOfLibraries.get(root)))) {
return root;
}
}
return null;
}
private String calcPackagePrefix(@NotNull VirtualFile root,
@NotNull List<VirtualFile> hierarchy,
VirtualFile moduleContentRoot,
VirtualFile libraryClassRoot,
VirtualFile librarySourceRoot) {
VirtualFile packageRoot = findPackageRootInfo(hierarchy, moduleContentRoot, libraryClassRoot, librarySourceRoot);
String prefix = packagePrefix.get(packageRoot);
if (prefix != null && !root.equals(packageRoot)) {
assert packageRoot != null;
String relative = VfsUtilCore.getRelativePath(root, packageRoot, '.');
prefix = StringUtil.isEmpty(prefix) ? relative : prefix + '.' + relative;
}
return prefix;
}
@Nullable
private VirtualFile findPackageRootInfo(@NotNull List<VirtualFile> hierarchy,
VirtualFile moduleContentRoot,
VirtualFile libraryClassRoot,
VirtualFile librarySourceRoot) {
for (VirtualFile root : hierarchy) {
if (moduleContentRoot != null &&
sourceRootOf.get(root).contains(contentRootOf.get(moduleContentRoot)) &&
librarySourceRoot == null) {
return root;
}
if (root.equals(libraryClassRoot) || root.equals(librarySourceRoot)) {
return root;
}
if (root.equals(moduleContentRoot) && !sourceRootOf.containsKey(root) && librarySourceRoot == null && libraryClassRoot == null) {
return null;
}
}
return null;
}
@NotNull
private LinkedHashSet<OrderEntry> getLibraryOrderEntries(@NotNull List<VirtualFile> hierarchy,
@Nullable VirtualFile libraryClassRoot,
@Nullable VirtualFile librarySourceRoot,
@NotNull MultiMap<VirtualFile, OrderEntry> libClassRootEntries,
@NotNull MultiMap<VirtualFile, OrderEntry> libSourceRootEntries) {
LinkedHashSet<OrderEntry> orderEntries = ContainerUtil.newLinkedHashSet();
for (VirtualFile root : hierarchy) {
if (root.equals(libraryClassRoot) && !sourceRootOf.containsKey(root)) {
orderEntries.addAll(libClassRootEntries.get(root));
}
if (root.equals(librarySourceRoot) && libraryClassRoot == null) {
orderEntries.addAll(libSourceRootEntries.get(root));
}
if (libClassRootEntries.containsKey(root) || sourceRootOf.containsKey(root) && librarySourceRoot == null) {
break;
}
}
return orderEntries;
}
@Nullable
private ModuleSourceOrderEntry getModuleSourceEntry(@NotNull List<VirtualFile> hierarchy,
@NotNull VirtualFile moduleContentRoot,
@NotNull MultiMap<VirtualFile, OrderEntry> libClassRootEntries) {
Module module = contentRootOf.get(moduleContentRoot);
for (VirtualFile root : hierarchy) {
if (sourceRootOf.get(root).contains(module)) {
return ContainerUtil.findInstance(ModuleRootManager.getInstance(module).getOrderEntries(), ModuleSourceOrderEntry.class);
}
if (libClassRootEntries.containsKey(root)) {
return null;
}
}
return null;
}
}
@NotNull
private static Pair<DirectoryInfo, String> calcDirectoryInfo(@NotNull final VirtualFile root,
@NotNull final List<VirtualFile> hierarchy,
@NotNull RootInfo info) {
VirtualFile moduleContentRoot = info.findModuleRootInfo(hierarchy);
VirtualFile libraryClassRoot = info.findLibraryRootInfo(hierarchy, false);
VirtualFile librarySourceRoot = info.findLibraryRootInfo(hierarchy, true);
boolean inProject = moduleContentRoot != null || libraryClassRoot != null || librarySourceRoot != null;
VirtualFile nearestContentRoot;
if (inProject) {
nearestContentRoot = moduleContentRoot;
}
else {
nearestContentRoot = info.findNearestContentRootForExcluded(hierarchy);
if (nearestContentRoot == null) {
return new Pair<DirectoryInfo, String>(NonProjectDirectoryInfo.EXCLUDED, null);
}
}
VirtualFile sourceRoot = info.findPackageRootInfo(hierarchy, moduleContentRoot, null, librarySourceRoot);
VirtualFile moduleSourceRoot = info.findPackageRootInfo(hierarchy, moduleContentRoot, null, null);
boolean inModuleSources = moduleSourceRoot != null;
boolean inLibrarySource = librarySourceRoot != null;
int typeId = moduleSourceRoot != null ? info.rootTypeId.get(moduleSourceRoot) : 0;
Module module = info.contentRootOf.get(nearestContentRoot);
DirectoryInfo directoryInfo =
new DirectoryInfoImpl(root, module, nearestContentRoot, sourceRoot, libraryClassRoot, inModuleSources, inLibrarySource, !inProject, typeId);
String packagePrefix = info.calcPackagePrefix(root, hierarchy, moduleContentRoot, libraryClassRoot, librarySourceRoot);
return Pair.create(directoryInfo, packagePrefix);
}
@NotNull
public OrderEntry[] getOrderEntries(@NotNull DirectoryInfo info) {
if (!(info instanceof DirectoryInfoImpl)) return OrderEntry.EMPTY_ARRAY;
OrderEntry[] entries = this.getOrderEntries().get(((DirectoryInfoImpl)info).getRoot());
return entries == null ? OrderEntry.EMPTY_ARRAY : entries;
}
public interface InfoCache {
@Nullable
DirectoryInfo getCachedInfo(@NotNull VirtualFile dir);
void cacheInfo(@NotNull VirtualFile dir, @NotNull DirectoryInfo info);
}
}