IDEADEV-21222
[fedora-idea.git] / source / com / intellij / cyclicDependencies / CyclicDependenciesBuilder.java
blob8b09c41c697bf1948ad4b09f8a54e62ed067b75b
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;
17 import java.util.*;
19 /**
20 * User: anna
21 * Date: Jan 30, 2005
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) {
38 myProject = project;
39 myScope = 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() {
60 return myProject;
63 public AnalysisScope getScope() {
64 return myScope;
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);
87 builder.analyze();
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)) {
100 continue;
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())) {
107 continue;
109 final PsiPackage depPackage = findPackage(packageName);
110 if (depPackage == null) { //not from analyze scope
111 continue;
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,
137 final PsiFile file,
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);
149 depFiles.add(file);
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);
165 if (map != null){
166 psiFiles = map.get(depPack);
168 if (psiFiles == null) {
169 psiFiles = new HashSet<PsiFile>();
171 return psiFiles;
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));
184 return result;
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));
206 return result;
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);
224 return myPackages;
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();
241 }));
242 return graph;
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);
251 else {
252 return result;
254 while (psiPackage.getParentPackage() != null && psiPackage.getParentPackage().getQualifiedName().length() != 0) {
255 final PsiPackage aPackage = findPackage(psiPackage.getParentPackage().getQualifiedName());
256 if (aPackage == null) {
257 break;
259 result.add(aPackage);
260 psiPackage = psiPackage.getParentPackage();
262 return result;
265 private PsiPackage findPackage(String packName) {
266 final PsiPackage psiPackage = getAllScopePackages().get(packName);
267 return psiPackage;