update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / packageDependencies / ui / TreeModelBuilder.java
blob4e4622dbd29289daa930b199fdb7237588770055
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.packageDependencies.ui;
18 import com.intellij.analysis.AnalysisScopeBundle;
19 import com.intellij.ide.projectView.impl.ModuleGroup;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.module.Module;
22 import com.intellij.openapi.module.ModuleManager;
23 import com.intellij.openapi.progress.ProgressIndicator;
24 import com.intellij.openapi.progress.ProgressManager;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.roots.*;
27 import com.intellij.openapi.roots.libraries.LibraryUtil;
28 import com.intellij.openapi.util.Comparing;
29 import com.intellij.openapi.util.IconLoader;
30 import com.intellij.openapi.util.Key;
31 import com.intellij.openapi.util.Pair;
32 import com.intellij.openapi.vfs.VirtualFile;
33 import com.intellij.psi.*;
34 import com.intellij.util.Icons;
35 import com.intellij.util.ui.tree.TreeUtil;
36 import gnu.trove.THashMap;
37 import gnu.trove.TObjectHashingStrategy;
38 import org.jetbrains.annotations.NotNull;
39 import org.jetbrains.annotations.Nullable;
41 import javax.swing.*;
42 import java.util.HashMap;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.Set;
47 public class TreeModelBuilder {
48 private static final Key<Integer> FILE_COUNT = Key.create("FILE_COUNT");
49 private final ProjectFileIndex myFileIndex;
50 private final PsiManager myPsiManager;
51 private final Project myProject;
52 private static final Logger LOG = Logger.getInstance("com.intellij.packageDependencies.ui.TreeModelBuilder");
53 private final boolean myShowModuleGroups;
55 private static enum ScopeType {
56 TEST, SOURCE, LIB
59 private final boolean myShowModules;
60 private final boolean myGroupByScopeType;
61 private final boolean myFlattenPackages;
62 private boolean myShowFiles;
63 private final boolean myShowIndividualLibs;
64 private final Marker myMarker;
65 private final boolean myAddUnmarkedFiles;
66 private final PackageDependenciesNode myRoot;
67 private final Map<ScopeType, Map<Pair<Module, PsiPackage>, PackageNode>> myModulePackageNodes = new HashMap<ScopeType, Map<Pair<Module, PsiPackage>, PackageNode>>();
68 private final Map<ScopeType, Map<Pair<OrderEntry, PsiPackage>, PackageNode>> myLibraryPackageNodes = new HashMap<ScopeType, Map<Pair<OrderEntry, PsiPackage>, PackageNode>>();
69 private final Map<ScopeType, Map<Module, ModuleNode>> myModuleNodes = new HashMap<ScopeType, Map<Module, ModuleNode>>();
70 private final Map<ScopeType, Map<String, ModuleGroupNode>> myModuleGroupNodes = new HashMap<ScopeType, Map<String, ModuleGroupNode>>();
71 private final Map<ScopeType, Map<OrderEntry, LibraryNode>> myLibraryNodes = new HashMap<ScopeType, Map<OrderEntry, LibraryNode>>();
72 private int myScannedFileCount = 0;
73 private int myTotalFileCount = 0;
74 private int myMarkedFileCount = 0;
75 private GeneralGroupNode myAllLibsNode = null;
77 private GeneralGroupNode mySourceRoot = null;
78 private GeneralGroupNode myTestRoot = null;
79 private GeneralGroupNode myLibsRoot = null;
81 private static final Icon LIB_ICON_OPEN = IconLoader.getIcon("/nodes/ppLibOpen.png");
82 private static final Icon LIB_ICON_CLOSED = IconLoader.getIcon("/nodes/ppLibClosed.png");
83 private static final Icon TEST_ICON = IconLoader.getIcon("/nodes/testSourceFolder.png");
84 public static final String PRODUCTION_NAME = AnalysisScopeBundle.message("package.dependencies.production.node.text");
85 public static final String TEST_NAME = AnalysisScopeBundle.message("package.dependencies.test.node.text");
86 public static final String LIBRARY_NAME = AnalysisScopeBundle.message("package.dependencies.library.node.text");
88 public TreeModelBuilder(Project project, boolean showIndividualLibs, Marker marker, DependenciesPanel.DependencyPanelSettings settings) {
89 myProject = project;
90 myShowModules = settings.UI_SHOW_MODULES;
91 myGroupByScopeType = settings.UI_GROUP_BY_SCOPE_TYPE;
92 myFlattenPackages = settings.UI_FLATTEN_PACKAGES;
93 myShowFiles = settings.UI_SHOW_FILES;
94 myShowIndividualLibs = showIndividualLibs;
95 myShowModuleGroups = settings.UI_SHOW_MODULE_GROUPS;
96 myMarker = marker;
97 myAddUnmarkedFiles = !settings.UI_FILTER_LEGALS;
98 myRoot = new RootNode();
99 myFileIndex = ProjectRootManager.getInstance(project).getFileIndex();
100 myPsiManager = PsiManager.getInstance(project);
102 createMaps(ScopeType.LIB);
103 createMaps(ScopeType.SOURCE);
104 createMaps(ScopeType.TEST);
106 if (myGroupByScopeType) {
107 mySourceRoot = new GeneralGroupNode(PRODUCTION_NAME, Icons.PACKAGE_OPEN_ICON, Icons.PACKAGE_ICON);
108 myTestRoot = new GeneralGroupNode(TEST_NAME, TEST_ICON, TEST_ICON);
109 myLibsRoot = new GeneralGroupNode(LIBRARY_NAME, LIB_ICON_OPEN, LIB_ICON_CLOSED);
110 myRoot.add(mySourceRoot);
111 myRoot.add(myTestRoot);
112 myRoot.add(myLibsRoot);
116 private void createMaps(ScopeType scopeType) {
117 myModulePackageNodes.put(scopeType, new HashMap<Pair<Module, PsiPackage>, PackageNode>());
118 myLibraryPackageNodes.put(scopeType, new THashMap<Pair<OrderEntry, PsiPackage>, PackageNode>(new TObjectHashingStrategy<Pair<OrderEntry, PsiPackage>>() {
119 public int computeHashCode(final Pair<OrderEntry, PsiPackage> key) {
120 return key.getSecond() == null ? 0 : key.getSecond().hashCode();
123 public boolean equals(final Pair<OrderEntry, PsiPackage> o1, final Pair<OrderEntry, PsiPackage> o2) {
124 return Comparing.equal(o1.getSecond(), o2.getSecond());
126 }));
127 myModuleGroupNodes.put(scopeType, new HashMap<String, ModuleGroupNode>());
128 myModuleNodes.put(scopeType, new HashMap<Module, ModuleNode>());
129 myLibraryNodes.put(scopeType, new HashMap<OrderEntry, LibraryNode>());
132 public static synchronized TreeModel createTreeModel(Project project, boolean showProgress, Set<PsiFile> files, Marker marker, DependenciesPanel.DependencyPanelSettings settings) {
133 return new TreeModelBuilder(project, true, marker, settings).build(files, showProgress);
136 public static synchronized TreeModel createTreeModel(Project project, Marker marker, DependenciesPanel.DependencyPanelSettings settings) {
137 return new TreeModelBuilder(project, true, marker, settings).build(project, false);
140 public static synchronized TreeModel createTreeModel(Project project,
141 boolean showProgress,
142 boolean showIndividualLibs,
143 Marker marker) {
144 return new TreeModelBuilder(project, showIndividualLibs, marker, new DependenciesPanel.DependencyPanelSettings()).build(project, showProgress);
147 private void countFiles(Project project) {
148 final Integer fileCount = project.getUserData(FILE_COUNT);
149 if (fileCount == null) {
150 myFileIndex.iterateContent(new ContentIterator() {
151 public boolean processFile(VirtualFile fileOrDir) {
152 if (!fileOrDir.isDirectory()) {
153 counting(fileOrDir);
155 return true;
159 for (VirtualFile root : LibraryUtil.getLibraryRoots(project)) {
160 countFilesRecursively(root);
163 project.putUserData(FILE_COUNT, myTotalFileCount);
164 } else {
165 myTotalFileCount = fileCount.intValue();
169 public static void clearCaches(Project project) {
170 project.putUserData(FILE_COUNT, null);
173 public TreeModel build(final Project project, boolean showProgress) {
174 return build(project, showProgress, false);
177 public TreeModel build(final Project project, final boolean showProgress, final boolean sortByType) {
178 Runnable buildingRunnable = new Runnable() {
179 public void run() {
180 countFiles(project);
181 final PsiManager psiManager = PsiManager.getInstance(project);
182 myFileIndex.iterateContent(new ContentIterator() {
183 public boolean processFile(VirtualFile fileOrDir) {
184 if (!fileOrDir.isDirectory()) {
185 final PsiFile psiFile = psiManager.findFile(fileOrDir);
186 if (psiFile != null) {
187 buildFileNode(psiFile);
190 return true;
194 for (VirtualFile root : LibraryUtil.getLibraryRoots(project)) {
195 processFilesRecursively(root, psiManager);
200 if (showProgress) {
201 ProgressManager.getInstance().runProcessWithProgressSynchronously(buildingRunnable, AnalysisScopeBundle.message("package.dependencies.build.process.title"), true, project);
203 else {
204 buildingRunnable.run();
207 TreeUtil.sort(myRoot, new DependencyNodeComparator(sortByType));
208 return new TreeModel(myRoot, myTotalFileCount, myMarkedFileCount);
211 private void processFilesRecursively(VirtualFile file, PsiManager psiManager) {
212 if (file.isDirectory()) {
213 VirtualFile[] children = file.getChildren();
214 for (VirtualFile aChildren : children) {
215 processFilesRecursively(aChildren, psiManager);
218 else {
219 final PsiFile psiFile = psiManager.findFile(file);
220 if (psiFile != null) { // skip inners & anonymous
221 buildFileNode(psiFile);
226 private void countFilesRecursively(VirtualFile file) {
227 if (file.isDirectory()) {
228 VirtualFile[] children = file.getChildren();
229 for (VirtualFile aChildren : children) {
230 countFilesRecursively(aChildren);
233 else {
234 counting(file);
238 private void counting(final VirtualFile file) {
239 myTotalFileCount++;
240 ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
241 if (indicator != null) {
242 indicator.setText(AnalysisScopeBundle.message("package.dependencies.build.progress.text"));
243 indicator.setIndeterminate(true);
244 indicator.setText2(file.getPresentableUrl());
248 private TreeModel build(final Set<PsiFile> files, boolean showProgress) {
249 if (files.size() == 1) {
250 myShowFiles = true;
253 Runnable buildingRunnable = new Runnable() {
254 public void run() {
255 for (final PsiFile file : files) {
256 if (file != null) {
257 buildFileNode(file);
263 if (showProgress) {
264 ProgressManager.getInstance().runProcessWithProgressSynchronously(buildingRunnable, AnalysisScopeBundle.message("package.dependencies.build.process.title"), false, myProject);
266 else {
267 buildingRunnable.run();
270 TreeUtil.sort(myRoot, new DependencyNodeComparator());
271 return new TreeModel(myRoot, myTotalFileCount, myMarkedFileCount);
274 private void buildFileNode(PsiFile file) {
275 ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
276 if (indicator != null) {
277 indicator.setIndeterminate(false);
278 indicator.setText(AnalysisScopeBundle.message("package.dependencies.build.progress.text"));
279 final VirtualFile virtualFile = file.getVirtualFile();
280 if (virtualFile != null) {
281 indicator.setText2(virtualFile.getPresentableUrl());
283 indicator.setFraction(((double)myScannedFileCount++) / myTotalFileCount);
286 if (file == null || !file.isValid()) return;
287 boolean isMarked = myMarker != null && myMarker.isMarked(file);
288 if (isMarked) myMarkedFileCount++;
289 if (isMarked || myAddUnmarkedFiles) {
290 PackageDependenciesNode dirNode = getFileParentNode(file);
292 if (myShowFiles) {
293 FileNode fileNode = new FileNode(file, isMarked);
294 dirNode.add(fileNode);
296 else {
297 dirNode.addFile(file, isMarked);
302 public @NotNull PackageDependenciesNode getFileParentNode(PsiFile file) {
303 VirtualFile vFile = file.getVirtualFile();
304 LOG.assertTrue(vFile != null);
305 final VirtualFile containingDirectory = vFile.getParent();
306 LOG.assertTrue(containingDirectory != null);
307 PsiPackage aPackage = null;
308 if (file instanceof PsiJavaFile){
309 aPackage = getFilePackage((PsiJavaFile)file);
310 } else {
311 final String packageName = myFileIndex.getPackageNameByDirectory(containingDirectory);
312 if (packageName != null) {
313 aPackage = JavaPsiFacade.getInstance(myPsiManager.getProject()).findPackage(packageName);
316 if (aPackage != null) {
317 if (myFileIndex.isInLibrarySource(vFile) || myFileIndex.isInLibraryClasses(vFile)) {
318 return getLibraryDirNode(aPackage, getLibraryForFile(file));
320 else {
321 return getModuleDirNode(aPackage, myFileIndex.getModuleForFile(vFile), getFileScopeType(vFile));
324 return getModuleNode(myFileIndex.getModuleForFile(vFile), getFileScopeType(vFile));
328 @Nullable
329 private PsiPackage getFilePackage(PsiJavaFile file) {
330 VirtualFile vFile = file.getVirtualFile();
331 if (vFile != null && myFileIndex.isInLibrarySource(vFile)) {
332 final VirtualFile directory = vFile.getParent();
333 if (directory != null) {
334 final String packageName = myFileIndex.getPackageNameByDirectory(directory);
335 if (packageName != null) {
336 return JavaPsiFacade.getInstance(myPsiManager.getProject()).findPackage(packageName);
340 return JavaPsiFacade.getInstance(myPsiManager.getProject()).findPackage(file.getPackageName());
343 private ScopeType getFileScopeType(VirtualFile file) {
344 if (myFileIndex.isLibraryClassFile(file) || myFileIndex.isInLibrarySource(file)) return ScopeType.LIB;
345 if (myFileIndex.isInTestSourceContent(file)) return ScopeType.TEST;
346 return ScopeType.SOURCE;
349 @Nullable
350 private OrderEntry getLibraryForFile(PsiFile file) {
351 final VirtualFile virtualFile = file.getVirtualFile();
352 if (virtualFile == null) return null;
353 List<OrderEntry> orders = myFileIndex.getOrderEntriesForFile(virtualFile);
354 for (OrderEntry order : orders) {
355 if (order instanceof LibraryOrderEntry || order instanceof JdkOrderEntry) return order;
357 return null;
360 private <T> T getMap(Map<ScopeType, T> map, ScopeType scopeType) {
361 return map.get(myGroupByScopeType ? scopeType : ScopeType.SOURCE);
364 private PackageDependenciesNode getLibraryDirNode(PsiPackage aPackage, OrderEntry libraryOrJdk) {
365 if (aPackage == null || aPackage.getName() == null) {
366 return getLibraryOrJDKNode(libraryOrJdk);
369 if (!myShowModules && !myGroupByScopeType) {
370 return getModuleDirNode(aPackage, null, ScopeType.LIB);
373 Pair<OrderEntry, PsiPackage> descriptor = new Pair<OrderEntry, PsiPackage>(myShowModules ? libraryOrJdk : null, aPackage);
374 PackageNode node = getMap(myLibraryPackageNodes, ScopeType.LIB).get(descriptor);
375 if (node != null) return node;
377 node = new PackageNode(aPackage, myFlattenPackages);
378 getMap(myLibraryPackageNodes, ScopeType.LIB).put(descriptor, node);
380 if (myFlattenPackages) {
381 getLibraryOrJDKNode(libraryOrJdk).add(node);
383 else {
384 getLibraryDirNode(aPackage.getParentPackage(), libraryOrJdk).add(node);
387 return node;
390 private PackageDependenciesNode getModuleDirNode(PsiPackage aPackage, Module module, ScopeType scopeType) {
391 if (aPackage == null) {
392 return getModuleNode(module, scopeType);
395 Pair<Module, PsiPackage> descriptor = new Pair<Module, PsiPackage>(myShowModules ? module : null, aPackage);
396 PackageNode node = getMap(myModulePackageNodes, scopeType).get(descriptor);
398 if (node != null) return node;
400 node = new PackageNode(aPackage, myFlattenPackages);
401 getMap(myModulePackageNodes, scopeType).put(descriptor, node);
403 if (myFlattenPackages) {
404 getModuleNode(module, scopeType).add(node);
406 else {
407 getModuleDirNode(aPackage.getParentPackage(), module, scopeType).add(node);
410 return node;
414 @Nullable
415 private PackageDependenciesNode getModuleNode(Module module, ScopeType scopeType) {
416 if (module == null || !myShowModules) {
417 return getRootNode(scopeType);
419 ModuleNode node = getMap(myModuleNodes, scopeType).get(module);
420 if (node != null) return node;
421 node = new ModuleNode(module);
422 final ModuleManager moduleManager = ModuleManager.getInstance(myProject);
423 final String[] groupPath = moduleManager.getModuleGroupPath(module);
424 if (groupPath == null) {
425 getMap(myModuleNodes, scopeType).put(module, node);
426 getRootNode(scopeType).add(node);
427 return node;
429 getMap(myModuleNodes, scopeType).put(module, node);
430 if (myShowModuleGroups) {
431 getParentModuleGroup(groupPath, scopeType).add(node);
432 } else {
433 getRootNode(scopeType).add(node);
435 return node;
438 private PackageDependenciesNode getParentModuleGroup(String [] groupPath, ScopeType scopeType){
439 ModuleGroupNode groupNode = getMap(myModuleGroupNodes, scopeType).get(groupPath[groupPath.length - 1]);
440 if (groupNode == null) {
441 groupNode = new ModuleGroupNode(new ModuleGroup(groupPath));
442 getMap(myModuleGroupNodes, scopeType).put(groupPath[groupPath.length - 1], groupNode);
443 getRootNode(scopeType).add(groupNode);
445 if (groupPath.length > 1) {
446 String [] path = new String[groupPath.length - 1];
447 System.arraycopy(groupPath, 0, path, 0, groupPath.length - 1);
448 final PackageDependenciesNode node = getParentModuleGroup(path, scopeType);
449 node.add(groupNode);
451 return groupNode;
454 private PackageDependenciesNode getLibraryOrJDKNode(OrderEntry libraryOrJdk) {
455 if (libraryOrJdk == null || !myShowModules) {
456 return getRootNode(ScopeType.LIB);
459 if (!myShowIndividualLibs) {
460 if (myGroupByScopeType) return getRootNode(ScopeType.LIB);
461 if (myAllLibsNode == null) {
462 myAllLibsNode = new GeneralGroupNode(AnalysisScopeBundle.message("dependencies.libraries.node.text"), LIB_ICON_OPEN, LIB_ICON_CLOSED);
463 getRootNode(ScopeType.LIB).add(myAllLibsNode);
465 return myAllLibsNode;
468 LibraryNode node = getMap(myLibraryNodes, ScopeType.LIB).get(libraryOrJdk);
469 if (node != null) return node;
470 node = new LibraryNode(libraryOrJdk);
471 getMap(myLibraryNodes, ScopeType.LIB).put(libraryOrJdk, node);
473 getRootNode(ScopeType.LIB).add(node);
474 return node;
478 @NotNull
479 private PackageDependenciesNode getRootNode(ScopeType scopeType) {
480 if (!myGroupByScopeType) {
481 return myRoot;
483 else {
484 if (scopeType == ScopeType.TEST) {
485 return myTestRoot;
487 else if (scopeType == ScopeType.SOURCE) {
488 return mySourceRoot;
490 else {
491 return myLibsRoot;