/* * Copyright 2000-2009 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. */ /* * User: anna * Date: 23-Jan-2008 */ package com.intellij.ide.projectView.impl.nodes; import com.intellij.ide.projectView.ProjectViewNode; import com.intellij.ide.projectView.ViewSettings; import com.intellij.ide.projectView.impl.ProjectRootsUtil; import com.intellij.ide.util.treeView.AbstractTreeNode; import com.intellij.ide.util.treeView.TreeViewUtil; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.FileTypeRegistry; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModuleFileIndex; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; 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.*; import com.intellij.psi.search.PsiElementProcessor; import com.intellij.psi.util.PsiUtilBase; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.PathUtil; import consulo.ide.projectView.ShowExcludedFilesProjectViewPaneOptionProvider; import consulo.ide.projectView.impl.nodes.PackageElement; import consulo.psi.PsiPackage; import consulo.psi.PsiPackageManager; import consulo.vfs.ArchiveFileSystem; import consulo.vfs.util.ArchiveVfsUtil; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; public class BaseProjectViewDirectoryHelper { public static final Logger LOGGER = Logger.getInstance(BaseProjectViewDirectoryHelper.class); @Nullable public static String getLocationString(@NotNull PsiDirectory psiDirectory) { PsiPackage aPackage = PsiPackageManager.getInstance(psiDirectory.getProject()).findAnyPackage(psiDirectory); if (ProjectRootsUtil.isSourceRoot(psiDirectory) && aPackage != null) { return aPackage.getQualifiedName(); } final VirtualFile directory = psiDirectory.getVirtualFile(); final VirtualFile contentRootForFile = ProjectRootManager.getInstance(psiDirectory.getProject()).getFileIndex().getContentRootForFile(directory); if (Comparing.equal(contentRootForFile, psiDirectory)) { return PathUtil.toPresentableUrl(directory.getUrl()); } return null; } public static boolean isShowFQName(Project project, ViewSettings settings, Object parentValue, PsiDirectory value) { PsiPackage aPackage; return value != null && !(parentValue instanceof Project) && settings.isFlattenPackages() && (aPackage = PsiPackageManager.getInstance(project).findAnyPackage(value)) != null && !aPackage.getQualifiedName().isEmpty(); } @Nullable public static String getNodeName(ViewSettings settings, Object parentValue, @NotNull PsiDirectory directory) { Project project = directory.getProject(); PsiPackage aPackage = PsiPackageManager.getInstance(project).findAnyPackage(directory); String name = directory.getName(); VirtualFile dirFile = directory.getVirtualFile(); if (dirFile.getFileSystem() instanceof ArchiveFileSystem && dirFile.getParent() == null) { VirtualFile virtualFileForArchive = ArchiveVfsUtil.getVirtualFileForArchive(dirFile); if (virtualFileForArchive != null) { name = virtualFileForArchive.getName(); } } PsiPackage parentPackage; if (!ProjectRootsUtil.isSourceRoot(directory) && aPackage != null && !aPackage.getQualifiedName().isEmpty() && parentValue instanceof PsiDirectory) { parentPackage = PsiPackageManager.getInstance(project).findAnyPackage(((PsiDirectory)parentValue)); } else if (ProjectRootsUtil.isSourceRoot(directory) && aPackage != null) { //package prefix aPackage = null; parentPackage = null; } else { parentPackage = null; } return TreeViewUtil.getNodeName(settings, aPackage, parentPackage, name, isShowFQName(project, settings, parentValue, directory)); } public static boolean skipDirectory(PsiDirectory directory) { return PsiPackageManager.getInstance(directory.getProject()).findAnyPackage(directory) == null; } public static boolean isEmptyMiddleDirectory(PsiDirectory directory, final boolean strictlyEmpty) { return PsiPackageManager.getInstance(directory.getProject()).findAnyPackage(directory) != null && PackageNodeUtil.isEmptyMiddlePackage(directory, null, strictlyEmpty); } public static boolean canRepresent(Object element, PsiDirectory directory) { if (element instanceof VirtualFile) { VirtualFile vFile = (VirtualFile)element; return Comparing.equal(directory.getVirtualFile(), vFile); } if (element instanceof PackageElement) { final PackageElement packageElement = (PackageElement)element; return Arrays.asList(packageElement.getPackage().getDirectories()).contains(directory); } return false; } public static Collection<AbstractTreeNode> getDirectoryChildren(final PsiDirectory psiDirectory, final ViewSettings settings, final boolean withSubDirectories) { final List<AbstractTreeNode> children = new ArrayList<AbstractTreeNode>(); final Project project = psiDirectory.getProject(); final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); final Module module = fileIndex.getModuleForFile(psiDirectory.getVirtualFile()); final ModuleFileIndex moduleFileIndex = module == null ? null : ModuleRootManager.getInstance(module).getFileIndex(); if (!settings.isFlattenPackages() || skipDirectory(psiDirectory)) { processPsiDirectoryChildren(psiDirectory, directoryChildrenInProject(psiDirectory, settings), children, fileIndex, null, settings, withSubDirectories); } else { // source directory in "flatten packages" mode final PsiDirectory parentDir = psiDirectory.getParentDirectory(); if (parentDir == null || skipDirectory(parentDir) /*|| !rootDirectoryFound(parentDir)*/ && withSubDirectories) { addAllSubpackages(children, psiDirectory, moduleFileIndex, settings); } PsiDirectory[] subdirs = psiDirectory.getSubdirectories(); for (PsiDirectory subdir : subdirs) { if (!skipDirectory(subdir)) { continue; } VirtualFile directoryFile = subdir.getVirtualFile(); if (FileTypeRegistry.getInstance().isFileIgnored(directoryFile)) continue; if (withSubDirectories) { children.add(new PsiDirectoryNode(project, subdir, settings)); } } processPsiDirectoryChildren(psiDirectory, psiDirectory.getFiles(), children, fileIndex, moduleFileIndex, settings, withSubDirectories); } return children; } public static List<VirtualFile> getTopLevelRoots(Project project) { List<VirtualFile> topLevelContentRoots = new ArrayList<VirtualFile>(); ProjectRootManager prm = ProjectRootManager.getInstance(project); ProjectFileIndex index = prm.getFileIndex(); for (VirtualFile root : prm.getContentRoots()) { VirtualFile parent = root.getParent(); if (parent == null || !index.isInContent(parent)) { topLevelContentRoots.add(root); } } return topLevelContentRoots; } private static PsiElement[] directoryChildrenInProject(PsiDirectory psiDirectory, final ViewSettings settings) { DirectoryIndex directoryIndex = DirectoryIndex.getInstance(psiDirectory.getProject()); VirtualFile dir = psiDirectory.getVirtualFile(); if (shouldBeShown(directoryIndex, dir, settings)) { final List<PsiElement> children = new ArrayList<PsiElement>(); psiDirectory.processChildren(new PsiElementProcessor<PsiFileSystemItem>() { @Override public boolean execute(@NotNull PsiFileSystemItem element) { if (shouldBeShown(directoryIndex, element.getVirtualFile(), settings)) { children.add(element); } return true; } }); return PsiUtilCore.toPsiElementArray(children); } PsiManager manager = psiDirectory.getManager(); Set<PsiElement> directoriesOnTheWayToContentRoots = new THashSet<PsiElement>(); for (VirtualFile root : getTopLevelRoots(psiDirectory.getProject())) { VirtualFile current = root; while (current != null) { VirtualFile parent = current.getParent(); if (Comparing.equal(parent, dir)) { final PsiDirectory psi = manager.findDirectory(current); if (psi != null) { directoriesOnTheWayToContentRoots.add(psi); } } current = parent; } } return PsiUtilBase.toPsiElementArray(directoriesOnTheWayToContentRoots); } private static boolean shouldBeShown(DirectoryIndex directoryIndex, VirtualFile dir, ViewSettings settings) { DirectoryInfo directoryInfo = directoryIndex.getInfoForFile(dir); if (directoryInfo.isInProject()) return true; return settings.getViewOption(ShowExcludedFilesProjectViewPaneOptionProvider.KEY) == Boolean.TRUE && directoryInfo.isExcluded(); } // used only for non-flatten packages mode public static void processPsiDirectoryChildren(final PsiDirectory psiDir, PsiElement[] children, List<AbstractTreeNode> container, ProjectFileIndex projectFileIndex, ModuleFileIndex moduleFileIndex, ViewSettings viewSettings, boolean withSubDirectories) { for (PsiElement child : children) { LOGGER.assertTrue(child.isValid()); final VirtualFile vFile; if (child instanceof PsiFile) { vFile = ((PsiFile)child).getVirtualFile(); addNode(moduleFileIndex, vFile, container, PsiFileNode.class, child, viewSettings); } else if (child instanceof PsiDirectory) { if (withSubDirectories) { PsiDirectory dir = (PsiDirectory)child; vFile = dir.getVirtualFile(); if (!vFile.equals(projectFileIndex.getSourceRootForFile(vFile))) { // if is not a source root if (viewSettings.isHideEmptyMiddlePackages() && !skipDirectory(psiDir) && isEmptyMiddleDirectory(dir, true)) { processPsiDirectoryChildren(dir, directoryChildrenInProject(dir, viewSettings), container, projectFileIndex, moduleFileIndex, viewSettings, withSubDirectories); // expand it recursively continue; } } addNode(moduleFileIndex, vFile, container, PsiDirectoryNode.class, child, viewSettings); } } else { LOGGER.error("Either PsiFile or PsiDirectory expected as a child of " + child.getParent() + ", but was " + child); } } } public static void addNode(ModuleFileIndex moduleFileIndex, VirtualFile vFile, List<AbstractTreeNode> container, Class<? extends AbstractTreeNode> nodeClass, PsiElement element, final ViewSettings settings) { if (vFile == null) { return; } // this check makes sense for classes not in library content only if (moduleFileIndex != null && !moduleFileIndex.isInContent(vFile)) { return; } try { container.add(ProjectViewNode.createTreeNode(nodeClass, element.getProject(), element, settings)); } catch (Exception e) { LOGGER.error(e); } } // used only in flatten packages mode public static void addAllSubpackages(List<AbstractTreeNode> container, PsiDirectory dir, ModuleFileIndex moduleFileIndex, ViewSettings viewSettings) { final Project project = dir.getProject(); PsiDirectory[] subdirs = dir.getSubdirectories(); for (PsiDirectory subdir : subdirs) { if (skipDirectory(subdir)) { continue; } if (moduleFileIndex != null) { if (!moduleFileIndex.isInContent(subdir.getVirtualFile())) { container.add(new PsiDirectoryNode(project, subdir, viewSettings)); continue; } } if (viewSettings.isHideEmptyMiddlePackages()) { if (!isEmptyMiddleDirectory(subdir, false)) { container.add(new PsiDirectoryNode(project, subdir, viewSettings)); } } else { container.add(new PsiDirectoryNode(project, subdir, viewSettings)); } addAllSubpackages(container, subdir, moduleFileIndex, viewSettings); } } }