// made public for Upsource
  public RootIndex(@NotNull Project project, @NotNull InfoCache cache) {
    myProject = project;
    myInfoCache = cache;
    final RootInfo info = buildRootInfo(project);

    MultiMap<String, VirtualFile> rootsByPackagePrefix = MultiMap.create();
    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);
      rootsByPackagePrefix.putValue(pair.second, root);
      myPackagePrefixByRoot.put(root, pair.second);
    }
    myPackageDirectoryCache =
        new PackageDirectoryCache(rootsByPackagePrefix) {
          @Override
          protected boolean isPackageDirectory(
              @NotNull VirtualFile dir, @NotNull String packageName) {
            return getInfoForFile(dir).isInProject() && packageName.equals(getPackageName(dir));
          }
        };
  }
  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;
  }
  @NotNull
  private Map<VirtualFile, OrderEntry[]> getOrderEntries() {
    Map<VirtualFile, OrderEntry[]> result = myOrderEntries;
    if (result != null) return result;

    MultiMap<VirtualFile, OrderEntry> libClassRootEntries = MultiMap.createSmart();
    MultiMap<VirtualFile, OrderEntry> libSourceRootEntries = MultiMap.createSmart();
    MultiMap<VirtualFile, OrderEntry> depEntries = MultiMap.createSmart();

    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;
  }
  @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);
  }