mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
correct cyclic dependencies
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user