correct cyclic dependencies

This commit is contained in:
Anna Kozlova
2005-02-08 22:37:47 +03:00
parent c5e251cd25
commit 539bf7ab33
4 changed files with 204 additions and 43 deletions
@@ -2,24 +2,19 @@ package com.intellij.cyclicDependencies;
import com.intellij.analysis.AnalysisScope;
import com.intellij.compiler.Chunk;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ContentIterator;
import com.intellij.openapi.roots.FileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Pair;
import com.intellij.packageDependencies.ForwardDependenciesBuilder;
import com.intellij.packageDependencies.DependenciesBuilder;
import com.intellij.psi.*;
import com.intellij.util.graph.CachingSemiGraph;
import com.intellij.util.graph.DFSTBuilder;
import com.intellij.util.graph.Graph;
import com.intellij.util.graph.GraphGenerator;
import com.intellij.refactoring.typeCook.deductive.PsiTypeVariable;
import java.util.*;
@@ -33,6 +28,7 @@ public class CyclicDependenciesBuilder{
private Map<String, PsiPackage> myPackages = new HashMap<String, PsiPackage>();
private Graph<PsiPackage> myGraph;
private Map<PsiPackage, Map<PsiPackage, Set<PsiFile>>> myFilesInDependentPackages = new HashMap<PsiPackage, Map<PsiPackage, Set<PsiFile>>>();
private Map<PsiPackage, Map<PsiPackage, Set<PsiFile>>> myBackwardFilesInDependentPackages = new HashMap<PsiPackage, Map<PsiPackage, Set<PsiFile>>>();
private Map<PsiPackage, Set<PsiPackage>> myPackageDependencies = new HashMap<PsiPackage, Set<PsiPackage>>();
private HashMap<PsiPackage, Set<ArrayList<PsiPackage>>> myCyclicDependencies = new HashMap<PsiPackage, Set<ArrayList<PsiPackage>>>();
private int myFileCount = 0;
@@ -108,45 +104,32 @@ public class CyclicDependenciesBuilder{
myPackageDependencies.put(pack, pack2Packages);
}
for (Iterator<PsiFile> it = psiFiles.iterator(); it.hasNext();) {
PsiFile psiFile = it.next(); //bounded scope - package scope for files
PsiFile psiFile = it.next();
if (!(psiFile instanceof PsiJavaFile) ||
!projectFileIndex.isInSourceContent(psiFile.getVirtualFile()) ||
!getScope().contains(psiFile)) {
continue;
}
//construct all dependencies
Set<PsiFile> wholeDependencies = myForwardBuilder.getDependencies().get(psiJavaFile);
if (wholeDependencies == null) {
wholeDependencies = new HashSet<PsiFile>();
myForwardBuilder.getDependencies().put(psiJavaFile, wholeDependencies);
}
wholeDependencies.add(psiFile);
// construct dependent packages
final String packageName = ((PsiJavaFile)psiFile).getPackageName();
//do not depend on parent packages
if (packageName == null || packageName.startsWith(pack.getQualifiedName())) {
continue;
}
final PsiPackage depPackage = findPackage(packageName);
if (depPackage == null) { //not from analyze scope
final Set<PsiPackage> depPackages = getPackageHierarhy(packageName);
if (depPackages.isEmpty()) { //not from analyze scope
continue;
}
pack2Packages.add(depPackage);
pack2Packages.addAll(depPackages);
//construct files in dependent packages
Map<PsiPackage, Set<PsiFile>> dependentPackages2Files = myFilesInDependentPackages.get(pack);
if (dependentPackages2Files == null) {
dependentPackages2Files = new HashMap<PsiPackage, Set<PsiFile>>();
myFilesInDependentPackages.put(pack, dependentPackages2Files);
for (Iterator<PsiPackage> depIt = depPackages.iterator(); depIt.hasNext();) {
PsiPackage depPackage = depIt.next();
constractFilesInDependenciesPackagesMap(pack, depPackage, psiFile, myFilesInDependentPackages);
constractFilesInDependenciesPackagesMap(depPackage, pack, psiJavaFile, myBackwardFilesInDependentPackages);
}
Set<PsiFile> depFiles = dependentPackages2Files.get(depPackage);
if (depFiles == null) {
depFiles = new HashSet<PsiFile>();
dependentPackages2Files.put(depPackage, depFiles);
}
depFiles.add(psiFile);
constractWholeDependenciesMap(psiJavaFile, psiFile);
}
}
}
@@ -164,18 +147,135 @@ public class CyclicDependenciesBuilder{
myCyclicDependencies = getCycles(myPackages.values(), myPerPackageCycleCount);
}
private void constractFilesInDependenciesPackagesMap(final PsiPackage pack,
final PsiPackage depPackage,
final PsiFile file,
final Map<PsiPackage, Map<PsiPackage, Set<PsiFile>>> filesInDependentPackages) {
Map<PsiPackage, Set<PsiFile>> dependentPackages2Files = filesInDependentPackages.get(pack);
if (dependentPackages2Files == null) {
dependentPackages2Files = new HashMap<PsiPackage, Set<PsiFile>>();
filesInDependentPackages.put(pack, dependentPackages2Files);
}
Set<PsiFile> depFiles = dependentPackages2Files.get(depPackage);
if (depFiles == null) {
depFiles = new HashSet<PsiFile>();
dependentPackages2Files.put(depPackage, depFiles);
}
depFiles.add(file);
}
//construct all dependencies for usage view
private void constractWholeDependenciesMap(final PsiJavaFile psiJavaFile, final PsiFile psiFile) {
Set<PsiFile> wholeDependencies = myForwardBuilder.getDependencies().get(psiJavaFile);
if (wholeDependencies == null) {
wholeDependencies = new HashSet<PsiFile>();
myForwardBuilder.getDependencies().put(psiJavaFile, wholeDependencies);
}
wholeDependencies.add(psiFile);
}
public int getPerPackageCycleCount() {
return myPerPackageCycleCount;
}
public Set<PsiFile> getDependentFilesInPackage(PsiPackage pack, PsiPackage depPack) {
Set<PsiFile> psiFiles = myFilesInDependentPackages.get(pack).get(depPack);
Set<PsiFile> psiFiles = new HashSet<PsiFile>();
final Map<PsiPackage, Set<PsiFile>> map = myFilesInDependentPackages.get(pack);
if (map != null){
psiFiles = map.get(depPack);
}
if (psiFiles == null) {
psiFiles = new HashSet<PsiFile>();
}
return psiFiles;
}
public Set<PsiFile> getDependentFilesInPackage(PsiPackage firstPack, PsiPackage middlePack, PsiPackage lastPack) {
Set<PsiFile> result = new HashSet<PsiFile>();
final Map<PsiPackage, Set<PsiFile>> forwardMap = myFilesInDependentPackages.get(middlePack);
if (forwardMap != null && forwardMap.get(lastPack) != null){
result.addAll(forwardMap.get(lastPack));
}
final Map<PsiPackage, Set<PsiFile>> backwardMap = myBackwardFilesInDependentPackages.get(middlePack);
if (backwardMap != null && backwardMap.get(firstPack) != null){
result.addAll(backwardMap.get(firstPack));
}
return result;
}
/*public Set<PsiFile> getDependentFilesInPackage(PsiPackage pack, PsiPackage depPack) {
final Set<PsiFile> result = new HashSet<PsiFile>();
final Map<PsiFile, Set<PsiFile>> dependencies = myForwardBuilder.getDependencies();
final Set<PsiFile> allPsiPackageFiles = getAllPsiPackageFiles(pack);
for (Iterator<PsiFile> it = allPsiPackageFiles.iterator(); it.hasNext();) {
PsiFile file = it.next();
final Set<PsiFile> psiFiles = dependencies.get(file);
if (psiFiles == null){
continue;
}
for (Iterator<PsiFile> iterator = psiFiles.iterator(); iterator.hasNext();) {
PsiFile psiFile = iterator.next();
if (psiFile.getContainingDirectory().getPackage().getQualifiedName().startsWith(depPack.getQualifiedName())){
result.add(psiFile);
}
}
}
return result;
}
public Set<PsiFile> getDependentFilesInPackage(PsiPackage firstPack, PsiPackage middlePack, PsiPackage lastPack) {
Set<PsiFile> result = new HashSet<PsiFile>();
final Map<PsiFile, Set<PsiFile>> dependencies = myForwardBuilder.getDependencies();
final Set<PsiFile> allPsiPackageFiles = getAllPsiPackageFiles(middlePack);
for (Iterator<PsiFile> it = allPsiPackageFiles.iterator(); it.hasNext();) {
PsiFile file = it.next();
final Set<PsiFile> psiFiles = dependencies.get(file);
if (psiFiles == null){
continue;
}
for (Iterator<PsiFile> iterator = psiFiles.iterator(); iterator.hasNext();) {
PsiFile psiFile = iterator.next();
if (psiFile.getContainingDirectory().getPackage().getQualifiedName().startsWith(lastPack.getQualifiedName())){
result.add(file);
}
}
}
result.addAll(getDependentFilesInPackage(firstPack, middlePack));
return result;
}
private Set<PsiFile> getAllPsiPackageFiles(PsiPackage aPackage){
Set<PsiFile> result = new HashSet<PsiFile>();
final PsiDirectory[] directories = aPackage.getDirectories();
if (directories == null){
return result;
}
for (int i = 0; i < directories.length; i++) {
PsiDirectory directory = directories[i];
final PsiFile[] files = directory.getFiles();
if (files == null){
continue;
}
for (int j = 0; j < files.length; j++) {
PsiFile file = files[j];
if (getScope().contains(file)){
result.add(file);
}
}
final PsiDirectory[] subdirectories = directory.getSubdirectories();
if (subdirectories == null){
continue;
}
for (int j = 0; j < subdirectories.length; j++) {
PsiDirectory subdirectory = subdirectories[j];
result.addAll(getAllPsiPackageFiles(subdirectory.getPackage()));
}
}
return result;
}
*/
public HashMap<PsiPackage, Set<ArrayList<PsiPackage>>> getCyclicDependencies() {
return myCyclicDependencies;
}
@@ -188,6 +288,9 @@ public class CyclicDependenciesBuilder{
final List<Chunk<PsiPackage>> chunksByPackage = findChunksByPackage(psiPackage, chunks);
for (Iterator<Chunk<PsiPackage>> it = chunksByPackage.iterator(); it.hasNext();) {
Chunk<PsiPackage> chunk = it.next();
if (chunk.getNodes().size() == 1){
continue;
}
Set<ArrayList<PsiPackage>> paths2Pack = result.get(psiPackage);
if (paths2Pack == null) {
paths2Pack = new HashSet<ArrayList<PsiPackage>>();
@@ -10,7 +10,9 @@ import com.intellij.openapi.util.IconLoader;
import com.intellij.packageDependencies.DependencyUISettings;
import com.intellij.packageDependencies.DependencyValidationManager;
import com.intellij.packageDependencies.ui.*;
import com.intellij.psi.*;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiPackage;
import com.intellij.ui.*;
import com.intellij.ui.content.Content;
import com.intellij.util.EditSourceOnDoubleClickHandler;
@@ -20,7 +22,10 @@ import com.intellij.util.ui.tree.TreeUtil;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.*;
import java.util.*;
@@ -87,12 +92,18 @@ public class CyclicDependenciesPanel extends JPanel {
return;
}
final PackageDependenciesNode nextPackageNode = getNextPackageNode(selectedPackageNode);
Set<PsiFile> searchFor = myBuilder.getDependentFilesInPackage((PsiPackage)selectedPackageNode.getPsiElement(), ((PsiPackage)nextPackageNode.getPsiElement()));
Set<PsiFile> searchFor = new HashSet<PsiFile>();
Set<PackageNode> packNodes = new HashSet<PackageNode>();
getPackageNodesHierarchy(selectedPackageNode, packNodes);
for (Iterator<PackageNode> iterator = packNodes.iterator(); iterator.hasNext();) {
PackageNode packageNode = iterator.next();
searchFor.addAll(myBuilder.getDependentFilesInPackage((PsiPackage)packageNode.getPsiElement(), ((PsiPackage)nextPackageNode.getPsiElement())));
}
if (searchIn.isEmpty() || searchFor.isEmpty()) {
myUsagesPanel.setToInitialPosition();
}
else {
myBuilder.setRootNodeNameInUsageView("Usages of package \'" + nextPackageNode.toString() + "\' classes in package \'" + selectedPackageNode.toString() + "\' classes");
myBuilder.setRootNodeNameInUsageView("Usages of package \'" + ((PsiPackage)nextPackageNode.getPsiElement()).getQualifiedName() + "\' in package \'" + ((PsiPackage)selectedPackageNode.getPsiElement()).getQualifiedName() + "\'");
myUsagesPanel.findUsages(searchIn, searchFor);
}
}
@@ -104,10 +115,23 @@ public class CyclicDependenciesPanel extends JPanel {
initTree(myRightTree);
mySettings.UI_FILTER_LEGALS = false;
mySettings.UI_FLATTEN_PACKAGES = true;
mySettings.UI_FLATTEN_PACKAGES = false;
TreeUtil.selectFirstNode(myLeftTree);
}
private void getPackageNodesHierarchy(PackageNode node, Set<PackageNode> result){
result.add(node);
for (int i = 0; i < node.getChildCount(); i++){
final TreeNode child = node.getChildAt(i);
if (child instanceof PackageNode){
final PackageNode packNode = (PackageNode)child;
if (!result.contains(packNode)){
getPackageNodesHierarchy(packNode, result);
}
}
}
}
private PackageDependenciesNode getNextPackageNode(DefaultMutableTreeNode node) {
DefaultMutableTreeNode child = node;
while (node != null) {
@@ -128,6 +152,29 @@ public class CyclicDependenciesPanel extends JPanel {
return null;
}
private PackageDependenciesNode hideEmptyMiddlePackages(PackageDependenciesNode node, StringBuffer result){
if (node.getChildCount() > 1 || (node.getChildCount() == 1 && node.getChildAt(0) instanceof FileNode)){
result.append((result.length() != 0 ? ".":"") + (node.toString().equals("<default package>") ? "" : node.toString()));//toString()
} else {
if (node.getChildCount() == 1){
PackageDependenciesNode child = (PackageDependenciesNode)node.getChildAt(0);
if (!(node instanceof PackageNode)){
node.removeAllChildren();
child = hideEmptyMiddlePackages(child, result);
node.add(child);
} else {
if (node.getChildAt(0) instanceof PackageNode){
node.removeAllChildren();
result.append((result.length() != 0 ? ".":"") + (node.toString().equals("<default package>") ? "" : node.toString()));
node = hideEmptyMiddlePackages(child, result);
((PackageNode)node).setPackageName(result.toString());//toString()
}
}
}
}
return node;
}
private JComponent createToolbar() {
DefaultActionGroup group = new DefaultActionGroup();
group.add(new CloseAction());
@@ -171,6 +218,9 @@ public class CyclicDependenciesPanel extends JPanel {
PsiPackage psiPackage = iterator.next();
psiFiles.addAll(getPackageFiles(psiPackage));
}
boolean showFiles = mySettings.UI_SHOW_FILES; //do not show files in the left tree
mySettings.UI_FLATTEN_PACKAGES = true;
mySettings.UI_SHOW_FILES = false;
myLeftTreeExpantionMonitor.freeze();
myLeftTree.setModel(TreeModelBuilder.createTreeModel(myProject, false, psiFiles, new TreeModelBuilder.Marker() {
public boolean isMarked(PsiFile file) {
@@ -179,6 +229,8 @@ public class CyclicDependenciesPanel extends JPanel {
}, mySettings));
myLeftTreeExpantionMonitor.restore();
expandFirstLevel(myLeftTree);
mySettings.UI_SHOW_FILES = showFiles;
mySettings.UI_FLATTEN_PACKAGES = false;
}
private ActionGroup createTreePopupActions() {
@@ -202,14 +254,15 @@ public class CyclicDependenciesPanel extends JPanel {
for (int i = 0; i < packCycle.size(); i++) {
final PsiPackage psiPackage = packCycle.get(i);
PsiPackage nextPackage = packCycle.get(i == packCycle.size() - 1 ? 0 : i + 1);
final Set<PsiFile> dependentFilesInPackage = myBuilder.getDependentFilesInPackage(psiPackage, nextPackage);
PsiPackage prevPackage = packCycle.get(i == 0 ? packCycle.size() - 1 : i - 1);
final Set<PsiFile> dependentFilesInPackage = myBuilder.getDependentFilesInPackage(prevPackage, psiPackage, nextPackage);
final PackageDependenciesNode pack = (PackageDependenciesNode)TreeModelBuilder.createTreeModel(myProject, false, dependentFilesInPackage, new TreeModelBuilder.Marker() {
public boolean isMarked(PsiFile file) {
return false;
}
}, mySettings).getRoot();
nodes[i] = (PackageDependenciesNode)pack.getChildAt(0);
nodes[i] = hideEmptyMiddlePackages((PackageDependenciesNode)pack.getChildAt(0), new StringBuffer());
}
PackageDependenciesNode cycleNode = new CycleNode();
@@ -280,7 +333,7 @@ public class CyclicDependenciesPanel extends JPanel {
PackageDependenciesNode node = (PackageDependenciesNode)paths[0].getLastPathComponent();
if (node.isRoot()) return EMPTY_FILE_SET;
Set<PsiFile> result = new HashSet<PsiFile>();
node.fillFiles(result, !DependencyUISettings.getInstance().UI_FLATTEN_PACKAGES);
node.fillFiles(result, true);
return result;
}
@@ -26,7 +26,8 @@ public class FindDependencyUtil {
indicator.setText("Searching for usages: " + psiFile.getVirtualFile().getPresentableUrl());
}
final Set<PsiFile> deps = new HashSet<PsiFile>(builder.getDependencies().get(psiFile));
final Set<PsiFile> depsByFile = builder.getDependencies().get(psiFile);
final Set<PsiFile> deps = depsByFile != null ? new HashSet<PsiFile>(depsByFile) : new HashSet<PsiFile>();
deps.retainAll(searchFor);
if (deps.isEmpty()) continue;
@@ -44,6 +44,10 @@ public class PackageNode extends PackageDependenciesNode {
return myPackageName;
}
public void setPackageName(final String packageName) {
myPackageName = packageName;
}
public String getPackageQName() {
return myPackageQName;
}