1 package com
.intellij
.cyclicDependencies
;
3 import com
.intellij
.analysis
.AnalysisScope
;
4 import com
.intellij
.analysis
.AnalysisScopeBundle
;
5 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
6 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
7 import com
.intellij
.openapi
.progress
.ProgressManager
;
8 import com
.intellij
.openapi
.project
.Project
;
9 import com
.intellij
.openapi
.roots
.ProjectFileIndex
;
10 import com
.intellij
.openapi
.roots
.ProjectRootManager
;
11 import com
.intellij
.packageDependencies
.ForwardDependenciesBuilder
;
12 import com
.intellij
.psi
.*;
13 import com
.intellij
.util
.graph
.CachingSemiGraph
;
14 import com
.intellij
.util
.graph
.Graph
;
15 import com
.intellij
.util
.graph
.GraphGenerator
;
23 public class CyclicDependenciesBuilder
{
24 private final Project myProject
;
25 private final AnalysisScope myScope
;
26 private Map
<String
, PsiPackage
> myPackages
= new HashMap
<String
, PsiPackage
>();
27 private Graph
<PsiPackage
> myGraph
;
28 private Map
<PsiPackage
, Map
<PsiPackage
, Set
<PsiFile
>>> myFilesInDependentPackages
= new HashMap
<PsiPackage
, Map
<PsiPackage
, Set
<PsiFile
>>>();
29 private Map
<PsiPackage
, Map
<PsiPackage
, Set
<PsiFile
>>> myBackwardFilesInDependentPackages
= new HashMap
<PsiPackage
, Map
<PsiPackage
, Set
<PsiFile
>>>();
30 private Map
<PsiPackage
, Set
<PsiPackage
>> myPackageDependencies
= new HashMap
<PsiPackage
, Set
<PsiPackage
>>();
31 private HashMap
<PsiPackage
, Set
<List
<PsiPackage
>>> myCyclicDependencies
= new HashMap
<PsiPackage
, Set
<List
<PsiPackage
>>>();
32 private int myFileCount
= 0;
33 private ForwardDependenciesBuilder myForwardBuilder
;
35 private String myRootNodeNameInUsageView
;
37 public CyclicDependenciesBuilder(final Project project
, final AnalysisScope scope
) {
40 myForwardBuilder
= new ForwardDependenciesBuilder(myProject
, myScope
){
41 public String
getRootNodeNameInUsageView() {
42 return CyclicDependenciesBuilder
.this.getRootNodeNameInUsageView();
45 public String
getInitialUsagesPosition() {
46 return AnalysisScopeBundle
.message("cyclic.dependencies.usage.view.initial.text");
51 public String
getRootNodeNameInUsageView() {
52 return myRootNodeNameInUsageView
;
55 public void setRootNodeNameInUsageView(final String rootNodeNameInUsageView
) {
56 myRootNodeNameInUsageView
= rootNodeNameInUsageView
;
59 public Project
getProject() {
63 public AnalysisScope
getScope() {
67 public ForwardDependenciesBuilder
getForwardBuilder() {
68 return myForwardBuilder
;
71 public void analyze() {
72 final ProjectFileIndex projectFileIndex
= ProjectRootManager
.getInstance(getProject()).getFileIndex();
73 getScope().accept(new PsiRecursiveElementVisitor() {
74 public void visitFile(PsiFile file
) {
75 if (file
instanceof PsiJavaFile
) {
76 PsiJavaFile psiJavaFile
= (PsiJavaFile
)file
;
77 if (getScope().contains(psiJavaFile
)) {
78 final PsiPackage aPackage
= findPackage(psiJavaFile
.getPackageName());
79 if (aPackage
!= null) {
80 myPackages
.put(psiJavaFile
.getPackageName(), aPackage
);
83 final Set
<PsiPackage
> packs
= getPackageHierarhy(psiJavaFile
.getPackageName());
84 final ForwardDependenciesBuilder builder
= new ForwardDependenciesBuilder(getProject(), new AnalysisScope(psiJavaFile
));
85 builder
.setTotalFileCount(getScope().getFileCount());
86 builder
.setInitialFileCount(++myFileCount
);
88 final Set
<PsiFile
> psiFiles
= builder
.getDependencies().get(psiJavaFile
);
89 for (Iterator
<PsiPackage
> iterator
= packs
.iterator(); iterator
.hasNext();) {
90 PsiPackage pack
= iterator
.next();
91 Set
<PsiPackage
> pack2Packages
= myPackageDependencies
.get(pack
);
92 if (pack2Packages
== null) {
93 pack2Packages
= new HashSet
<PsiPackage
>();
94 myPackageDependencies
.put(pack
, pack2Packages
);
96 for (Iterator
<PsiFile
> it
= psiFiles
.iterator(); it
.hasNext();) {
97 PsiFile psiFile
= it
.next();
98 if (!(psiFile
instanceof PsiJavaFile
) || !projectFileIndex
.isInSourceContent(psiFile
.getVirtualFile()) ||
99 !getScope().contains(psiFile
)) {
103 // construct dependent packages
104 final String packageName
= ((PsiJavaFile
)psiFile
).getPackageName();
105 //do not depend on parent packages
106 if (packageName
== null || packageName
.startsWith(pack
.getQualifiedName())) {
109 final PsiPackage depPackage
= findPackage(packageName
);
110 if (depPackage
== null) { //not from analyze scope
113 pack2Packages
.add(depPackage
);
115 constractFilesInDependenciesPackagesMap(pack
, depPackage
, psiFile
, myFilesInDependentPackages
);
116 constractFilesInDependenciesPackagesMap(depPackage
, pack
, psiJavaFile
, myBackwardFilesInDependentPackages
);
117 constractWholeDependenciesMap(psiJavaFile
, psiFile
);
123 ProgressIndicator indicator
= ProgressManager
.getInstance().getProgressIndicator();
124 if (indicator
!= null) {
125 if (indicator
.isCanceled()) {
126 throw new ProcessCanceledException();
128 indicator
.setText(AnalysisScopeBundle
.message("cyclic.dependencies.progress.text"));
129 indicator
.setText2("");
130 indicator
.setIndeterminate(true);
132 myCyclicDependencies
= getCycles(myPackages
.values());
135 private void constractFilesInDependenciesPackagesMap(final PsiPackage pack
,
136 final PsiPackage depPackage
,
138 final Map
<PsiPackage
, Map
<PsiPackage
, Set
<PsiFile
>>> filesInDependentPackages
) {
139 Map
<PsiPackage
, Set
<PsiFile
>> dependentPackages2Files
= filesInDependentPackages
.get(pack
);
140 if (dependentPackages2Files
== null) {
141 dependentPackages2Files
= new HashMap
<PsiPackage
, Set
<PsiFile
>>();
142 filesInDependentPackages
.put(pack
, dependentPackages2Files
);
144 Set
<PsiFile
> depFiles
= dependentPackages2Files
.get(depPackage
);
145 if (depFiles
== null) {
146 depFiles
= new HashSet
<PsiFile
>();
147 dependentPackages2Files
.put(depPackage
, depFiles
);
152 //construct all dependencies for usage view
153 private void constractWholeDependenciesMap(final PsiJavaFile psiJavaFile
, final PsiFile psiFile
) {
154 Set
<PsiFile
> wholeDependencies
= myForwardBuilder
.getDependencies().get(psiJavaFile
);
155 if (wholeDependencies
== null) {
156 wholeDependencies
= new HashSet
<PsiFile
>();
157 myForwardBuilder
.getDependencies().put(psiJavaFile
, wholeDependencies
);
159 wholeDependencies
.add(psiFile
);
162 public Set
<PsiFile
> getDependentFilesInPackage(PsiPackage pack
, PsiPackage depPack
) {
163 Set
<PsiFile
> psiFiles
= new HashSet
<PsiFile
>();
164 final Map
<PsiPackage
, Set
<PsiFile
>> map
= myFilesInDependentPackages
.get(pack
);
166 psiFiles
= map
.get(depPack
);
168 if (psiFiles
== null) {
169 psiFiles
= new HashSet
<PsiFile
>();
174 public Set
<PsiFile
> getDependentFilesInPackage(PsiPackage firstPack
, PsiPackage middlePack
, PsiPackage lastPack
) {
175 Set
<PsiFile
> result
= new HashSet
<PsiFile
>();
176 final Map
<PsiPackage
, Set
<PsiFile
>> forwardMap
= myFilesInDependentPackages
.get(firstPack
);
177 if (forwardMap
!= null && forwardMap
.get(middlePack
) != null){
178 result
.addAll(forwardMap
.get(middlePack
));
180 final Map
<PsiPackage
, Set
<PsiFile
>> backwardMap
= myBackwardFilesInDependentPackages
.get(lastPack
);
181 if (backwardMap
!= null && backwardMap
.get(middlePack
) != null){
182 result
.addAll(backwardMap
.get(middlePack
));
188 public HashMap
<PsiPackage
, Set
<List
<PsiPackage
>>> getCyclicDependencies() {
189 return myCyclicDependencies
;
192 public HashMap
<PsiPackage
, Set
<List
<PsiPackage
>>> getCycles(Collection
<PsiPackage
> packages
) {
193 if (myGraph
== null){
194 myGraph
= buildGraph();
196 final HashMap
<PsiPackage
, Set
<List
<PsiPackage
>>> result
= new HashMap
<PsiPackage
, Set
<List
<PsiPackage
>>>();
197 for (Iterator
<PsiPackage
> iterator
= packages
.iterator(); iterator
.hasNext();) {
198 PsiPackage psiPackage
= iterator
.next();
199 Set
<List
<PsiPackage
>> paths2Pack
= result
.get(psiPackage
);
200 if (paths2Pack
== null) {
201 paths2Pack
= new HashSet
<List
<PsiPackage
>>();
202 result
.put(psiPackage
, paths2Pack
);
204 paths2Pack
.addAll(CyclicGraphUtil
.getNodeCycles(myGraph
, psiPackage
));
209 public Map
<String
, PsiPackage
> getAllScopePackages() {
210 if (myPackages
.isEmpty()) {
211 final PsiManager psiManager
= PsiManager
.getInstance(getProject());
212 getScope().accept(new PsiRecursiveElementVisitor() {
213 public void visitFile(PsiFile file
) {
214 if (file
instanceof PsiJavaFile
) {
215 PsiJavaFile psiJavaFile
= (PsiJavaFile
)file
;
216 final PsiPackage aPackage
= psiManager
.findPackage(psiJavaFile
.getPackageName());
217 if (aPackage
!= null) {
218 myPackages
.put(aPackage
.getQualifiedName(), aPackage
);
228 private Graph
<PsiPackage
> buildGraph() {
229 final Graph
<PsiPackage
> graph
= GraphGenerator
.create(CachingSemiGraph
.create(new GraphGenerator
.SemiGraph
<PsiPackage
>() {
230 public Collection
<PsiPackage
> getNodes() {
231 return getAllScopePackages().values();
234 public Iterator
<PsiPackage
> getIn(PsiPackage psiPack
) {
235 final Set
<PsiPackage
> psiPackages
= myPackageDependencies
.get(psiPack
);
236 if (psiPackages
== null) { //for packs without java classes
237 return new HashSet
<PsiPackage
>().iterator();
239 return psiPackages
.iterator();
245 public Set
<PsiPackage
> getPackageHierarhy(String packageName
) {
246 final Set
<PsiPackage
> result
= new HashSet
<PsiPackage
>();
247 PsiPackage psiPackage
= findPackage(packageName
);
248 if (psiPackage
!= null) {
249 result
.add(psiPackage
);
254 while (psiPackage
.getParentPackage() != null && psiPackage
.getParentPackage().getQualifiedName().length() != 0) {
255 final PsiPackage aPackage
= findPackage(psiPackage
.getParentPackage().getQualifiedName());
256 if (aPackage
== null) {
259 result
.add(aPackage
);
260 psiPackage
= psiPackage
.getParentPackage();
265 private PsiPackage
findPackage(String packName
) {
266 final PsiPackage psiPackage
= getAllScopePackages().get(packName
);