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
;
42 import java
.util
.HashMap
;
43 import java
.util
.List
;
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
{
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
) {
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
;
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());
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
,
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()) {
159 for (VirtualFile root
: LibraryUtil
.getLibraryRoots(project
)) {
160 countFilesRecursively(root
);
163 project
.putUserData(FILE_COUNT
, myTotalFileCount
);
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() {
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
);
194 for (VirtualFile root
: LibraryUtil
.getLibraryRoots(project
)) {
195 processFilesRecursively(root
, psiManager
);
201 ProgressManager
.getInstance().runProcessWithProgressSynchronously(buildingRunnable
, AnalysisScopeBundle
.message("package.dependencies.build.process.title"), true, project
);
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
);
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
);
238 private void counting(final VirtualFile file
) {
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) {
253 Runnable buildingRunnable
= new Runnable() {
255 for (final PsiFile file
: files
) {
264 ProgressManager
.getInstance().runProcessWithProgressSynchronously(buildingRunnable
, AnalysisScopeBundle
.message("package.dependencies.build.process.title"), false, myProject
);
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
);
293 FileNode fileNode
= new FileNode(file
, isMarked
);
294 dirNode
.add(fileNode
);
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
);
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
));
321 return getModuleDirNode(aPackage
, myFileIndex
.getModuleForFile(vFile
), getFileScopeType(vFile
));
324 return getModuleNode(myFileIndex
.getModuleForFile(vFile
), getFileScopeType(vFile
));
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
;
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
;
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
);
384 getLibraryDirNode(aPackage
.getParentPackage(), libraryOrJdk
).add(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
);
407 getModuleDirNode(aPackage
.getParentPackage(), module
, scopeType
).add(node
);
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
);
429 getMap(myModuleNodes
, scopeType
).put(module
, node
);
430 if (myShowModuleGroups
) {
431 getParentModuleGroup(groupPath
, scopeType
).add(node
);
433 getRootNode(scopeType
).add(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
);
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
);
479 private PackageDependenciesNode
getRootNode(ScopeType scopeType
) {
480 if (!myGroupByScopeType
) {
484 if (scopeType
== ScopeType
.TEST
) {
487 else if (scopeType
== ScopeType
.SOURCE
) {