free the memory used to build RootIndex

This commit is contained in:
peter
2013-12-20 18:24:20 +01:00
parent b83ecae0bd
commit ec01e468b1
@@ -46,7 +46,6 @@ class RootIndex {
private static final DirectoryInfo NULL_INFO = DirectoryInfo.createNew();
private final Set<VirtualFile> myProjectExcludedRoots = ContainerUtil.newHashSet();
private final RootInfo myRoots = new RootInfo();
private final MultiMap<String, VirtualFile> myPackagePrefixRoots = new MultiMap<String, VirtualFile>() {
@Override
protected Collection<VirtualFile> createCollection() {
@@ -61,6 +60,22 @@ class RootIndex {
private final TObjectIntHashMap<JpsModuleSourceRootType<?>> myRootTypeId = new TObjectIntHashMap<JpsModuleSourceRootType<?>>();
RootIndex(@NotNull final Project project) {
final RootInfo info = buildRootInfo(project);
Set<VirtualFile> allRoots = info.getAllRoots();
for (VirtualFile root : allRoots) {
List<VirtualFile> hierarchy = getHierarchy(root, allRoots);
Pair<DirectoryInfo, String> pair = hierarchy == null ? new Pair<DirectoryInfo, String>(null, null) : info.calcDirectoryInfo(root, hierarchy);
cacheInfos(root, root, pair.first);
myPackagePrefixRoots.putValue(pair.second, root);
if (info.shouldMarkAsProjectExcluded(root, hierarchy)) {
myProjectExcludedRoots.add(root);
}
}
}
private RootInfo buildRootInfo(Project project) {
final RootInfo info = new RootInfo();
for (final Module module : ModuleManager.getInstance(project).getModules()) {
final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
final OrderEntry[] orderEntries = moduleRootManager.getOrderEntries();
@@ -68,24 +83,24 @@ class RootIndex {
final VirtualFile[] contentRoots = moduleRootManager.getContentRoots();
for (final VirtualFile contentRoot : contentRoots) {
if (!myRoots.contentRootOf.containsKey(contentRoot)) {
myRoots.contentRootOf.put(contentRoot, module);
if (!info.contentRootOf.containsKey(contentRoot)) {
info.contentRootOf.put(contentRoot, module);
}
}
for (ContentEntry contentEntry : contentEntries) {
for (VirtualFile excludeRoot : contentEntry.getExcludeFolderFiles()) {
myRoots.excludedFromModule.put(excludeRoot, module);
info.excludedFromModule.put(excludeRoot, module);
}
// Init module sources
for (final SourceFolder sourceFolder : contentEntry.getSourceFolders()) {
final VirtualFile sourceFolderRoot = sourceFolder.getFile();
if (sourceFolderRoot != null) {
myRoots.rootTypeId.put(sourceFolderRoot, getRootTypeId(sourceFolder.getRootType()));
myRoots.classAndSourceRoots.add(sourceFolderRoot);
myRoots.sourceRootOf.putValue(sourceFolderRoot, module);
myRoots.packagePrefix.put(sourceFolderRoot, sourceFolder.getPackagePrefix());
info.rootTypeId.put(sourceFolderRoot, getRootTypeId(sourceFolder.getRootType()));
info.classAndSourceRoots.add(sourceFolderRoot);
info.sourceRootOf.putValue(sourceFolderRoot, module);
info.packagePrefix.put(sourceFolderRoot, sourceFolder.getPackagePrefix());
}
}
}
@@ -97,11 +112,11 @@ class RootIndex {
if (depModule != null) {
VirtualFile[] importedClassRoots = OrderEnumerator.orderEntries(depModule).exportedOnly().recursively().classes().usingCache().getRoots();
for (VirtualFile importedClassRoot : importedClassRoots) {
myRoots.depEntries.putValue(importedClassRoot, orderEntry);
info.depEntries.putValue(importedClassRoot, orderEntry);
}
}
for (VirtualFile sourceRoot : orderEntry.getFiles(OrderRootType.SOURCES)) {
myRoots.depEntries.putValue(sourceRoot, orderEntry);
info.depEntries.putValue(sourceRoot, orderEntry);
}
}
else if (orderEntry instanceof LibraryOrSdkOrderEntry) {
@@ -111,29 +126,29 @@ class RootIndex {
// Init library sources
for (final VirtualFile sourceRoot : sourceRoots) {
myRoots.classAndSourceRoots.add(sourceRoot);
myRoots.libSourceRootEntries.putValue(sourceRoot, orderEntry);
myRoots.packagePrefix.put(sourceRoot, "");
info.classAndSourceRoots.add(sourceRoot);
info.libSourceRootEntries.putValue(sourceRoot, orderEntry);
info.packagePrefix.put(sourceRoot, "");
}
// init library classes
for (final VirtualFile classRoot : classRoots) {
myRoots.classAndSourceRoots.add(classRoot);
myRoots.libClassRootEntries.putValue(classRoot, orderEntry);
myRoots.packagePrefix.put(classRoot, "");
info.classAndSourceRoots.add(classRoot);
info.libClassRootEntries.putValue(classRoot, orderEntry);
info.packagePrefix.put(classRoot, "");
}
if (orderEntry instanceof LibraryOrderEntry) {
Library library = ((LibraryOrderEntry)orderEntry).getLibrary();
if (library != null) {
for (VirtualFile root : ((LibraryEx)library).getExcludedRoots()) {
myRoots.excludedFromLibraries.putValue(root, library);
info.excludedFromLibraries.putValue(root, library);
}
for (VirtualFile root : sourceRoots) {
myRoots.sourceOfLibraries.putValue(root, library);
info.sourceOfLibraries.putValue(root, library);
}
for (VirtualFile root : classRoots) {
myRoots.classOfLibraries.putValue(root, library);
info.classOfLibraries.putValue(root, library);
}
}
}
@@ -143,30 +158,9 @@ class RootIndex {
}
for (DirectoryIndexExcludePolicy policy : Extensions.getExtensions(DirectoryIndexExcludePolicy.EP_NAME, project)) {
Collections.addAll(myRoots.excludedFromProject, policy.getExcludeRootsForProject());
Collections.addAll(info.excludedFromProject, policy.getExcludeRootsForProject());
}
Set<VirtualFile> allRoots = myRoots.getAllRoots();
for (VirtualFile root : allRoots) {
List<VirtualFile> hierarchy = getHierarchy(root, allRoots);
Pair<DirectoryInfo, String> pair = hierarchy == null ? new Pair<DirectoryInfo, String>(null, null) : calcDirectoryInfo(root, hierarchy);
cacheInfos(root, root, pair.first);
myPackagePrefixRoots.putValue(pair.second, root);
if (shouldMarkAsProjectExcluded(root, hierarchy)) {
myProjectExcludedRoots.add(root);
}
}
}
private boolean shouldMarkAsProjectExcluded(VirtualFile root, @Nullable List<VirtualFile> hierarchy) {
if (hierarchy == null) return false;
if (!myRoots.excludedFromProject.contains(root) && !myRoots.excludedFromModule.containsKey(root)) return false;
return ContainerUtil.find(hierarchy, new Condition<VirtualFile>() {
@Override
public boolean value(VirtualFile ancestor) {
return myRoots.contentRootOf.containsKey(ancestor);
}
}) == null;
return info;
}
public void checkConsistency() {
@@ -174,10 +168,6 @@ class RootIndex {
assert file.exists() : file.getPath() + " does not exist";
}
for (VirtualFile file : myRoots.getAllRoots()) {
assert file.exists() : file.getPath() + " does not exist";
}
for (VirtualFile file : myPackagePrefixRoots.values()) {
assert file.exists() : file.getPath() + " does not exist";
}
@@ -337,80 +327,6 @@ class RootIndex {
return true;
}
@Nullable
private VirtualFile findModuleRootInfo(List<VirtualFile> hierarchy) {
for (VirtualFile root : hierarchy) {
Module module = myRoots.contentRootOf.get(root);
Module excludedFromModule = myRoots.excludedFromModule.get(root);
if (module != null && excludedFromModule != module) {
return root;
}
if (myRoots.excludedFromProject.contains(root) || excludedFromModule != null || !root.isDirectory()) {
return null;
}
}
return null;
}
@Nullable
private VirtualFile findLibraryRootInfo(List<VirtualFile> hierarchy, boolean source) {
Set<Library> excludedFromLibraries = ContainerUtil.newHashSet();
for (VirtualFile root : hierarchy) {
excludedFromLibraries.addAll(myRoots.excludedFromLibraries.get(root));
if (source && myRoots.libSourceRootEntries.containsKey(root) &&
(!myRoots.sourceOfLibraries.containsKey(root) || !excludedFromLibraries.containsAll(myRoots.sourceOfLibraries.get(root)))) {
return root;
} else if (!source && myRoots.libClassRootEntries.containsKey(root) &&
(!myRoots.classOfLibraries.containsKey(root) || !excludedFromLibraries.containsAll(myRoots.classOfLibraries.get(root)))) {
return root;
}
}
return null;
}
@NotNull
private Pair<DirectoryInfo, String> calcDirectoryInfo(VirtualFile root, @NotNull final List<VirtualFile> hierarchy) {
VirtualFile moduleContentRoot = findModuleRootInfo(hierarchy);
VirtualFile libraryClassRoot = findLibraryRootInfo(hierarchy, false);
VirtualFile librarySourceRoot = findLibraryRootInfo(hierarchy, true);
if (moduleContentRoot == null && libraryClassRoot == null && librarySourceRoot == null) {
return new Pair<DirectoryInfo, String>(null, null);
}
VirtualFile sourceRoot = findPackageRootInfo(hierarchy, moduleContentRoot, null, librarySourceRoot);
VirtualFile moduleSourceRoot = findPackageRootInfo(hierarchy, moduleContentRoot, null, null);
boolean inModuleSources = moduleSourceRoot != null;
boolean inLibrarySource = librarySourceRoot != null;
int rootTypeId = moduleSourceRoot != null ? myRoots.rootTypeId.get(moduleSourceRoot) : 0;
OrderEntry[] entries = getOrderEntries(hierarchy, moduleContentRoot, libraryClassRoot, librarySourceRoot);
DirectoryInfo directoryInfo = new DirectoryInfo(myRoots.contentRootOf.get(moduleContentRoot),
moduleContentRoot,
sourceRoot,
libraryClassRoot,
(byte)DirectoryInfo.createSourceRootTypeData(inModuleSources, inLibrarySource, rootTypeId),
entries);
String packagePrefix = calcPackagePrefix(root, hierarchy, moduleContentRoot, libraryClassRoot, librarySourceRoot);
return Pair.create(directoryInfo, packagePrefix);
}
private String calcPackagePrefix(VirtualFile root,
List<VirtualFile> hierarchy,
VirtualFile moduleContentRoot,
VirtualFile libraryClassRoot, VirtualFile librarySourceRoot) {
VirtualFile packageRoot = findPackageRootInfo(hierarchy, moduleContentRoot, libraryClassRoot, librarySourceRoot);
String packagePrefix = myRoots.packagePrefix.get(packageRoot);
if (packagePrefix != null && packageRoot != root) {
assert packageRoot != null;
String relative = VfsUtilCore.getRelativePath(root, packageRoot, '.');
packagePrefix = StringUtil.isEmpty(packagePrefix) ? relative : packagePrefix + '.' + relative;
}
return packagePrefix;
}
@Nullable
private static List<VirtualFile> getHierarchy(VirtualFile dir, Set<VirtualFile> allRoots) {
List<VirtualFile> hierarchy = ContainerUtil.newArrayList();
@@ -426,88 +342,10 @@ class RootIndex {
return hierarchy;
}
@Nullable
private VirtualFile findPackageRootInfo(List<VirtualFile> hierarchy,
VirtualFile moduleContentRoot,
VirtualFile libraryClassRoot,
VirtualFile librarySourceRoot) {
for (VirtualFile root : hierarchy) {
if (moduleContentRoot != null &&
myRoots.sourceRootOf.get(root).contains(myRoots.contentRootOf.get(moduleContentRoot)) &&
librarySourceRoot == null) {
return root;
}
if (root == libraryClassRoot || root == librarySourceRoot) {
return root;
}
if (root == moduleContentRoot && !myRoots.sourceRootOf.containsKey(root) && librarySourceRoot == null && libraryClassRoot == null) {
return null;
}
}
return null;
}
private LinkedHashSet<OrderEntry> getDependencyOrderEntries(List<VirtualFile> hierarchy) {
LinkedHashSet<OrderEntry> orderEntries = ContainerUtil.newLinkedHashSet();
for (VirtualFile root : hierarchy) {
orderEntries.addAll(myRoots.depEntries.get(root));
}
return orderEntries;
}
private LinkedHashSet<OrderEntry> getLibraryOrderEntries(List<VirtualFile> hierarchy,
@Nullable VirtualFile libraryClassRoot,
@Nullable VirtualFile librarySourceRoot) {
LinkedHashSet<OrderEntry> orderEntries = ContainerUtil.newLinkedHashSet();
for (VirtualFile root : hierarchy) {
if (root == libraryClassRoot && !myRoots.sourceRootOf.containsKey(root)) {
orderEntries.addAll(myRoots.libClassRootEntries.get(root));
}
if (root == librarySourceRoot && libraryClassRoot == null) {
orderEntries.addAll(myRoots.libSourceRootEntries.get(root));
}
if (myRoots.libClassRootEntries.containsKey(root) || myRoots.sourceRootOf.containsKey(root) && librarySourceRoot == null) {
break;
}
}
return orderEntries;
}
@Nullable
private ModuleSourceOrderEntry getModuleSourceEntry(List<VirtualFile> hierarchy, @NotNull VirtualFile moduleContentRoot) {
Module module = myRoots.contentRootOf.get(moduleContentRoot);
for (VirtualFile root : hierarchy) {
if (myRoots.sourceRootOf.get(root).contains(module)) {
return ContainerUtil.findInstance(ModuleRootManager.getInstance(module).getOrderEntries(), ModuleSourceOrderEntry.class);
}
if (myRoots.libClassRootEntries.containsKey(root)) {
return null;
}
}
return null;
}
private OrderEntry[] getOrderEntries(List<VirtualFile> hierarchy,
@Nullable VirtualFile moduleContentRoot,
@Nullable VirtualFile libraryClassRoot,
@Nullable VirtualFile librarySourceRoot) {
Set<OrderEntry> orderEntries = ContainerUtil.newLinkedHashSet();
orderEntries.addAll(getLibraryOrderEntries(hierarchy, libraryClassRoot, librarySourceRoot));
orderEntries.addAll(getDependencyOrderEntries(hierarchy));
if (moduleContentRoot != null) {
ContainerUtil.addIfNotNull(orderEntries, getModuleSourceEntry(hierarchy, moduleContentRoot));
}
if (orderEntries.isEmpty()) {
return null;
}
OrderEntry[] array = orderEntries.toArray(new OrderEntry[orderEntries.size()]);
Arrays.sort(array, DirectoryInfo.BY_OWNER_MODULE);
return array;
}
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 Map<VirtualFile, Module> contentRootOf = ContainerUtil.newHashMap();
@NotNull final MultiMap<VirtualFile, Module> sourceRootOf = MultiMapBasedOnSet.createBasedOnSet();
@NotNull final TObjectIntHashMap<VirtualFile> rootTypeId = new TObjectIntHashMap<VirtualFile>();
@@ -530,7 +368,171 @@ class RootIndex {
result.addAll(excludedFromProject);
return result;
}
private boolean shouldMarkAsProjectExcluded(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(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 (excludedFromProject.contains(root) || excludedFrom != null || !root.isDirectory()) {
return null;
}
}
return null;
}
@Nullable
private VirtualFile findLibraryRootInfo(List<VirtualFile> hierarchy, boolean source) {
Set<Library> librariesToIgnore = ContainerUtil.newHashSet();
for (VirtualFile root : hierarchy) {
librariesToIgnore.addAll(excludedFromLibraries.get(root));
if (source && libSourceRootEntries.containsKey(root) &&
(!sourceOfLibraries.containsKey(root) || !librariesToIgnore.containsAll(sourceOfLibraries.get(root)))) {
return root;
} else if (!source && libClassRootEntries.containsKey(root) &&
(!classOfLibraries.containsKey(root) || !librariesToIgnore.containsAll(classOfLibraries.get(root)))) {
return root;
}
}
return null;
}
@NotNull
private Pair<DirectoryInfo, String> calcDirectoryInfo(VirtualFile root, @NotNull final List<VirtualFile> hierarchy) {
VirtualFile moduleContentRoot = findModuleRootInfo(hierarchy);
VirtualFile libraryClassRoot = findLibraryRootInfo(hierarchy, false);
VirtualFile librarySourceRoot = findLibraryRootInfo(hierarchy, true);
if (moduleContentRoot == null && libraryClassRoot == null && librarySourceRoot == null) {
return new Pair<DirectoryInfo, String>(null, null);
}
VirtualFile sourceRoot = findPackageRootInfo(hierarchy, moduleContentRoot, null, librarySourceRoot);
VirtualFile moduleSourceRoot = findPackageRootInfo(hierarchy, moduleContentRoot, null, null);
boolean inModuleSources = moduleSourceRoot != null;
boolean inLibrarySource = librarySourceRoot != null;
int typeId = moduleSourceRoot != null ? rootTypeId.get(moduleSourceRoot) : 0;
OrderEntry[] entries = getOrderEntries(hierarchy, moduleContentRoot, libraryClassRoot, librarySourceRoot);
DirectoryInfo directoryInfo = new DirectoryInfo(contentRootOf.get(moduleContentRoot),
moduleContentRoot,
sourceRoot,
libraryClassRoot,
(byte)DirectoryInfo.createSourceRootTypeData(inModuleSources, inLibrarySource, typeId),
entries);
String packagePrefix = calcPackagePrefix(root, hierarchy, moduleContentRoot, libraryClassRoot, librarySourceRoot);
return Pair.create(directoryInfo, packagePrefix);
}
private String calcPackagePrefix(VirtualFile root,
List<VirtualFile> hierarchy,
VirtualFile moduleContentRoot,
VirtualFile libraryClassRoot, VirtualFile librarySourceRoot) {
VirtualFile packageRoot = findPackageRootInfo(hierarchy, moduleContentRoot, libraryClassRoot, librarySourceRoot);
String prefix = packagePrefix.get(packageRoot);
if (prefix != null && packageRoot != root) {
assert packageRoot != null;
String relative = VfsUtilCore.getRelativePath(root, packageRoot, '.');
prefix = StringUtil.isEmpty(prefix) ? relative : prefix + '.' + relative;
}
return prefix;
}
@Nullable
private VirtualFile findPackageRootInfo(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 == libraryClassRoot || root == librarySourceRoot) {
return root;
}
if (root == moduleContentRoot && !sourceRootOf.containsKey(root) && librarySourceRoot == null && libraryClassRoot == null) {
return null;
}
}
return null;
}
private LinkedHashSet<OrderEntry> getDependencyOrderEntries(List<VirtualFile> hierarchy) {
LinkedHashSet<OrderEntry> orderEntries = ContainerUtil.newLinkedHashSet();
for (VirtualFile root : hierarchy) {
orderEntries.addAll(depEntries.get(root));
}
return orderEntries;
}
private LinkedHashSet<OrderEntry> getLibraryOrderEntries(List<VirtualFile> hierarchy,
@Nullable VirtualFile libraryClassRoot,
@Nullable VirtualFile librarySourceRoot) {
LinkedHashSet<OrderEntry> orderEntries = ContainerUtil.newLinkedHashSet();
for (VirtualFile root : hierarchy) {
if (root == libraryClassRoot && !sourceRootOf.containsKey(root)) {
orderEntries.addAll(libClassRootEntries.get(root));
}
if (root == librarySourceRoot && libraryClassRoot == null) {
orderEntries.addAll(libSourceRootEntries.get(root));
}
if (libClassRootEntries.containsKey(root) || sourceRootOf.containsKey(root) && librarySourceRoot == null) {
break;
}
}
return orderEntries;
}
@Nullable
private ModuleSourceOrderEntry getModuleSourceEntry(List<VirtualFile> hierarchy, @NotNull VirtualFile moduleContentRoot) {
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;
}
private OrderEntry[] getOrderEntries(List<VirtualFile> hierarchy,
@Nullable VirtualFile moduleContentRoot,
@Nullable VirtualFile libraryClassRoot,
@Nullable VirtualFile librarySourceRoot) {
Set<OrderEntry> orderEntries = ContainerUtil.newLinkedHashSet();
orderEntries.addAll(getLibraryOrderEntries(hierarchy, libraryClassRoot, librarySourceRoot));
orderEntries.addAll(getDependencyOrderEntries(hierarchy));
if (moduleContentRoot != null) {
ContainerUtil.addIfNotNull(orderEntries, getModuleSourceEntry(hierarchy, moduleContentRoot));
}
if (orderEntries.isEmpty()) {
return null;
}
OrderEntry[] array = orderEntries.toArray(new OrderEntry[orderEntries.size()]);
Arrays.sort(array, DirectoryInfo.BY_OWNER_MODULE);
return array;
}
}
}