mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-04 23:39:07 +07:00
add backward dependencies analyzer
This commit is contained in:
@@ -431,6 +431,7 @@
|
||||
<action id="InspectCode" class="com.intellij.codeInspection.actions.CodeInspectionAction" text="_Inspect Code..." description="Inspect code"/>
|
||||
<action id="ViewOfflineInspection" class="com.intellij.codeInspection.actions.ViewOfflineResultsAction" text="View _Offline Inspection Results..." description="Load offline inspection results"/>
|
||||
<action id="ShowPackageDeps" class="com.intellij.packageDependencies.actions.AnalyzeDependenciesAction" text="Analyze _Dependencies..." description="Browse code choosen analysis item depends on" />
|
||||
<action id="ShowBackwardPackageDeps" class="com.intellij.packageDependencies.actions.BackwardDependenciesAction" text="Analyze _Backward Dependencies..." description="Browse code choosen analysis item depends on" />
|
||||
<action id="DupLocate" class="com.intellij.dupLocator.DuplocateAction" text="_Locate Duplicates..." description="Locate duplicate code in project" />
|
||||
</group>
|
||||
|
||||
|
||||
@@ -10,17 +10,19 @@ package com.intellij.analysis;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.module.impl.ModuleUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ContentIterator;
|
||||
import com.intellij.openapi.roots.FileIndex;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.roots.*;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
import java.util.*;
|
||||
|
||||
public class AnalysisScope {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.analysis.AnalysisScope");
|
||||
@@ -39,6 +41,7 @@ public class AnalysisScope {
|
||||
private final int myType;
|
||||
private HashSet myFilesSet;
|
||||
|
||||
|
||||
public interface PsiFileFilter {
|
||||
boolean accept(PsiFile file);
|
||||
}
|
||||
@@ -143,21 +146,92 @@ public class AnalysisScope {
|
||||
}
|
||||
}
|
||||
|
||||
public AnalysisScope[] getNarrowedComplementaryScope(Project defaultProject) {
|
||||
if (myType == PROJECT) {
|
||||
return new AnalysisScope[]{new AnalysisScope(defaultProject, SOURCE_JAVA_FILES)};
|
||||
}
|
||||
final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(defaultProject).getFileIndex();
|
||||
final HashSet<Module> modules = new HashSet<Module>();
|
||||
if (myType == FILE) {
|
||||
if (myElement instanceof PsiJavaFile) {
|
||||
PsiJavaFile psiJavaFile = (PsiJavaFile)myElement;
|
||||
final PsiClass[] classes = psiJavaFile.getClasses();
|
||||
boolean onlyPackLocalClasses = true;
|
||||
for (int i = 0; i < classes.length; i++) {
|
||||
final PsiClass aClass = classes[i];
|
||||
if (aClass.hasModifierProperty(PsiModifier.PUBLIC)) {
|
||||
onlyPackLocalClasses = false;
|
||||
}
|
||||
}
|
||||
if (onlyPackLocalClasses) {
|
||||
return new AnalysisScope[]{new AnalysisScope(psiJavaFile.getContainingDirectory().getPackage(), SOURCE_JAVA_FILES)};
|
||||
}
|
||||
}
|
||||
final VirtualFile vFile = ((PsiFile)myElement).getVirtualFile();
|
||||
modules.addAll(getAllInterstingModules(fileIndex, vFile));
|
||||
}
|
||||
else if (myType == DIRECTORY) {
|
||||
final VirtualFile vFile = ((PsiDirectory)myElement).getVirtualFile();
|
||||
modules.addAll(getAllInterstingModules(fileIndex, vFile));
|
||||
}
|
||||
else if (myType == PACKAGE) {
|
||||
final PsiDirectory[] directories = ((PsiPackage)myElement).getDirectories();
|
||||
for (int idx = 0; idx < directories.length; idx++) {
|
||||
modules.addAll(getAllInterstingModules(fileIndex, directories[idx].getVirtualFile()));
|
||||
}
|
||||
}
|
||||
else if (myType == MODULE) {
|
||||
modules.add(myModule);
|
||||
}
|
||||
|
||||
if (modules.isEmpty()) {
|
||||
return new AnalysisScope[]{new AnalysisScope(defaultProject, SOURCE_JAVA_FILES)};
|
||||
}
|
||||
HashSet<AnalysisScope> result = new HashSet<AnalysisScope>();
|
||||
final Module[] allModules = ModuleManager.getInstance(defaultProject).getModules();
|
||||
for (int i = 0; i < allModules.length; i++) {
|
||||
for (Iterator<Module> iterator = modules.iterator(); iterator.hasNext();) {
|
||||
final Module module = iterator.next();
|
||||
if (allModules[i].equals(module)) {
|
||||
result.add(new AnalysisScope(allModules[i], SOURCE_JAVA_FILES));
|
||||
continue;
|
||||
}
|
||||
if (ModuleManager.getInstance(defaultProject).isModuleDependent(allModules[i], module)) {
|
||||
result.add(new AnalysisScope(allModules[i], SOURCE_JAVA_FILES));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toArray(new AnalysisScope[result.size()]);
|
||||
}
|
||||
|
||||
private HashSet<Module> getAllInterstingModules(final ProjectFileIndex fileIndex, final VirtualFile vFile) {
|
||||
final HashSet<Module> modules = new HashSet<Module>();
|
||||
if (fileIndex.isInLibrarySource(vFile) || fileIndex.isInLibraryClasses(vFile)) {
|
||||
final OrderEntry[] orderEntries = fileIndex.getOrderEntriesForFile(vFile);
|
||||
for (int j = 0; j < orderEntries.length; j++) {
|
||||
modules.add(orderEntries[j].getOwnerModule());
|
||||
}
|
||||
}
|
||||
else {
|
||||
modules.add(fileIndex.getModuleForFile(vFile));
|
||||
}
|
||||
return modules;
|
||||
}
|
||||
|
||||
|
||||
public void accept(final PsiElementVisitor visitor) {
|
||||
if (myProject != null) {
|
||||
final FileIndex projectFileIndex = ProjectRootManager.getInstance(myProject).getFileIndex();
|
||||
projectFileIndex.iterateContent(
|
||||
new ContentIterator() {
|
||||
public boolean processFile(VirtualFile fileOrDir) {
|
||||
if (projectFileIndex.isContentJavaSourceFile(fileOrDir)) {
|
||||
PsiFile psiFile = PsiManager.getInstance(myProject).findFile(fileOrDir);
|
||||
LOG.assertTrue(psiFile != null);
|
||||
psiFile.accept(visitor);
|
||||
}
|
||||
return true;
|
||||
projectFileIndex.iterateContent(new ContentIterator() {
|
||||
public boolean processFile(VirtualFile fileOrDir) {
|
||||
if (projectFileIndex.isContentJavaSourceFile(fileOrDir)) {
|
||||
PsiFile psiFile = PsiManager.getInstance(myProject).findFile(fileOrDir);
|
||||
LOG.assertTrue(psiFile != null);
|
||||
psiFile.accept(visitor);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
else if (myModule != null) {
|
||||
final FileIndex moduleFileIndex = ModuleRootManager.getInstance(myModule).getFileIndex();
|
||||
|
||||
@@ -23,9 +23,10 @@ import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.packageDependencies.DependenciesBuilder;
|
||||
import com.intellij.packageDependencies.ForwardDependenciesBuilder;
|
||||
import com.intellij.packageDependencies.DependencyRule;
|
||||
import com.intellij.packageDependencies.DependencyValidationManager;
|
||||
import com.intellij.packageDependencies.DependenciesBuilder;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.PsiSearchHelper;
|
||||
import com.intellij.psi.search.TodoItem;
|
||||
@@ -269,7 +270,7 @@ public class GeneralHighlightingPass extends TextEditorHighlightingPass {
|
||||
private void collectDependencyProblems(final List<HighlightInfo> list) {
|
||||
if (myUpdateAll && myFile instanceof PsiJavaFile && myFile.isPhysical() && myFile.getVirtualFile() != null &&
|
||||
mySettings.getInspectionProfile().isToolEnabled(HighlightDisplayKey.ILLEGAL_DEPENDENCY)) {
|
||||
DependenciesBuilder builder = new DependenciesBuilder(myProject, new AnalysisScope(myFile, AnalysisScope.SOURCE_JAVA_FILES));
|
||||
DependenciesBuilder builder = new ForwardDependenciesBuilder(myProject, new AnalysisScope(myFile, AnalysisScope.SOURCE_JAVA_FILES));
|
||||
final DependencyValidationManager validationManager = DependencyValidationManager.getInstance(myProject);
|
||||
builder.analyzeFileDependencies(myFile, new DependenciesBuilder.DependencyProcessor() {
|
||||
public void process(PsiElement place, PsiElement dependency) {
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.openapi.wm.StatusBar;
|
||||
import com.intellij.openapi.wm.WindowManager;
|
||||
import com.intellij.packageDependencies.ForwardDependenciesBuilder;
|
||||
import com.intellij.packageDependencies.DependenciesBuilder;
|
||||
import com.intellij.psi.PsiCompiledElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
@@ -1027,7 +1028,7 @@ public class CompileDriver {
|
||||
final TranslatingCompilerStateCache cache,
|
||||
VfsSnapshot snapshot,
|
||||
Set<String> sourcesWithOutputRemoved) {
|
||||
final DependenciesBuilder builder = new DependenciesBuilder(myProject, new AnalysisScope(psiFile, AnalysisScope.SOURCE_JAVA_FILES));
|
||||
final DependenciesBuilder builder = new ForwardDependenciesBuilder(myProject, new AnalysisScope(psiFile, AnalysisScope.SOURCE_JAVA_FILES));
|
||||
builder.analyze();
|
||||
final Map<PsiFile, Set<PsiFile>> dependencies = builder.getDependencies();
|
||||
final Set<PsiFile> dependentFiles = dependencies.get(psiFile);
|
||||
|
||||
@@ -64,7 +64,7 @@ public class ScopeEditorPanel {
|
||||
myTreeToolbar.setLayout(new BorderLayout());
|
||||
myTreeToolbar.add(createTreeToolbar(), BorderLayout.WEST);
|
||||
|
||||
myTreeExpantionMonitor = TreeExpantionMonitor.install(myPackageTree);
|
||||
myTreeExpantionMonitor = TreeExpantionMonitor.install(myPackageTree, myProject);
|
||||
|
||||
myTreeMarker = new TreeModelBuilder.Marker() {
|
||||
public boolean isMarked(PsiFile file) {
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.intellij.packageDependencies;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.analysis.AnalysisScope;
|
||||
import com.intellij.psi.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* User: anna
|
||||
* Date: Jan 16, 2005
|
||||
*/
|
||||
public class BackwardDependenciesBuilder extends DependenciesBuilder {
|
||||
|
||||
public BackwardDependenciesBuilder(final Project project, final AnalysisScope scope) {
|
||||
super(project, scope);
|
||||
}
|
||||
|
||||
public String getRootNodeNameInUsageView() {
|
||||
return "Usages of the left tree scope selection in the right tree scope selection";
|
||||
}
|
||||
|
||||
public String getInitialUsagesPosition() {
|
||||
return "Select where to search in right tree and what to search in left tree.";
|
||||
}
|
||||
|
||||
public boolean isBackward(){
|
||||
return true;
|
||||
}
|
||||
|
||||
public void analyze() {
|
||||
final AnalysisScope[] scopes = getScope().getNarrowedComplementaryScope(getProject());
|
||||
final DependenciesBuilder[] builders = new DependenciesBuilder[scopes.length];
|
||||
int totalCount = 0;
|
||||
for (int i = 0; i < scopes.length; i++) {
|
||||
AnalysisScope scope = scopes[i];
|
||||
totalCount += scope.getFileCount();
|
||||
}
|
||||
final int finalTotalFilesCount = totalCount;
|
||||
totalCount = 0;
|
||||
for (int i = 0; i < scopes.length; i++) {
|
||||
AnalysisScope scope = scopes[i];
|
||||
builders[i] = new ForwardDependenciesBuilder(getProject(), scope);
|
||||
builders[i].setInitialFileCount(totalCount);
|
||||
builders[i].setTotalFileCount(finalTotalFilesCount);
|
||||
builders[i].analyze();
|
||||
totalCount += scope.getFileCount();
|
||||
}
|
||||
|
||||
final PsiManager psiManager = PsiManager.getInstance(getProject());
|
||||
psiManager.startBatchFilesProcessingMode();
|
||||
try {
|
||||
getScope().accept(new PsiRecursiveElementVisitor() {
|
||||
public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
}
|
||||
|
||||
public void visitFile(final PsiFile file) {
|
||||
ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
|
||||
if (indicator != null) {
|
||||
if (indicator.isCanceled()) {
|
||||
throw new ProcessCanceledException();
|
||||
}
|
||||
indicator.setText("Analyzing package dependencies");
|
||||
indicator.setText2(file.getVirtualFile().getPresentableUrl());
|
||||
indicator.setFraction(((double) ++myFileCount) / getScope().getFileCount());
|
||||
}
|
||||
for (int i = 0; i < builders.length; i++) {
|
||||
final Map<PsiFile, Set<PsiFile>> dependencies = builders[i].getDependencies();
|
||||
for (Iterator<PsiFile> iterator = dependencies.keySet().iterator(); iterator.hasNext();) {
|
||||
final PsiFile psiFile = iterator.next();
|
||||
if (dependencies.get(psiFile).contains(file)) {
|
||||
Set<PsiFile> fileDeps = getDependencies().get(file);
|
||||
if (fileDeps == null) {
|
||||
fileDeps = new HashSet<PsiFile>();
|
||||
getDependencies().put(file, fileDeps);
|
||||
}
|
||||
fileDeps.add(psiFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
psiManager.dropResolveCaches();
|
||||
}
|
||||
});
|
||||
}
|
||||
finally {
|
||||
psiManager.finishBatchFilesProcessingMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,76 +1,59 @@
|
||||
package com.intellij.packageDependencies;
|
||||
|
||||
import com.intellij.analysis.AnalysisScope;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.javadoc.PsiDocComment;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.javadoc.PsiDocComment;
|
||||
import com.intellij.analysis.AnalysisScope;
|
||||
import com.intellij.openapi.project.Project;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class DependenciesBuilder {
|
||||
/**
|
||||
* User: anna
|
||||
* Date: Jan 19, 2005
|
||||
*/
|
||||
public abstract class DependenciesBuilder {
|
||||
private Project myProject;
|
||||
private final AnalysisScope myScope;
|
||||
private final Map<PsiFile, Set<PsiFile>> myDependencies = new HashMap<PsiFile, Set<PsiFile>>();
|
||||
private final int myTotalFileCount;
|
||||
private int myFileCount = 0;
|
||||
protected int myTotalFileCount;
|
||||
protected int myFileCount = 0;
|
||||
|
||||
public DependenciesBuilder(Project project, AnalysisScope scope) {
|
||||
protected DependenciesBuilder(final Project project, final AnalysisScope scope) {
|
||||
myProject = project;
|
||||
myScope = scope;
|
||||
myTotalFileCount = scope.getFileCount();
|
||||
}
|
||||
|
||||
public AnalysisScope getScope() {
|
||||
return myScope;
|
||||
protected void setInitialFileCount(final int fileCount) {
|
||||
myFileCount = fileCount;
|
||||
}
|
||||
|
||||
public void analyze() {
|
||||
final PsiManager psiManager = PsiManager.getInstance(myProject);
|
||||
psiManager.startBatchFilesProcessingMode();
|
||||
try {
|
||||
myScope.accept(new PsiRecursiveElementVisitor() {
|
||||
public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
}
|
||||
|
||||
public void visitFile(final PsiFile file) {
|
||||
ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
|
||||
if (indicator != null) {
|
||||
if (indicator.isCanceled()) {
|
||||
throw new ProcessCanceledException();
|
||||
}
|
||||
indicator.setText("Analyzing package dependencies");
|
||||
indicator.setText2(file.getVirtualFile().getPresentableUrl());
|
||||
indicator.setFraction(((double)++myFileCount) / myTotalFileCount);
|
||||
}
|
||||
|
||||
final Set<PsiFile> fileDeps = new HashSet<PsiFile>();
|
||||
myDependencies.put(file, fileDeps);
|
||||
analyzeFileDependencies(file, new DependencyProcessor() {
|
||||
public void process(PsiElement place, PsiElement dependency) {
|
||||
PsiFile dependencyFile = dependency.getContainingFile();
|
||||
if (dependencyFile != null && dependencyFile.isPhysical()) {
|
||||
fileDeps.add(dependencyFile);
|
||||
}
|
||||
}
|
||||
});
|
||||
psiManager.dropResolveCaches();
|
||||
}
|
||||
});
|
||||
}
|
||||
finally {
|
||||
psiManager.finishBatchFilesProcessingMode();
|
||||
}
|
||||
protected void setTotalFileCount(final int totalFileCount) {
|
||||
myTotalFileCount = totalFileCount;
|
||||
}
|
||||
|
||||
public Map<PsiFile, Set<PsiFile>> getDependencies() {
|
||||
return myDependencies;
|
||||
}
|
||||
|
||||
public Map<PsiFile, Map<DependencyRule, Set<PsiFile>>> getIllegalDependencies() {
|
||||
public AnalysisScope getScope() {
|
||||
return myScope;
|
||||
}
|
||||
|
||||
public Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
public abstract String getRootNodeNameInUsageView();
|
||||
|
||||
public abstract String getInitialUsagesPosition();
|
||||
|
||||
public abstract boolean isBackward();
|
||||
|
||||
public abstract void analyze();
|
||||
|
||||
public Map<PsiFile, Map<DependencyRule, Set<PsiFile>>> getIllegalDependencies(){
|
||||
Map<PsiFile, Map<DependencyRule, Set<PsiFile>>> result = new HashMap<PsiFile, Map<DependencyRule, Set<PsiFile>>>();
|
||||
DependencyValidationManager validator = DependencyValidationManager.getInstance(myProject);
|
||||
for (Iterator<PsiFile> iterator = myDependencies.keySet().iterator(); iterator.hasNext();) {
|
||||
@@ -79,7 +62,8 @@ public class DependenciesBuilder {
|
||||
Map<DependencyRule, Set<PsiFile>> illegal = null;
|
||||
for (Iterator<PsiFile> depsIterator = deps.iterator(); depsIterator.hasNext();) {
|
||||
PsiFile dependency = depsIterator.next();
|
||||
final DependencyRule rule = validator.getViolatorDependencyRule(file, dependency);
|
||||
final DependencyRule rule = isBackward() ? validator.getViolatorDependencyRule(dependency, file) :
|
||||
validator.getViolatorDependencyRule(file, dependency);
|
||||
if (rule != null) {
|
||||
if (illegal == null) {
|
||||
illegal = new HashMap<DependencyRule, Set<PsiFile>>();
|
||||
@@ -146,4 +130,4 @@ public class DependenciesBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,12 +70,7 @@ public class DependencyValidationManager extends NamedScopesHolder implements Pr
|
||||
});
|
||||
}
|
||||
|
||||
public void addContent(DependenciesBuilder builder) {
|
||||
DependenciesPanel panel = new DependenciesPanel(myProject, builder);
|
||||
Content content = PeerFactory.getInstance().getContentFactory().createContent(panel,
|
||||
"Dependencies of " + builder.getScope().getDisplayName(),
|
||||
false);
|
||||
panel.setContent(content);
|
||||
public void addContent(Content content) {
|
||||
myContentManager.addContent(content);
|
||||
myContentManager.setSelectedContent(content);
|
||||
ToolWindowManager.getInstance(myProject).getToolWindow(ToolWindowId.DEPENDENCIES).activate(null);
|
||||
|
||||
@@ -42,4 +42,40 @@ public class FindDependencyUtil {
|
||||
|
||||
return usages.toArray(new UsageInfo[usages.size()]);
|
||||
}
|
||||
|
||||
public static UsageInfo[] findBackwardDependencies(final DependenciesBuilder builder, final Set<PsiFile> searchIn, final Set<PsiFile> searchFor) {
|
||||
final List<UsageInfo> usages = new ArrayList<UsageInfo>();
|
||||
ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
|
||||
|
||||
|
||||
final Set<PsiFile> deps = new HashSet<PsiFile>();
|
||||
for (Iterator<PsiFile> iterator = searchFor.iterator(); iterator.hasNext();) {
|
||||
PsiFile psiFile = iterator.next();
|
||||
deps.addAll(builder.getDependencies().get(psiFile));
|
||||
}
|
||||
deps.retainAll(searchIn);
|
||||
if (deps.isEmpty()) return new UsageInfo[0];
|
||||
|
||||
int totalCount = deps.size();
|
||||
int count = 0;
|
||||
for (Iterator<PsiFile> inIterator = deps.iterator(); inIterator.hasNext();) {
|
||||
final PsiFile psiFile = inIterator.next();
|
||||
if (indicator != null) {
|
||||
if (indicator.isCanceled()) throw new ProcessCanceledException();
|
||||
indicator.setFraction(((double)++count)/totalCount);
|
||||
indicator.setText("Searching for usages in: " + psiFile.getVirtualFile().getPresentableUrl());
|
||||
}
|
||||
|
||||
builder.analyzeFileDependencies(psiFile, new DependenciesBuilder.DependencyProcessor() {
|
||||
public void process(PsiElement place, PsiElement dependency) {
|
||||
PsiFile dependencyFile = dependency.getContainingFile();
|
||||
if (searchFor.contains(dependencyFile)) {
|
||||
usages.add(new UsageInfo(place));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return usages.toArray(new UsageInfo[usages.size()]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.intellij.packageDependencies;
|
||||
|
||||
import com.intellij.analysis.AnalysisScope;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ForwardDependenciesBuilder extends DependenciesBuilder {
|
||||
|
||||
public ForwardDependenciesBuilder(Project project, AnalysisScope scope) {
|
||||
super(project, scope);
|
||||
}
|
||||
|
||||
public String getRootNodeNameInUsageView(){
|
||||
return "Usages of the right tree scope selection in the left tree scope selection";
|
||||
}
|
||||
|
||||
public String getInitialUsagesPosition(){
|
||||
return "Select where to search in left tree and what to search in right tree.";
|
||||
}
|
||||
|
||||
public boolean isBackward(){
|
||||
return false;
|
||||
}
|
||||
|
||||
public void analyze() {
|
||||
final PsiManager psiManager = PsiManager.getInstance(getProject());
|
||||
psiManager.startBatchFilesProcessingMode();
|
||||
try {
|
||||
getScope().accept(new PsiRecursiveElementVisitor() {
|
||||
public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
}
|
||||
|
||||
public void visitFile(final PsiFile file) {
|
||||
ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
|
||||
if (indicator != null) {
|
||||
if (indicator.isCanceled()) {
|
||||
throw new ProcessCanceledException();
|
||||
}
|
||||
indicator.setText("Analyzing package dependencies");
|
||||
indicator.setText2(file.getVirtualFile().getPresentableUrl());
|
||||
indicator.setFraction(((double)++ myFileCount) / myTotalFileCount);
|
||||
}
|
||||
|
||||
final Set<PsiFile> fileDeps = new HashSet<PsiFile>();
|
||||
getDependencies().put(file, fileDeps);
|
||||
analyzeFileDependencies(file, new DependencyProcessor() {
|
||||
public void process(PsiElement place, PsiElement dependency) {
|
||||
PsiFile dependencyFile = dependency.getContainingFile();
|
||||
if (dependencyFile != null && dependencyFile.isPhysical()) {
|
||||
fileDeps.add(dependencyFile);
|
||||
}
|
||||
}
|
||||
});
|
||||
psiManager.dropResolveCaches();
|
||||
}
|
||||
});
|
||||
}
|
||||
finally {
|
||||
psiManager.finishBatchFilesProcessingMode();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,8 +3,12 @@ package com.intellij.packageDependencies.actions;
|
||||
import com.intellij.analysis.AnalysisScope;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.packageDependencies.DependenciesBuilder;
|
||||
import com.intellij.packageDependencies.DependencyValidationManager;
|
||||
import com.intellij.packageDependencies.ForwardDependenciesBuilder;
|
||||
import com.intellij.packageDependencies.DependenciesBuilder;
|
||||
import com.intellij.packageDependencies.ui.DependenciesPanel;
|
||||
import com.intellij.ui.content.Content;
|
||||
import com.intellij.peer.PeerFactory;
|
||||
|
||||
public class AnalyzeDependenciesHandler {
|
||||
private Project myProject;
|
||||
@@ -16,13 +20,18 @@ public class AnalyzeDependenciesHandler {
|
||||
}
|
||||
|
||||
public void analyze() {
|
||||
final DependenciesBuilder builder = new DependenciesBuilder(myProject, myScope);
|
||||
final DependenciesBuilder forwardBuilder = new ForwardDependenciesBuilder(myProject, myScope);
|
||||
if (ApplicationManager.getApplication().runProcessWithProgressSynchronously(new Runnable() {
|
||||
public void run() {
|
||||
builder.analyze();
|
||||
forwardBuilder.analyze();
|
||||
}
|
||||
}, "Analyzing Dependencies", true, myProject)) {
|
||||
DependencyValidationManager.getInstance(myProject).addContent(builder);
|
||||
DependenciesPanel panel = new DependenciesPanel(myProject, forwardBuilder);
|
||||
Content content = PeerFactory.getInstance().getContentFactory().createContent(panel,
|
||||
"Dependencies of " + forwardBuilder.getScope().getDisplayName(),
|
||||
false);
|
||||
panel.setContent(content);
|
||||
DependencyValidationManager.getInstance(myProject).addContent(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.intellij.packageDependencies.actions;
|
||||
|
||||
import com.intellij.analysis.BaseAnalysisAction;
|
||||
import com.intellij.analysis.AnalysisScope;
|
||||
import com.intellij.openapi.project.Project;
|
||||
|
||||
/**
|
||||
* User: anna
|
||||
* Date: Jan 16, 2005
|
||||
*/
|
||||
public class BackwardDependenciesAction extends BaseAnalysisAction{
|
||||
public BackwardDependenciesAction() {
|
||||
super(AnalysisScope.SOURCE_JAVA_FILES, "Backward Dependency Analysis", "Analyze", "Analysis");
|
||||
}
|
||||
|
||||
protected void analyze(Project project, AnalysisScope scope) {
|
||||
new BackwardDependenciesHandler(project, scope).analyze();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.intellij.packageDependencies.actions;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.analysis.AnalysisScope;
|
||||
import com.intellij.packageDependencies.DependencyValidationManager;
|
||||
import com.intellij.packageDependencies.BackwardDependenciesBuilder;
|
||||
import com.intellij.packageDependencies.DependenciesBuilder;
|
||||
import com.intellij.packageDependencies.ui.DependenciesPanel;
|
||||
import com.intellij.ui.content.Content;
|
||||
import com.intellij.peer.PeerFactory;
|
||||
|
||||
/**
|
||||
* User: anna
|
||||
* Date: Jan 16, 2005
|
||||
*/
|
||||
public class BackwardDependenciesHandler {
|
||||
private Project myProject;
|
||||
private AnalysisScope myScope;
|
||||
|
||||
public BackwardDependenciesHandler(Project project, AnalysisScope scope) {
|
||||
myProject = project;
|
||||
myScope = scope;
|
||||
}
|
||||
|
||||
public void analyze() {
|
||||
final DependenciesBuilder builder = new BackwardDependenciesBuilder(myProject, myScope);
|
||||
|
||||
if (ApplicationManager.getApplication().runProcessWithProgressSynchronously(new Runnable() {
|
||||
public void run() {
|
||||
builder.analyze();
|
||||
}
|
||||
}, "Analyzing Backward Dependencies", true, myProject)) {
|
||||
DependenciesPanel panel = new DependenciesPanel(myProject, builder);
|
||||
Content content = PeerFactory.getInstance().getContentFactory().createContent(panel,
|
||||
"Backward Dependencies of " + builder.getScope().getDisplayName(),
|
||||
false);
|
||||
panel.setContent(content);
|
||||
DependencyValidationManager.getInstance(myProject).addContent(content);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,30 +12,29 @@ import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
import com.intellij.openapi.wm.StatusBar;
|
||||
import com.intellij.openapi.wm.WindowManager;
|
||||
import com.intellij.packageDependencies.DependenciesBuilder;
|
||||
import com.intellij.packageDependencies.DependencyRule;
|
||||
import com.intellij.packageDependencies.DependencyUISettings;
|
||||
import com.intellij.packageDependencies.DependencyValidationManager;
|
||||
import com.intellij.packageDependencies.*;
|
||||
import com.intellij.packageDependencies.actions.AnalyzeDependenciesHandler;
|
||||
import com.intellij.packageDependencies.actions.BackwardDependenciesHandler;
|
||||
import com.intellij.pom.Navigatable;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.ui.*;
|
||||
import com.intellij.ui.content.Content;
|
||||
import com.intellij.util.Icons;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ui.Tree;
|
||||
import com.intellij.util.ui.tree.TreeUtil;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.TreeSelectionEvent;
|
||||
import javax.swing.event.TreeSelectionListener;
|
||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||
import javax.swing.tree.DefaultTreeModel;
|
||||
import javax.swing.tree.TreePath;
|
||||
import javax.swing.tree.TreeSelectionModel;
|
||||
import javax.swing.event.TreeExpansionListener;
|
||||
import javax.swing.event.TreeExpansionEvent;
|
||||
import javax.swing.tree.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
public class DependenciesPanel extends JPanel {
|
||||
private Map<PsiFile, Set<PsiFile>> myDependencies;
|
||||
@@ -64,7 +63,7 @@ public class DependenciesPanel extends JPanel {
|
||||
myBuilder = builder;
|
||||
myIllegalDependencies = myBuilder.getIllegalDependencies();
|
||||
myProject = project;
|
||||
myUsagesPanel = new UsagesPanel(myProject);
|
||||
myUsagesPanel = new UsagesPanel(myProject, myBuilder);
|
||||
|
||||
Splitter treeSplitter = new Splitter();
|
||||
treeSplitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myLeftTree));
|
||||
@@ -76,8 +75,8 @@ public class DependenciesPanel extends JPanel {
|
||||
add(splitter, BorderLayout.CENTER);
|
||||
add(createToolbar(), BorderLayout.NORTH);
|
||||
|
||||
myRightTreeExpantionMonitor = TreeExpantionMonitor.install(myRightTree);
|
||||
myLeftTreeExpantionMonitor = TreeExpantionMonitor.install(myLeftTree);
|
||||
myRightTreeExpantionMonitor = TreeExpantionMonitor.install(myRightTree, myProject);
|
||||
myLeftTreeExpantionMonitor = TreeExpantionMonitor.install(myLeftTree, myProject);
|
||||
|
||||
myRightTreeMarker = new TreeModelBuilder.Marker() {
|
||||
public boolean isMarked(PsiFile file) {
|
||||
@@ -100,7 +99,7 @@ public class DependenciesPanel extends JPanel {
|
||||
final StringBuffer denyRules = new StringBuffer();
|
||||
final StringBuffer allowRules = new StringBuffer();
|
||||
final TreePath selectionPath = myLeftTree.getSelectionPath();
|
||||
if (selectionPath == null){
|
||||
if (selectionPath == null) {
|
||||
return;
|
||||
}
|
||||
PackageDependenciesNode selectedNode = (PackageDependenciesNode)selectionPath.getLastPathComponent();
|
||||
@@ -108,10 +107,10 @@ public class DependenciesPanel extends JPanel {
|
||||
final StatusBar statusBar = WindowManager.getInstance().getStatusBar(myProject);
|
||||
if (denyRules.length() + allowRules.length() > 0) {
|
||||
statusBar.setInfo("The following rule" +
|
||||
((denyRules.length() == 0 || allowRules.length() == 0) ? " is " : "s are ") +
|
||||
"violated: " +
|
||||
(denyRules.length() > 0 ? denyRules.toString() + (allowRules.length() > 0 ? "; " : "") : " ") +
|
||||
(allowRules.length() > 0 ? allowRules.toString() : " "));
|
||||
((denyRules.length() == 0 || allowRules.length() == 0) ? " is " : "s are ") +
|
||||
"violated: " +
|
||||
(denyRules.length() > 0 ? denyRules.toString() + (allowRules.length() > 0 ? "; " : "") : " ") +
|
||||
(allowRules.length() > 0 ? allowRules.toString() : " "));
|
||||
|
||||
}
|
||||
else {
|
||||
@@ -130,7 +129,7 @@ public class DependenciesPanel extends JPanel {
|
||||
myUsagesPanel.setToInitialPosition();
|
||||
}
|
||||
else {
|
||||
myUsagesPanel.findUsages(builder, searchIn, searchFor);
|
||||
myUsagesPanel.findUsages(searchIn, searchFor);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -150,23 +149,24 @@ public class DependenciesPanel extends JPanel {
|
||||
}
|
||||
|
||||
private void traverseToLeaves(final PackageDependenciesNode treeNode, final StringBuffer denyRules, final StringBuffer allowRules) {
|
||||
for (int i = 0; i < treeNode.getChildCount(); i++) {
|
||||
traverseToLeaves((PackageDependenciesNode)treeNode.getChildAt(i), denyRules, allowRules);
|
||||
}
|
||||
if (myIllegalDependencies.containsKey(treeNode.getPsiElement())) {
|
||||
final Map<DependencyRule, Set<PsiFile>> illegalDeps = myIllegalDependencies.get(treeNode.getPsiElement());
|
||||
for (Iterator<DependencyRule> iterator = illegalDeps.keySet().iterator(); iterator.hasNext();) {
|
||||
final DependencyRule rule = iterator.next();
|
||||
if (rule.isDenyRule()) {
|
||||
if (denyRules.indexOf(rule.getDisplayText()) == -1) {
|
||||
denyRules.append(rule.getDisplayText());
|
||||
denyRules.append("\n");
|
||||
final Enumeration enumeration = treeNode.breadthFirstEnumeration();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
PsiElement childPsiElement = ((PackageDependenciesNode)enumeration.nextElement()).getPsiElement();
|
||||
if (myIllegalDependencies.containsKey(childPsiElement)) {
|
||||
final Map<DependencyRule, Set<PsiFile>> illegalDeps = myIllegalDependencies.get(childPsiElement);
|
||||
for (Iterator<DependencyRule> iterator = illegalDeps.keySet().iterator(); iterator.hasNext();) {
|
||||
final DependencyRule rule = iterator.next();
|
||||
if (rule.isDenyRule()) {
|
||||
if (denyRules.indexOf(rule.getDisplayText()) == -1) {
|
||||
denyRules.append(rule.getDisplayText());
|
||||
denyRules.append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (allowRules.indexOf(rule.getDisplayText()) == -1) {
|
||||
allowRules.append(rule.getDisplayText());
|
||||
allowRules.append("\n");
|
||||
else {
|
||||
if (allowRules.indexOf(rule.getDisplayText()) == -1) {
|
||||
allowRules.append(rule.getDisplayText());
|
||||
allowRules.append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -448,7 +448,12 @@ public class DependenciesPanel extends JPanel {
|
||||
DependencyValidationManager.getInstance(myProject).closeContent(myContent);
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
new AnalyzeDependenciesHandler(myProject, myBuilder.getScope()).analyze();
|
||||
if (myBuilder.isBackward()) {
|
||||
new BackwardDependenciesHandler(myProject, myBuilder.getScope()).analyze();
|
||||
}
|
||||
else {
|
||||
new AnalyzeDependenciesHandler(myProject, myBuilder.getScope()).analyze();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,34 +1,39 @@
|
||||
package com.intellij.packageDependencies.ui;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiManager;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.TreeExpansionEvent;
|
||||
import javax.swing.event.TreeExpansionListener;
|
||||
import javax.swing.event.TreeSelectionEvent;
|
||||
import javax.swing.event.TreeSelectionListener;
|
||||
import javax.swing.tree.TreePath;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import java.util.*;
|
||||
|
||||
public class TreeExpantionMonitor {
|
||||
public static TreeExpantionMonitor install(JTree tree) {
|
||||
return new TreeExpantionMonitor(tree);
|
||||
public static TreeExpantionMonitor install(JTree tree, Project project) {
|
||||
return new TreeExpantionMonitor(tree, project);
|
||||
}
|
||||
|
||||
private Set<TreePath> myExpandedPaths = new HashSet<TreePath>();
|
||||
private List<TreePath> mySelectionPath = new ArrayList<TreePath>();
|
||||
private List<PackageDependenciesNode> mySelectionNodes = new ArrayList<PackageDependenciesNode>();
|
||||
private JTree myTree;
|
||||
private boolean myFrozen = false;
|
||||
private Project myProject;
|
||||
|
||||
|
||||
private TreeExpantionMonitor(JTree tree) {
|
||||
private TreeExpantionMonitor(JTree tree, Project project) {
|
||||
myTree = tree;
|
||||
myProject = project;
|
||||
myTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
|
||||
public void valueChanged(TreeSelectionEvent e) {
|
||||
if (myFrozen) return;
|
||||
mySelectionPath = new ArrayList<TreePath>();
|
||||
mySelectionNodes = new ArrayList<PackageDependenciesNode>();
|
||||
TreePath[] paths = myTree.getSelectionPaths();
|
||||
if (paths != null) {
|
||||
for (int i = 0; i < paths.length; i++) {
|
||||
mySelectionPath.add(paths[i]);
|
||||
mySelectionNodes.add((PackageDependenciesNode)paths[i].getLastPathComponent());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,15 +68,28 @@ public class TreeExpantionMonitor {
|
||||
myFrozen = true;
|
||||
}
|
||||
|
||||
|
||||
public void restore() {
|
||||
freeze();
|
||||
for (int i = 0; i < mySelectionPath.size(); i++) {
|
||||
TreePath treePath = mySelectionPath.get(i);
|
||||
myTree.getSelectionModel().addSelectionPath(treePath);
|
||||
}
|
||||
for (Iterator<TreePath> iterator = myExpandedPaths.iterator(); iterator.hasNext();) {
|
||||
myTree.expandPath(iterator.next());
|
||||
}
|
||||
myFrozen = false;
|
||||
freeze();
|
||||
for (int i = 0; i < mySelectionNodes.size(); i++) {
|
||||
myTree.getSelectionModel().addSelectionPath(findPathByNode(mySelectionNodes.get(i)));
|
||||
}
|
||||
for (Iterator<TreePath> iterator = myExpandedPaths.iterator(); iterator.hasNext();) {
|
||||
myTree.expandPath(iterator.next());
|
||||
}
|
||||
myFrozen = false;
|
||||
}
|
||||
|
||||
|
||||
private TreePath findPathByNode(final PackageDependenciesNode node) {
|
||||
PsiManager manager = PsiManager.getInstance(myProject);
|
||||
Enumeration enumeration = ((DefaultMutableTreeNode)myTree.getModel().getRoot()).breadthFirstEnumeration();
|
||||
while (enumeration.hasMoreElements()) {
|
||||
PackageDependenciesNode child = (PackageDependenciesNode)enumeration.nextElement();
|
||||
if (manager.areElementsEquivalent(child.getPsiElement(), node.getPsiElement())) {
|
||||
return new TreePath(child.getPath());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,9 @@ import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.util.ColorProgressBar;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.packageDependencies.DependenciesBuilder;
|
||||
import com.intellij.packageDependencies.FindDependencyUtil;
|
||||
import com.intellij.packageDependencies.BackwardDependenciesBuilder;
|
||||
import com.intellij.packageDependencies.DependenciesBuilder;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.usages.*;
|
||||
@@ -24,23 +25,24 @@ public class UsagesPanel extends JPanel {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.packageDependencies.ui.UsagesPanel");
|
||||
|
||||
private Project myProject;
|
||||
|
||||
private DependenciesBuilder myBuilder;
|
||||
private ProgressIndicator myCurrentProgress;
|
||||
private JComponent myCurrentComponent;
|
||||
private Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
|
||||
|
||||
public UsagesPanel(Project project) {
|
||||
public UsagesPanel(Project project, DependenciesBuilder builder) {
|
||||
super(new BorderLayout());
|
||||
myProject = project;
|
||||
myBuilder = builder;
|
||||
setToInitialPosition();
|
||||
}
|
||||
|
||||
public void setToInitialPosition() {
|
||||
cancelCurrentFindRequest();
|
||||
setToComponent(createLabel("Select where to search in left tree and what to search in right tree."));
|
||||
setToComponent(createLabel(myBuilder.getInitialUsagesPosition()));
|
||||
}
|
||||
|
||||
public void findUsages(final DependenciesBuilder builder, final Set<PsiFile> searchIn, final Set<PsiFile> searchFor) {
|
||||
public void findUsages(final Set<PsiFile> searchIn, final Set<PsiFile> searchFor) {
|
||||
cancelCurrentFindRequest();
|
||||
|
||||
myAlarm.cancelAllRequests();
|
||||
@@ -56,7 +58,11 @@ public class UsagesPanel extends JPanel {
|
||||
public void run() {
|
||||
UsageInfo[] usages = new UsageInfo[0];
|
||||
try {
|
||||
usages = FindDependencyUtil.findDependencies(builder, searchIn, searchFor);
|
||||
if (myBuilder.isBackward()){
|
||||
usages = FindDependencyUtil.findBackwardDependencies(myBuilder, searchFor, searchIn);
|
||||
} else {
|
||||
usages = FindDependencyUtil.findDependencies(myBuilder, searchIn, searchFor);
|
||||
}
|
||||
}
|
||||
catch (ProcessCanceledException e) {
|
||||
}
|
||||
@@ -93,7 +99,7 @@ public class UsagesPanel extends JPanel {
|
||||
try {
|
||||
Usage[] usages = UsageInfoToUsageConverter.convert(usageInfos);
|
||||
UsageViewPresentation presentation = new UsageViewPresentation();
|
||||
presentation.setCodeUsagesString("Usages of the right tree scope selection in the left tree scope selection");
|
||||
presentation.setCodeUsagesString(myBuilder.getRootNodeNameInUsageView());
|
||||
UsageView usageView = myProject.getComponent(UsageViewManager.class).createUsageView(new UsageTarget[0],
|
||||
usages, presentation);
|
||||
setToComponent(usageView.getComponent());
|
||||
@@ -181,4 +187,4 @@ public class UsagesPanel extends JPanel {
|
||||
public JLabel myTextLabel;
|
||||
public JPanel myPanel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user