/* * Copyright 2000-2013 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.ide.projectView.impl.nodes; import com.intellij.ide.projectView.ViewSettings; import com.intellij.ide.util.treeView.AbstractTreeNode; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; import com.intellij.openapi.roots.impl.DirectoryIndex; import com.intellij.openapi.roots.impl.DirectoryInfo; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiManager; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.util.IncorrectOperationException; import consulo.ide.projectView.impl.nodes.PackageElement; import consulo.module.extension.ModuleExtension; import consulo.psi.PsiPackage; import consulo.psi.PsiPackageManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; public class PackageNodeUtil { /** * a directory is considered "empty" if it has at least one child and all its children are only directories * * @param strictlyEmpty if true, the package is considered empty if it has only 1 child and this child is a directory * otherwise the package is considered as empty if all direct children that it has are directories */ public static boolean isEmptyMiddlePackage(@NotNull PsiDirectory dir, @Nullable Class<? extends ModuleExtension> moduleExtensionClass, boolean strictlyEmpty) { final VirtualFile[] files = dir.getVirtualFile().getChildren(); if (files.length == 0) { return false; } PsiManager manager = dir.getManager(); int subpackagesCount = 0; int directoriesCount = 0; for (VirtualFile file : files) { if (FileTypeManager.getInstance().isFileIgnored(file)) continue; if (!file.isDirectory()) return false; PsiDirectory childDir = manager.findDirectory(file); if (childDir != null) { directoriesCount++; if (strictlyEmpty && directoriesCount > 1) return false; final PsiPackageManager psiPackageManager = PsiPackageManager.getInstance(dir.getProject()); PsiPackage tempPackage = moduleExtensionClass == null ? psiPackageManager.findAnyPackage(childDir) : psiPackageManager.findPackage(dir, moduleExtensionClass); if (tempPackage != null) { subpackagesCount++; } } } if (strictlyEmpty) { return directoriesCount == subpackagesCount && directoriesCount == 1; } return directoriesCount == subpackagesCount && directoriesCount > 0; } private static class ModuleLibrariesSearchScope extends GlobalSearchScope { private final Module myModule; public ModuleLibrariesSearchScope(@NotNull Module module) { super(module.getProject()); myModule = module; } @Override public boolean contains(@NotNull VirtualFile file) { final OrderEntry orderEntry = ModuleRootManager.getInstance(myModule).getFileIndex().getOrderEntryForFile(file); return orderEntry instanceof ModuleExtensionWithSdkOrderEntry || orderEntry instanceof LibraryOrderEntry; } @Override public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) { final ModuleFileIndex fileIndex = ModuleRootManager.getInstance(myModule).getFileIndex(); return Comparing.compare(fileIndex.getOrderEntryForFile(file2), fileIndex.getOrderEntryForFile(file1)); } @Override public boolean isSearchInModuleContent(@NotNull Module aModule) { return false; } @Override public boolean isSearchInLibraries() { return true; } } private static class ProjectLibrariesSearchScope extends GlobalSearchScope { private final DirectoryIndex myDirectoryIndex; public ProjectLibrariesSearchScope(@NotNull Project project) { super(project); myDirectoryIndex = DirectoryIndex.getInstance(project); } @Override public boolean contains(@NotNull VirtualFile file) { VirtualFile dir = file.isDirectory() ? file : file.getParent(); if (dir == null) return false; DirectoryInfo info = myDirectoryIndex.getInfoForDirectory(dir); return info != null && info.hasLibraryClassRoot(); } @Override public int compare(@NotNull VirtualFile file1, @NotNull VirtualFile file2) { throw new IncorrectOperationException("not implemented"); } @Override public boolean isSearchInModuleContent(@NotNull Module aModule) { return false; } @Override public boolean isSearchInLibraries() { return true; } } public static boolean isPackageEmpty(@NotNull PsiPackage aPackage, @Nullable Module module, boolean strictlyEmpty, final boolean inLibrary) { final Project project = aPackage.getProject(); final PsiDirectory[] dirs = getDirectories(aPackage, project, module, inLibrary); for (final PsiDirectory dir : dirs) { if (!isEmptyMiddlePackage(dir, null, strictlyEmpty)) { return false; } } return true; } @NotNull public static Collection<AbstractTreeNode> createPackageViewChildrenOnFiles(@NotNull List<VirtualFile> sourceRoots, @NotNull Project project, @NotNull ViewSettings settings, @Nullable Module module, final boolean inLibrary) { final PsiManager psiManager = PsiManager.getInstance(project); final List<AbstractTreeNode> children = new ArrayList<AbstractTreeNode>(); final Set<PsiPackage> topLevelPackages = new HashSet<PsiPackage>(); for (final VirtualFile root : sourceRoots) { final PsiDirectory directory = psiManager.findDirectory(root); if (directory == null) { continue; } final PsiPackage directoryPackage = PsiPackageManager.getInstance(project).findAnyPackage(directory); if (directoryPackage == null || isPackageDefault(directoryPackage)) { // add subpackages final PsiDirectory[] subdirectories = directory.getSubdirectories(); for (PsiDirectory subdirectory : subdirectories) { final PsiPackage aPackage = PsiPackageManager.getInstance(project).findAnyPackage(subdirectory); if (aPackage != null && !isPackageDefault(aPackage)) { topLevelPackages.add(aPackage); } } // add non-dir items children.addAll(BaseProjectViewDirectoryHelper.getDirectoryChildren(directory, settings, false)); } else { topLevelPackages.add(directoryPackage); } } for (final PsiPackage topLevelPackage : topLevelPackages) { addPackageAsChild(children, topLevelPackage, module, settings, inLibrary); } return children; } public static boolean isPackageDefault(@NotNull PsiPackage directoryPackage) { final String qName = directoryPackage.getQualifiedName(); return qName.isEmpty(); } public static void addPackageAsChild(@NotNull Collection<AbstractTreeNode> children, @NotNull PsiPackage aPackage, @Nullable Module module, @NotNull ViewSettings settings, final boolean inLibrary) { final boolean shouldSkipPackage = settings.isHideEmptyMiddlePackages() && isPackageEmpty(aPackage, module, !settings.isFlattenPackages(), inLibrary); final Project project = aPackage.getProject(); if (!shouldSkipPackage) { children.add(new PackageElementNode(project, new PackageElement(module, aPackage, inLibrary), settings)); } if (settings.isFlattenPackages() || shouldSkipPackage) { final PsiPackage[] subpackages = getSubpackages(aPackage, module, project, inLibrary); for (PsiPackage subpackage : subpackages) { addPackageAsChild(children, subpackage, module, settings, inLibrary); } } } @NotNull public static PsiPackage[] getSubpackages(@NotNull PsiPackage aPackage, @Nullable Module module, @NotNull Project project, final boolean searchInLibraries) { final PsiDirectory[] dirs = getDirectories(aPackage, project, module, searchInLibraries); final Set<PsiPackage> subpackages = new HashSet<PsiPackage>(); for (PsiDirectory dir : dirs) { final PsiDirectory[] subdirectories = dir.getSubdirectories(); for (PsiDirectory subdirectory : subdirectories) { final PsiPackage psiPackage = PsiPackageManager.getInstance(project).findAnyPackage(subdirectory); if (psiPackage != null) { final String name = psiPackage.getName(); // skip "default" subpackages as they should be attributed to other modules // this is the case when contents of one module is nested into contents of another if (name != null && !name.isEmpty()) { subpackages.add(psiPackage); } } } } return subpackages.toArray(new PsiPackage[subpackages.size()]); } @NotNull public static PsiDirectory[] getDirectories(@NotNull PsiPackage aPackage, @NotNull Project project, @Nullable Module module, boolean inLibrary) { final GlobalSearchScope scopeToShow = getScopeToShow(project, module, inLibrary); return aPackage.getDirectories(scopeToShow); } @NotNull private static GlobalSearchScope getScopeToShow(@NotNull Project project, @Nullable Module module, boolean forLibraries) { if (module == null) { if (forLibraries) { return new ProjectLibrariesSearchScope(project); } return GlobalSearchScope.projectScope(project); } else { if (forLibraries) { return new ModuleLibrariesSearchScope(module); } return GlobalSearchScope.moduleScope(module); } } }