/*
* 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.
*/
package com.intellij.ide.todo.nodes;
import consulo.ide.projectView.impl.nodes.PackageElement;
import com.intellij.ide.projectView.impl.nodes.PackageNodeUtil;
import com.intellij.ide.todo.TodoFileDirAndModuleComparator;
import com.intellij.ide.todo.TodoTreeBuilder;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.ContainerUtil;
import consulo.psi.PsiPackage;
import consulo.psi.PsiPackageManager;
import org.jetbrains.annotations.Nullable;
import consulo.roots.ContentFolderScopes;
import java.util.*;
/**
* User: anna
* Date: May 27, 2005
*/
public class TodoTreeHelper {
public static void addPackagesToChildren(Project project, final ArrayList<AbstractTreeNode> children, final Module module, final TodoTreeBuilder builder) {
final PsiManager psiManager = PsiManager.getInstance(project);
final List<VirtualFile> sourceRoots = new ArrayList<VirtualFile>();
if (module == null) {
final ProjectRootManager projectRootManager = ProjectRootManager.getInstance(project);
ContainerUtil.addAll(sourceRoots, projectRootManager.getContentSourceRoots());
}
else {
ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
ContainerUtil.addAll(sourceRoots, moduleRootManager.getContentFolderFiles(ContentFolderScopes.productionAndTest()));
}
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 || PackageNodeUtil.isPackageDefault(directoryPackage)) {
// add subpackages
final PsiDirectory[] subdirectories = directory.getSubdirectories();
for (PsiDirectory subdirectory : subdirectories) {
final PsiPackage aPackage = PsiPackageManager.getInstance(project).findAnyPackage(subdirectory);
if (aPackage != null && !PackageNodeUtil.isPackageDefault(aPackage)) {
topLevelPackages.add(aPackage);
}
else {
final Iterator<PsiFile> files = builder.getFiles(subdirectory);
if (!files.hasNext()) continue;
TodoDirNode dirNode = new TodoDirNode(project, subdirectory, builder);
if (!children.contains(dirNode)) {
children.add(dirNode);
}
}
}
// add non-dir items
final Iterator<PsiFile> filesUnderDirectory = builder.getFilesUnderDirectory(directory);
for (; filesUnderDirectory.hasNext(); ) {
final PsiFile file = filesUnderDirectory.next();
TodoFileNode todoFileNode = new TodoFileNode(project, file, builder, false);
if (!children.contains(todoFileNode)) {
children.add(todoFileNode);
}
}
}
else {
// this is the case when a source root has pakage prefix assigned
topLevelPackages.add(directoryPackage);
}
}
GlobalSearchScope scope = module != null ? GlobalSearchScope.moduleScope(module) : GlobalSearchScope.projectScope(project);
ArrayList<PsiPackage> packages = new ArrayList<PsiPackage>();
for (PsiPackage psiPackage : topLevelPackages) {
final PsiPackage aPackage = findNonEmptyPackage(psiPackage, module, project, builder, scope);
if (aPackage != null) {
packages.add(aPackage);
}
}
for (PsiPackage psiPackage : packages) {
if (!builder.getTodoTreeStructure().getIsFlattenPackages()) {
PackageElement element =
new PackageElement(module, psiPackage, false);
TodoPackageNode packageNode = new TodoPackageNode(project, element, builder, psiPackage.getQualifiedName());
if (!children.contains(packageNode)) {
children.add(packageNode);
}
}
else {
Set<PsiPackage> allPackages = new HashSet<PsiPackage>();
traverseSubPackages(psiPackage, module, builder, project, allPackages);
for (PsiPackage aPackage : allPackages) {
TodoPackageNode packageNode =
new TodoPackageNode(project, new PackageElement(module, aPackage, false), builder);
if (!children.contains(packageNode)) {
children.add(packageNode);
}
}
}
}
addPackagesToChildren0(project, children, module, builder);
}
private static void addPackagesToChildren0(Project project, ArrayList<AbstractTreeNode> children, Module module, TodoTreeBuilder builder) {
final List<VirtualFile> roots = new ArrayList<VirtualFile>();
final List<VirtualFile> sourceRoots = new ArrayList<VirtualFile>();
final PsiManager psiManager = PsiManager.getInstance(project);
if (module == null) {
final ProjectRootManager projectRootManager = ProjectRootManager.getInstance(project);
ContainerUtil.addAll(roots, projectRootManager.getContentRoots());
ContainerUtil.addAll(sourceRoots, projectRootManager.getContentSourceRoots());
}
else {
ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
ContainerUtil.addAll(roots, moduleRootManager.getContentRoots());
ContainerUtil.addAll(sourceRoots, moduleRootManager.getContentFolderFiles(ContentFolderScopes.productionAndTest()));
}
roots.removeAll(sourceRoots);
for (VirtualFile dir : roots) {
final PsiDirectory directory = psiManager.findDirectory(dir);
if (directory == null) {
continue;
}
final Iterator<PsiFile> files = builder.getFiles(directory);
if (!files.hasNext()) continue;
TodoDirNode dirNode = new TodoDirNode(project, directory, builder);
if (!children.contains(dirNode)) {
children.add(dirNode);
}
}
}
@Nullable
public static PsiPackage findNonEmptyPackage(PsiPackage rootPackage,
Module module,
Project project,
TodoTreeBuilder builder,
GlobalSearchScope scope) {
if (!isPackageEmpty(new PackageElement(module, rootPackage, false), builder, project)) {
return rootPackage;
}
final PsiPackage[] subPackages = rootPackage.getSubPackages(scope);
PsiPackage suggestedNonEmptyPackage = null;
int count = 0;
for (PsiPackage aPackage : subPackages) {
if (!isPackageEmpty(new PackageElement(module, aPackage, false), builder, project)) {
if (++count > 1) return rootPackage;
suggestedNonEmptyPackage = aPackage;
}
}
for (PsiPackage aPackage : subPackages) {
if (aPackage != suggestedNonEmptyPackage) {
PsiPackage subPackage = findNonEmptyPackage(aPackage, module, project, builder, scope);
if (subPackage != null) {
if (count > 0) {
return rootPackage;
}
else {
count++;
suggestedNonEmptyPackage = subPackage;
}
}
}
}
return suggestedNonEmptyPackage;
}
private static void traverseSubPackages(PsiPackage psiPackage,
Module module,
TodoTreeBuilder builder,
Project project,
Set<PsiPackage> packages) {
if (!isPackageEmpty(new PackageElement(module, psiPackage, false), builder, project)) {
packages.add(psiPackage);
}
GlobalSearchScope scope = module != null ? GlobalSearchScope.moduleScope(module) : GlobalSearchScope.projectScope(project);
final PsiPackage[] subPackages = psiPackage.getSubPackages(scope);
for (PsiPackage subPackage : subPackages) {
traverseSubPackages(subPackage, module, builder, project, packages);
}
}
private static boolean isPackageEmpty(PackageElement packageElement,
TodoTreeBuilder builder,
Project project) {
if (packageElement == null) return true;
final PsiPackage psiPackage = packageElement.getPackage();
final Module module = packageElement.getModule();
GlobalSearchScope scope = module != null ? GlobalSearchScope.moduleScope(module) : GlobalSearchScope.projectScope(project);
final PsiDirectory[] directories = psiPackage.getDirectories(scope);
boolean isEmpty = true;
for (PsiDirectory psiDirectory : directories) {
isEmpty &= builder.isDirectoryEmpty(psiDirectory);
}
return isEmpty;
}
public static Collection<AbstractTreeNode> getDirectoryChildren(PsiDirectory psiDirectory, TodoTreeBuilder builder, boolean isFlatten) {
final Project project = psiDirectory.getProject();
ArrayList<AbstractTreeNode> children = new ArrayList<AbstractTreeNode>();
if (!isFlatten || !skipDirectory(psiDirectory)) {
final Iterator<PsiFile> iterator = builder.getFiles(psiDirectory);
while (iterator.hasNext()) {
final PsiFile psiFile = iterator.next();
// Add files
final PsiDirectory containingDirectory = psiFile.getContainingDirectory();
TodoFileNode todoFileNode = new TodoFileNode(project, psiFile, builder, false);
if (psiDirectory.equals(containingDirectory) && !children.contains(todoFileNode)) {
children.add(todoFileNode);
continue;
}
// Add directories (find first ancestor directory that is in our psiDirectory)
PsiDirectory _dir = psiFile.getContainingDirectory();
while (_dir != null) {
if (skipDirectory(_dir)) {
break;
}
final PsiDirectory parentDirectory = _dir.getParentDirectory();
TodoDirNode todoDirNode = new TodoDirNode(project, _dir, builder);
if (parentDirectory != null && psiDirectory.equals(parentDirectory) && !children.contains(todoDirNode)) {
children.add(todoDirNode);
break;
}
_dir = parentDirectory;
}
}
}
else { // flatten packages
final PsiDirectory parentDirectory = psiDirectory.getParentDirectory();
if (parentDirectory == null ||
!skipDirectory(parentDirectory) ||
!ProjectRootManager.getInstance(project).getFileIndex().isInContent(parentDirectory.getVirtualFile())) {
final Iterator<PsiFile> iterator = builder.getFiles(psiDirectory);
while (iterator.hasNext()) {
final PsiFile psiFile = iterator.next();
// Add files
TodoFileNode todoFileNode = new TodoFileNode(project, psiFile, builder, false);
if (psiDirectory.equals(psiFile.getContainingDirectory()) && !children.contains(todoFileNode)) {
children.add(todoFileNode);
continue;
}
// Add directories
final PsiDirectory _dir = psiFile.getContainingDirectory();
if (skipDirectory(_dir)) {
continue;
}
TodoDirNode todoDirNode = new TodoDirNode(project, _dir, builder);
if (PsiTreeUtil.isAncestor(psiDirectory, _dir, true) && !children.contains(todoDirNode) && !builder.isDirectoryEmpty(_dir)) {
children.add(todoDirNode);
}
}
}
else {
final Iterator<PsiFile> iterator = builder.getFiles(psiDirectory);
while (iterator.hasNext()) {
final PsiFile psiFile = iterator.next();
final PsiDirectory containingDirectory = psiFile.getContainingDirectory();
TodoFileNode todoFileNode = new TodoFileNode(project, psiFile, builder, false);
if (psiDirectory.equals(containingDirectory) && !children.contains(todoFileNode)) {
children.add(todoFileNode);
}
}
}
}
Collections.sort(children, TodoFileDirAndModuleComparator.INSTANCE);
return children;
}
public static boolean skipDirectory(final PsiDirectory directory) {
return PsiPackageManager.getInstance(directory.getProject()).findAnyPackage(directory) != null;
}
@Nullable
public static PsiElement getSelectedElement(Object userObject) {
if (userObject instanceof TodoDirNode) {
TodoDirNode descriptor = (TodoDirNode)userObject;
return descriptor.getValue();
}
else if (userObject instanceof TodoFileNode) {
TodoFileNode descriptor = (TodoFileNode)userObject;
return descriptor.getValue();
}
else if (userObject instanceof TodoPackageNode) {
TodoPackageNode descriptor = (TodoPackageNode)userObject;
final PackageElement packageElement = descriptor.getValue();
return packageElement != null ? packageElement.getPackage() : null;
}
return null;
}
}