DependencyFinder.java
/*
* Copyright (c) 2001-2024, Jean Tessier
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Jean Tessier nor the names of his contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jeantessier.dependencyfinder.gui;
import com.jeantessier.classreader.ClassfileLoaderDispatcher;
import com.jeantessier.classreader.ClassfileLoaderEventSource;
import com.jeantessier.classreader.ModifiedOnlyDispatcher;
import com.jeantessier.classreader.Monitor;
import com.jeantessier.commandline.CommandLine;
import com.jeantessier.commandline.CommandLineUsage;
import com.jeantessier.commandline.NullParameterStrategy;
import com.jeantessier.dependency.*;
import com.jeantessier.dependency.TextMetricsReport;
import org.apache.logging.log4j.*;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
public class DependencyFinder extends JFrame {
private static final String DEFAULT_FONT_NAME = "Monospaced";
private static final String DESIRED_FONT_NAME = "Courier";
private boolean minimize;
private boolean maximize;
private boolean advancedMode;
private final JPanel queryPanel = new JPanel();
private final JMenuBar menuBar = new JMenuBar();
private final JMenu fileMenu = new JMenu();
private final JMenu viewMenu = new JMenu();
private final JMenu helpMenu = new JMenu();
private final JToolBar toolbar = new JToolBar();
private final JTextArea dependenciesResultArea = new JTextArea();
private final JTextArea closureResultArea = new JTextArea();
private final JTextArea metricsResultArea = new JTextArea();
private final MetricsTableModel metricsChartModel = new MetricsTableModel();
private final StatusLine statusLine = new StatusLine(420);
private final JProgressBar progressBar = new JProgressBar();
private Collection<String> inputFiles = null;
private ClassfileLoaderDispatcher dispatcher = null;
private NodeFactory nodeFactory = null;
private Monitor monitor = null;
private final JCheckBox packageScope = new JCheckBox("packages");
private final JCheckBox classScope = new JCheckBox("classes");
private final JCheckBox featureScope = new JCheckBox("features");
private final JTextField scopeIncludes = new JTextField();
private final JTextField packageScopeIncludes = new JTextField();
private final JTextField classScopeIncludes = new JTextField();
private final JTextField featureScopeIncludes = new JTextField();
private final JTextField scopeExcludes = new JTextField();
private final JTextField packageScopeExcludes = new JTextField();
private final JTextField classScopeExcludes = new JTextField();
private final JTextField featureScopeExcludes = new JTextField();
private final JCheckBox packageFilter = new JCheckBox("packages");
private final JCheckBox classFilter = new JCheckBox("classes");
private final JCheckBox featureFilter = new JCheckBox("features");
private final JTextField filterIncludes = new JTextField();
private final JTextField packageFilterIncludes = new JTextField();
private final JTextField classFilterIncludes = new JTextField();
private final JTextField featureFilterIncludes = new JTextField();
private final JTextField filterExcludes = new JTextField();
private final JTextField packageFilterExcludes = new JTextField();
private final JTextField classFilterExcludes = new JTextField();
private final JTextField featureFilterExcludes = new JTextField();
private final JCheckBox showInbounds = new JCheckBox("<--");
private final JCheckBox showOutbounds = new JCheckBox("-->");
private final JCheckBox showEmptyNodes = new JCheckBox("empty elements");
private final JCheckBox copyOnly = new JCheckBox("copy only");
private final JTextField maximumInboundDepth = new JTextField("0", 2);
private final JTextField maximumOutboundDepth = new JTextField(2);
private GraphCopier dependenciesQuery = null;
public DependencyFinder(CommandLine commandLine) {
this.setSize(new Dimension(800, 600));
this.setTitle("Dependency Finder");
this.setIconImage(new ImageIcon(getClass().getResource("icons/logoicon.gif")).getImage());
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
this.addWindowListener(new WindowKiller());
setNewDependencyGraph();
packageScope.setToolTipText("Select packages");
classScope.setToolTipText("Select classes (with their package)");
featureScope.setToolTipText("Select methods and fields (with their class and package)");
scopeIncludes.setToolTipText("Package, class, method, or field must match any these expressions. E.g., /^com.mycompany/, /\\.get\\w+\\(/");
packageScopeIncludes.setToolTipText("Package must match any these expressions. E.g., /^com.mycompany/, /\\.get\\w+\\(/");
classScopeIncludes.setToolTipText("Class must match any these expressions. E.g., /^com.mycompany/, /\\.get\\w+\\(/");
featureScopeIncludes.setToolTipText("Method or field must match any these expressions. E.g., /^com.mycompany/, /\\.get\\w+\\(/");
scopeExcludes.setToolTipText("Package, class, method, or field must NOT match any of these expressions. E.g., /Test/");
packageScopeExcludes.setToolTipText("Package must NOT match any of these expressions. E.g., /Test/");
classScopeExcludes.setToolTipText("Class must NOT match any of these expressions. E.g., /Test/");
featureScopeExcludes.setToolTipText("Method or field must NOT match any of these expressions. E.g., /Test/");
packageFilter.setToolTipText("Show dependencies to/from packages");
classFilter.setToolTipText("Show dependencies to/from classes");
featureFilter.setToolTipText("Show dependencies to/from methods and fields");
filterIncludes.setToolTipText("Package, class, method, or field at the other end of the dependency must match any these expressions. E.g., /^com.mycompany/, /\\.get\\w+\\(/");
packageFilterIncludes.setToolTipText("Package at the other end of the dependency must match any these expressions. E.g., /^com.mycompany/, /\\.get\\w+\\(/");
classFilterIncludes.setToolTipText("Class at the other end of the dependency must match any these expressions. E.g., /^com.mycompany/, /\\.get\\w+\\(/");
featureFilterIncludes.setToolTipText("Method or field at the other end of the dependency must match any these expressions. E.g., /^com.mycompany/, /\\.get\\w+\\(/");
filterExcludes.setToolTipText("Package, class, method, or field at the other end of the dependency must NOT match any of these expressions. E.g., /Test/");
packageFilterExcludes.setToolTipText("Package at the other end of the dependency must NOT match any of these expressions. E.g., /Test/");
classFilterExcludes.setToolTipText("Class at the other end of the dependency must NOT match any of these expressions. E.g., /Test/");
featureFilterExcludes.setToolTipText("Method or field at the other end of the dependency must NOT match any of these expressions. E.g., /Test/");
showInbounds.setToolTipText("Show dependencies that point to the selected packages, classes, methods, or fields");
showOutbounds.setToolTipText("Show dependencies that originate from the selected packages, classes, methods, or fields");
showEmptyNodes.setToolTipText("Show selected packages, classes, methods, and fields even if they do not have dependencies");
copyOnly.setToolTipText("<html>Only copy explicit dependencies to the result graph.<br>Do not introduce implicit dependencies<br>where explicit dependencies match the regular expressions<br>but are otherwise out of scope</html>");
showInbounds.setFont(getCodeFont(Font.BOLD, 14));
showOutbounds.setFont(getCodeFont(Font.BOLD, 14));
maximumInboundDepth.setToolTipText("Maximum hops against the direction dependencies. Empty field means no limit.");
maximumOutboundDepth.setToolTipText("Maximum hops in the direction of dependencies. Empty field means no limit.");
setAdvancedMode(false);
buildMenus(commandLine);
buildUI();
try {
UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
SwingUtilities.updateComponentTreeUI(this);
} catch (Exception ex) {
LogManager.getLogger(DependencyFinder.class).error("Unable to set look and feel", ex);
}
statusLine.showInfo("Ready.");
}
private Font getCodeFont(int style, int size) {
String fontName = Arrays.stream(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames())
.filter(name -> name.contains(DESIRED_FONT_NAME))
.findFirst()
.orElse(DEFAULT_FONT_NAME);
return new Font(fontName, style, size);
}
private boolean isAdvancedMode() {
return advancedMode;
}
void setAdvancedMode(boolean advancedMode) {
this.advancedMode = advancedMode;
copyOnly.setVisible(advancedMode);
}
public boolean getMaximize() {
return maximize;
}
private void setMaximize(boolean maximize) {
this.maximize = maximize;
}
public boolean getMinimize() {
return minimize;
}
private void setMinimize(boolean minimize) {
this.minimize = minimize;
}
public ClassfileLoaderDispatcher getClassfileLoaderDispatcher() {
return dispatcher;
}
private void setClassfileLoaderDispatcher(ClassfileLoaderDispatcher dispatcher) {
this.dispatcher = dispatcher;
}
public Monitor getMonitor() {
return monitor;
}
private void setMonitor(Monitor monitor) {
this.monitor = monitor;
}
public Collection<String> getInputFiles() {
return inputFiles;
}
private void setInputFiles(Collection<String> inputFiles) {
this.inputFiles = inputFiles;
}
public void addInputFile(File file) {
inputFiles.add(file.toString());
}
public Collection<PackageNode> getPackages() {
return getNodeFactory().getPackages().values();
}
public NodeFactory getNodeFactory() {
return nodeFactory;
}
private void setNodeFactory(NodeFactory nodeFactory) {
this.nodeFactory = nodeFactory;
}
StatusLine getStatusLine() {
return statusLine;
}
JProgressBar getProgressBar() {
return progressBar;
}
private void buildMenus(CommandLine commandLine) {
buildFileMenu(commandLine);
buildViewMenu();
buildHelpMenu();
this.setJMenuBar(menuBar);
}
private void buildFileMenu(CommandLine commandLine) {
menuBar.add(fileMenu);
fileMenu.setText("File");
Action action;
JMenuItem menuItem;
JButton button;
action = new DependencyExtractAction(this);
menuItem = fileMenu.add(action);
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_DOWN_MASK));
menuItem.setMnemonic('e');
button = toolbar.add(action);
button.setToolTipText((String) action.getValue(Action.LONG_DESCRIPTION));
action = new RefreshDependencyGraphAction(this);
menuItem = fileMenu.add(action);
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_DOWN_MASK));
menuItem.setMnemonic('r');
button = toolbar.add(action);
button.setToolTipText((String) action.getValue(Action.LONG_DESCRIPTION));
toolbar.addSeparator();
fileMenu.addSeparator();
action = new OpenFileAction(this);
menuItem = fileMenu.add(action);
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK));
menuItem.setMnemonic('o');
button = toolbar.add(action);
button.setToolTipText((String) action.getValue(Action.LONG_DESCRIPTION));
action = new SaveFileAction(this, commandLine.getSingleSwitch("encoding"), commandLine.getSingleSwitch("dtd-prefix"));
menuItem = fileMenu.add(action);
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
menuItem.setMnemonic('s');
button = toolbar.add(action);
button.setToolTipText((String) action.getValue(Action.LONG_DESCRIPTION));
if (commandLine.isPresent("indent-text")) {
((SaveFileAction) action).setIndentText(commandLine.getSingleSwitch("indent-text"));
}
toolbar.addSeparator();
fileMenu.addSeparator();
action = new NewDependencyGraphAction(this);
menuItem = fileMenu.add(action);
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_DOWN_MASK));
menuItem.setMnemonic('n');
button = toolbar.add(action);
button.setToolTipText((String) action.getValue(Action.LONG_DESCRIPTION));
toolbar.addSeparator();
fileMenu.addSeparator();
action = new DependencyQueryAction(this);
menuItem = fileMenu.add(action);
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, InputEvent.CTRL_DOWN_MASK));
menuItem.setMnemonic('d');
button = toolbar.add(action);
button.setToolTipText((String) action.getValue(Action.LONG_DESCRIPTION));
action = new ClosureQueryAction(this);
menuItem = fileMenu.add(action);
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK));
menuItem.setMnemonic('c');
button = toolbar.add(action);
button.setToolTipText((String) action.getValue(Action.LONG_DESCRIPTION));
action = new MetricsQueryAction(this);
menuItem = fileMenu.add(action);
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, InputEvent.CTRL_DOWN_MASK));
menuItem.setMnemonic('m');
button = toolbar.add(action);
button.setToolTipText((String) action.getValue(Action.LONG_DESCRIPTION));
action = new AllQueriesAction(this);
menuItem = fileMenu.add(action);
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK));
menuItem.setMnemonic('a');
button = toolbar.add(action);
button.setToolTipText((String) action.getValue(Action.LONG_DESCRIPTION));
toolbar.addSeparator();
fileMenu.addSeparator();
action = new ExitAction(this);
menuItem = fileMenu.add(action);
menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_DOWN_MASK));
menuItem.setMnemonic('x');
}
private void buildViewMenu() {
menuBar.add(viewMenu);
viewMenu.setText("View");
ButtonGroup group = new ButtonGroup();
JMenuItem menuItem;
menuItem = new JRadioButtonMenuItem(new SimpleQueryPanelAction(this));
menuItem.setSelected(true);
group.add(menuItem);
viewMenu.add(menuItem);
menuItem = new JRadioButtonMenuItem(new AdvancedQueryPanelAction(this));
group.add(menuItem);
viewMenu.add(menuItem);
}
private void buildHelpMenu() {
menuBar.add(helpMenu);
helpMenu.setText("Help");
Action action;
JMenuItem menuItem;
action = new AboutAction(this);
menuItem = helpMenu.add(action);
menuItem.setMnemonic('a');
}
private void buildUI() {
this.getContentPane().setLayout(new BorderLayout());
this.getContentPane().add(buildControlPanel(), BorderLayout.NORTH);
this.getContentPane().add(buildResultPanel(), BorderLayout.CENTER);
this.getContentPane().add(buildStatusPanel(), BorderLayout.SOUTH);
}
private JComponent buildControlPanel() {
JPanel result = new JPanel();
result.setLayout(new BorderLayout());
result.add(toolbar, BorderLayout.NORTH);
result.add(buildQueryPanel(), BorderLayout.CENTER);
return result;
}
JComponent buildQueryPanel() {
if (isAdvancedMode()) {
buildAdvancedQueryPanel();
} else {
buildSimpleQueryPanel();
}
return queryPanel;
}
private void buildSimpleQueryPanel() {
queryPanel.removeAll();
queryPanel.setLayout(new GridLayout(1, 2));
queryPanel.add(buildSimpleScopePanel());
queryPanel.add(buildSimpleFilterPanel());
queryPanel.revalidate();
}
private void buildAdvancedQueryPanel() {
queryPanel.removeAll();
queryPanel.setLayout(new GridLayout(1, 2));
queryPanel.add(buildAdvancedScopePanel());
queryPanel.add(buildAdvancedFilterPanel());
queryPanel.revalidate();
}
private JComponent buildSimpleScopePanel() {
JPanel result = new JPanel();
result.setBorder(BorderFactory.createTitledBorder("Select programming elements"));
result.setLayout(new BorderLayout());
result.add(buildSimpleScopePanelCheckboxes(), BorderLayout.NORTH);
result.add(buildSimpleScopePanelTextFields(), BorderLayout.SOUTH);
return result;
}
private JComponent buildSimpleScopePanelTextFields() {
JPanel result = new JPanel();
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(0, 2, 0, 2);
result.setLayout(gbl);
JLabel scopeIncludesLabel = new JLabel("including:");
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 0;
c.gridy = 0;
c.weightx = 0;
c.weighty = 0;
result.add(scopeIncludesLabel);
gbl.setConstraints(scopeIncludesLabel, c);
JLabel scopeExcludesLabel = new JLabel("excluding:");
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 1;
c.gridy = 0;
c.weightx = 0;
c.weighty = 0;
result.add(scopeExcludesLabel);
gbl.setConstraints(scopeExcludesLabel, c);
// -scope-includes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 1;
c.weightx = 1;
c.weighty = 0;
result.add(scopeIncludes);
gbl.setConstraints(scopeIncludes, c);
// -scope-excludes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = 1;
c.weightx = 1;
c.weighty = 0;
result.add(scopeExcludes);
gbl.setConstraints(scopeExcludes, c);
return result;
}
private JComponent buildSimpleScopePanelCheckboxes() {
JPanel result = new JPanel();
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(0, 2, 0, 2);
result.setLayout(gbl);
// -package-scope
c.anchor = GridBagConstraints.EAST;
c.fill = GridBagConstraints.NONE;
c.gridx = 0;
c.gridy = 0;
c.weightx = 1;
c.weighty = 0;
result.add(packageScope);
gbl.setConstraints(packageScope, c);
// -class-scope
c.anchor = GridBagConstraints.CENTER;
c.fill = GridBagConstraints.NONE;
c.gridx = 1;
c.gridy = 0;
c.weightx = 0;
c.weighty = 0;
result.add(classScope);
gbl.setConstraints(classScope, c);
// -feature-scope
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 2;
c.gridy = 0;
c.weightx = 1;
c.weighty = 0;
result.add(featureScope);
gbl.setConstraints(featureScope, c);
return result;
}
private JComponent buildSimpleFilterPanel() {
JPanel result = new JPanel();
result.setBorder(BorderFactory.createTitledBorder("Show dependencies (stop for closure)"));
result.setLayout(new BorderLayout());
result.add(buildSimpleFilterPanelCheckboxes(), BorderLayout.NORTH);
result.add(buildSimpleFilterPanelTextFields(), BorderLayout.SOUTH);
return result;
}
private JComponent buildSimpleFilterPanelTextFields() {
JPanel result = new JPanel();
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(0, 2, 0, 2);
result.setLayout(gbl);
JLabel filterIncludesLabel = new JLabel("including:");
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 0;
c.gridy = 0;
c.weightx = 0;
c.weighty = 0;
result.add(filterIncludesLabel);
gbl.setConstraints(filterIncludesLabel, c);
JLabel filterExcludesLabel = new JLabel("excluding:");
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 1;
c.gridy = 0;
c.weightx = 0;
c.weighty = 0;
result.add(filterExcludesLabel);
gbl.setConstraints(filterExcludesLabel, c);
// -filter-includes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 1;
c.weightx = 1;
c.weighty = 0;
result.add(filterIncludes);
gbl.setConstraints(filterIncludes, c);
// -filter-excludes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = 1;
c.weightx = 1;
c.weighty = 0;
result.add(filterExcludes);
gbl.setConstraints(filterExcludes, c);
return result;
}
private JComponent buildSimpleFilterPanelCheckboxes() {
JPanel result = new JPanel();
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(0, 2, 0, 2);
result.setLayout(gbl);
// -package-filter
c.anchor = GridBagConstraints.EAST;
c.fill = GridBagConstraints.NONE;
c.gridx = 0;
c.gridy = 0;
c.weightx = 1;
c.weighty = 0;
result.add(packageFilter);
gbl.setConstraints(packageFilter, c);
// -class-filter
c.anchor = GridBagConstraints.CENTER;
c.fill = GridBagConstraints.NONE;
c.gridx = 1;
c.gridy = 0;
c.weightx = 0;
c.weighty = 0;
result.add(classFilter);
gbl.setConstraints(classFilter, c);
// -feature-filter
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 2;
c.gridy = 0;
c.weightx = 1;
c.weighty = 0;
result.add(featureFilter);
gbl.setConstraints(featureFilter, c);
return result;
}
private JComponent buildAdvancedScopePanel() {
JPanel result = new JPanel();
result.setBorder(BorderFactory.createTitledBorder("Select programming elements"));
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(2, 0, 2, 5);
result.setLayout(gbl);
// -package-scope
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 0;
c.gridy = 2;
c.weightx = 0;
c.weighty = 0;
result.add(packageScope);
gbl.setConstraints(packageScope, c);
// -class-scope
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 0;
c.gridy = 3;
c.weightx = 0;
c.weighty = 0;
result.add(classScope);
gbl.setConstraints(classScope, c);
// -feature-scope
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 0;
c.gridy = 4;
c.weightx = 0;
c.weighty = 0;
result.add(featureScope);
gbl.setConstraints(featureScope, c);
JLabel scopeIncludesLabel = new JLabel("including:");
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 1;
c.gridy = 0;
c.weightx = 0;
c.weighty = 0;
result.add(scopeIncludesLabel);
gbl.setConstraints(scopeIncludesLabel, c);
// -scope-includes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = 1;
c.weightx = 1;
c.weighty = 0;
result.add(scopeIncludes);
gbl.setConstraints(scopeIncludes, c);
// -package-scope-includes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = 2;
c.weightx = 1;
c.weighty = 0;
result.add(packageScopeIncludes);
gbl.setConstraints(packageScopeIncludes, c);
// -class-scope-includes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = 3;
c.weightx = 1;
c.weighty = 0;
result.add(classScopeIncludes);
gbl.setConstraints(classScopeIncludes, c);
// -feature-scope-includes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = 4;
c.weightx = 1;
c.weighty = 0;
result.add(featureScopeIncludes);
gbl.setConstraints(featureScopeIncludes, c);
JLabel scopeExcludesLabel = new JLabel("excluding:");
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 2;
c.gridy = 0;
c.weightx = 0;
c.weighty = 0;
result.add(scopeExcludesLabel);
gbl.setConstraints(scopeExcludesLabel, c);
// -scope-excludes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 2;
c.gridy = 1;
c.weightx = 1;
c.weighty = 0;
result.add(scopeExcludes);
gbl.setConstraints(scopeExcludes, c);
// -package-scope-excludes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 2;
c.gridy = 2;
c.weightx = 1;
c.weighty = 0;
result.add(packageScopeExcludes);
gbl.setConstraints(packageScopeExcludes, c);
// -class-scope-excludes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 2;
c.gridy = 3;
c.weightx = 1;
c.weighty = 0;
result.add(classScopeExcludes);
gbl.setConstraints(classScopeExcludes, c);
// -feature-scope-excludes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 2;
c.gridy = 4;
c.weightx = 1;
c.weighty = 0;
result.add(featureScopeExcludes);
gbl.setConstraints(featureScopeExcludes, c);
return result;
}
private JComponent buildAdvancedFilterPanel() {
JPanel result = new JPanel();
result.setBorder(BorderFactory.createTitledBorder("Show dependencies (stop for closure)"));
GridBagLayout gbl = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(2, 0, 2, 5);
result.setLayout(gbl);
// -package-filter
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 0;
c.gridy = 2;
c.weightx = 0;
c.weighty = 0;
result.add(packageFilter);
gbl.setConstraints(packageFilter, c);
// -class-filter
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 0;
c.gridy = 3;
c.weightx = 0;
c.weighty = 0;
result.add(classFilter);
gbl.setConstraints(classFilter, c);
// -feature-filter
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 0;
c.gridy = 4;
c.weightx = 0;
c.weighty = 0;
result.add(featureFilter);
gbl.setConstraints(featureFilter, c);
JLabel filterIncludesLabel = new JLabel("including:");
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 1;
c.gridy = 0;
c.weightx = 0;
c.weighty = 0;
result.add(filterIncludesLabel);
gbl.setConstraints(filterIncludesLabel, c);
// -filter-includes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = 1;
c.weightx = 1;
c.weighty = 0;
result.add(filterIncludes);
gbl.setConstraints(filterIncludes, c);
// -package-filter-includes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = 2;
c.weightx = 1;
c.weighty = 0;
result.add(packageFilterIncludes);
gbl.setConstraints(packageFilterIncludes, c);
// -class-filter-includes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = 3;
c.weightx = 1;
c.weighty = 0;
result.add(classFilterIncludes);
gbl.setConstraints(classFilterIncludes, c);
// -feature-filter-includes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
c.gridy = 4;
c.weightx = 1;
c.weighty = 0;
result.add(featureFilterIncludes);
gbl.setConstraints(featureFilterIncludes, c);
JLabel filterExcludesLabel = new JLabel("excluding:");
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.NONE;
c.gridx = 2;
c.gridy = 0;
c.weightx = 0;
c.weighty = 0;
result.add(filterExcludesLabel);
gbl.setConstraints(filterExcludesLabel, c);
// -filter-excludes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 2;
c.gridy = 1;
c.weightx = 1;
c.weighty = 0;
result.add(filterExcludes);
gbl.setConstraints(filterExcludes, c);
// -package-filter-excludes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 2;
c.gridy = 2;
c.weightx = 1;
c.weighty = 0;
result.add(packageFilterExcludes);
gbl.setConstraints(packageFilterExcludes, c);
// -class-filter-excludes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 2;
c.gridy = 3;
c.weightx = 1;
c.weighty = 0;
result.add(classFilterExcludes);
gbl.setConstraints(classFilterExcludes, c);
// -feature-filter-excludes
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 2;
c.gridy = 4;
c.weightx = 1;
c.weighty = 0;
result.add(featureFilterExcludes);
gbl.setConstraints(featureFilterExcludes, c);
return result;
}
private JComponent buildResultPanel() {
JTabbedPane result = new JTabbedPane();
result.setBorder(BorderFactory.createTitledBorder("Results"));
result.addTab("Dependencies", buildDependenciesPanel());
result.addTab("Closure", buildClosurePanel());
result.addTab("Metrics", buildMetricsPanel());
return result;
}
private JComponent buildDependenciesPanel() {
JPanel result = new JPanel();
result.setLayout(new BorderLayout());
result.add(buildPrinterControlPanel(), BorderLayout.NORTH);
result.add(buildDependenciesResultPanel(), BorderLayout.CENTER);
return result;
}
private JComponent buildPrinterControlPanel() {
JPanel result = new JPanel();
result.add(new JLabel("Show "));
result.add(showInbounds);
result.add(showOutbounds);
result.add(showEmptyNodes);
result.add(copyOnly);
PrinterControlAction action = new PrinterControlAction(this);
showInbounds.addActionListener(action);
showOutbounds.addActionListener(action);
showEmptyNodes.addActionListener(action);
return result;
}
private JComponent buildDependenciesResultPanel() {
JComponent result = new JScrollPane(dependenciesResultArea);
dependenciesResultArea.setEditable(false);
dependenciesResultArea.setFont(getCodeFont(Font.PLAIN, 12));
return result;
}
private JComponent buildClosurePanel() {
JPanel result = new JPanel();
result.setLayout(new BorderLayout());
result.add(buildClosureControlPanel(), BorderLayout.NORTH);
result.add(buildClosureResultPanel(), BorderLayout.CENTER);
return result;
}
private JComponent buildClosureControlPanel() {
JPanel result = new JPanel();
result.add(new JLabel("Follow inbounds: "));
result.add(maximumInboundDepth);
result.add(new JLabel("Follow outbounds: "));
result.add(maximumOutboundDepth);
return result;
}
private JComponent buildClosureResultPanel() {
JComponent result = new JScrollPane(closureResultArea);
closureResultArea.setEditable(false);
closureResultArea.setFont(getCodeFont(Font.PLAIN, 12));
return result;
}
private JComponent buildMetricsPanel() {
return new JSplitPane(JSplitPane.VERTICAL_SPLIT, buildMetricsResultPanel(), buildMetricsChartPanel());
}
private JComponent buildMetricsResultPanel() {
JComponent result = new JScrollPane(metricsResultArea);
metricsResultArea.setEditable(false);
return result;
}
private JComponent buildMetricsChartPanel() {
JComponent result;
JTable table = new JTable(metricsChartModel);
table.setCellSelectionEnabled(true);
table.setColumnSelectionAllowed(true);
result = new JScrollPane(table);
return result;
}
private JComponent buildStatusPanel() {
JPanel result = new JPanel();
Dimension size = getProgressBar().getPreferredSize();
size.width = 100;
getProgressBar().setPreferredSize(size);
getProgressBar().setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
result.setLayout(new BorderLayout());
result.add(getStatusLine(), BorderLayout.CENTER);
result.add(getProgressBar(), BorderLayout.EAST);
return result;
}
public void resetQuery() {
packageScope.setSelected(true);
classScope.setSelected(false);
featureScope.setSelected(false);
scopeIncludes.setText("//");
packageScopeIncludes.setText("");
classScopeIncludes.setText("");
featureScopeIncludes.setText("");
scopeExcludes.setText("");
packageScopeExcludes.setText("");
classScopeExcludes.setText("");
featureScopeExcludes.setText("");
packageFilter.setSelected(true);
classFilter.setSelected(false);
featureFilter.setSelected(false);
filterIncludes.setText("//");
packageFilterIncludes.setText("");
classFilterIncludes.setText("");
featureFilterIncludes.setText("");
filterExcludes.setText("");
packageFilterExcludes.setText("");
classFilterExcludes.setText("");
featureFilterExcludes.setText("");
showInbounds.setSelected(true);
showOutbounds.setSelected(true);
showEmptyNodes.setSelected(true);
copyOnly.setSelected(false);
}
void clearDependencyResult() {
dependenciesQuery = null;
dependenciesResultArea.setText("");
}
void doDependencyQuery() {
var scopeCriteria = new RegularExpressionSelectionCriteria();
scopeCriteria.setMatchingPackages(packageScope.isSelected());
scopeCriteria.setMatchingClasses(classScope.isSelected());
scopeCriteria.setMatchingFeatures(featureScope.isSelected());
scopeCriteria.setGlobalIncludes(scopeIncludes.getText());
scopeCriteria.setGlobalExcludes(scopeExcludes.getText());
if (isAdvancedMode()) {
scopeCriteria.setPackageIncludes(packageScopeIncludes.getText());
scopeCriteria.setClassIncludes(classScopeIncludes.getText());
scopeCriteria.setFeatureIncludes(featureScopeIncludes.getText());
scopeCriteria.setPackageExcludes(packageScopeExcludes.getText());
scopeCriteria.setClassExcludes(classScopeExcludes.getText());
scopeCriteria.setFeatureExcludes(featureScopeExcludes.getText());
}
var filterCriteria = new RegularExpressionSelectionCriteria();
filterCriteria.setMatchingPackages(packageFilter.isSelected());
filterCriteria.setMatchingClasses(classFilter.isSelected());
filterCriteria.setMatchingFeatures(featureFilter.isSelected());
filterCriteria.setGlobalIncludes(filterIncludes.getText());
filterCriteria.setGlobalExcludes(filterExcludes.getText());
if (isAdvancedMode()) {
filterCriteria.setPackageIncludes(packageFilterIncludes.getText());
filterCriteria.setClassIncludes(classFilterIncludes.getText());
filterCriteria.setFeatureIncludes(featureFilterIncludes.getText());
filterCriteria.setPackageExcludes(packageFilterExcludes.getText());
filterCriteria.setClassExcludes(classFilterExcludes.getText());
filterCriteria.setFeatureExcludes(featureFilterExcludes.getText());
}
if ((isAdvancedMode() && copyOnly.isSelected()) || getMaximize()) {
var strategy = new SelectiveTraversalStrategy(scopeCriteria, filterCriteria);
dependenciesQuery = new GraphCopier(strategy);
} else {
dependenciesQuery = new GraphSummarizer(scopeCriteria, filterCriteria);
}
dependenciesQuery.traverseNodes(getPackages());
refreshDependenciesDisplay();
}
void refreshDependenciesDisplay() {
if (dependenciesQuery != null) {
var out = new StringWriter();
var printer = new com.jeantessier.dependency.TextPrinter(new PrintWriter(out));
printer.setShowInbounds(showInbounds.isSelected());
printer.setShowOutbounds(showOutbounds.isSelected());
printer.setShowEmptyNodes(showEmptyNodes.isSelected());
printer.traverseNodes(dependenciesQuery.getScopeFactory().getPackages().values());
dependenciesResultArea.setText(out.toString());
}
}
void clearClosureResult() {
closureResultArea.setText("");
}
void doClosureQuery() {
var startCriteria = new RegularExpressionSelectionCriteria();
startCriteria.setGlobalIncludes(scopeIncludes.getText());
startCriteria.setGlobalExcludes(scopeExcludes.getText());
if (isAdvancedMode()) {
startCriteria.setPackageIncludes(packageScopeIncludes.getText());
startCriteria.setClassIncludes(classScopeIncludes.getText());
startCriteria.setFeatureIncludes(featureScopeIncludes.getText());
startCriteria.setPackageExcludes(packageScopeExcludes.getText());
startCriteria.setClassExcludes(classScopeExcludes.getText());
startCriteria.setFeatureExcludes(featureScopeExcludes.getText());
}
var stopCriteria = new RegularExpressionSelectionCriteria();
stopCriteria.setGlobalIncludes(filterIncludes.getText());
stopCriteria.setGlobalExcludes(filterExcludes.getText());
if (isAdvancedMode()) {
stopCriteria.setPackageIncludes(packageFilterIncludes.getText());
stopCriteria.setClassIncludes(classFilterIncludes.getText());
stopCriteria.setFeatureIncludes(featureFilterIncludes.getText());
stopCriteria.setPackageExcludes(packageFilterExcludes.getText());
stopCriteria.setClassExcludes(classFilterExcludes.getText());
stopCriteria.setFeatureExcludes(featureFilterExcludes.getText());
}
var selector = new TransitiveClosure(startCriteria, stopCriteria);
try {
selector.setMaximumInboundDepth(Long.parseLong(maximumInboundDepth.getText()));
} catch (NumberFormatException ex) {
selector.setMaximumInboundDepth(TransitiveClosure.UNBOUNDED_DEPTH);
}
try {
selector.setMaximumOutboundDepth(Long.parseLong(maximumOutboundDepth.getText()));
} catch (NumberFormatException ex) {
selector.setMaximumOutboundDepth(TransitiveClosure.UNBOUNDED_DEPTH);
}
selector.traverseNodes(getPackages());
var scopeCriteria = new RegularExpressionSelectionCriteria();
scopeCriteria.setMatchingPackages(packageScope.isSelected());
scopeCriteria.setMatchingClasses(classScope.isSelected());
scopeCriteria.setMatchingFeatures(featureScope.isSelected());
scopeCriteria.setGlobalIncludes("//");
var filterCriteria = new RegularExpressionSelectionCriteria();
filterCriteria.setMatchingPackages(packageFilter.isSelected());
filterCriteria.setMatchingClasses(classFilter.isSelected());
filterCriteria.setMatchingFeatures(featureFilter.isSelected());
filterCriteria.setGlobalIncludes("//");
var summarizer = new GraphSummarizer(scopeCriteria, filterCriteria);
summarizer.traverseNodes(selector.getFactory().getPackages().values());
var out = new StringWriter();
var printer = new com.jeantessier.dependency.TextPrinter(new PrintWriter(out));
printer.traverseNodes(summarizer.getScopeFactory().getPackages().values());
closureResultArea.setText(out.toString());
}
void clearMetricsResult() {
metricsResultArea.setText("");
}
void doMetricsQuery() {
var scopeCriteria = new RegularExpressionSelectionCriteria();
scopeCriteria.setMatchingPackages(packageScope.isSelected());
scopeCriteria.setMatchingClasses(classScope.isSelected());
scopeCriteria.setMatchingFeatures(featureScope.isSelected());
scopeCriteria.setGlobalIncludes(scopeIncludes.getText());
scopeCriteria.setGlobalExcludes(scopeExcludes.getText());
if (isAdvancedMode()) {
scopeCriteria.setPackageIncludes(packageScopeIncludes.getText());
scopeCriteria.setClassIncludes(classScopeIncludes.getText());
scopeCriteria.setFeatureIncludes(featureScopeIncludes.getText());
scopeCriteria.setPackageExcludes(packageScopeExcludes.getText());
scopeCriteria.setClassExcludes(classScopeExcludes.getText());
scopeCriteria.setFeatureExcludes(featureScopeExcludes.getText());
}
var filterCriteria = new RegularExpressionSelectionCriteria();
filterCriteria.setMatchingPackages(packageFilter.isSelected());
filterCriteria.setMatchingClasses(classFilter.isSelected());
filterCriteria.setMatchingFeatures(featureFilter.isSelected());
filterCriteria.setGlobalIncludes(filterIncludes.getText());
filterCriteria.setGlobalExcludes(filterExcludes.getText());
if (isAdvancedMode()) {
filterCriteria.setPackageIncludes(packageFilterIncludes.getText());
filterCriteria.setClassIncludes(classFilterIncludes.getText());
filterCriteria.setFeatureIncludes(featureFilterIncludes.getText());
filterCriteria.setPackageExcludes(packageFilterExcludes.getText());
filterCriteria.setClassExcludes(classFilterExcludes.getText());
filterCriteria.setFeatureExcludes(featureFilterExcludes.getText());
}
var strategy = new SelectiveTraversalStrategy(scopeCriteria, filterCriteria);
var metrics = new com.jeantessier.dependency.MetricsGatherer(strategy);
metrics.traverseNodes(getPackages());
var out = new StringWriter();
var report = new TextMetricsReport(new PrintWriter(out));
report.process(metrics);
metricsResultArea.setText(out.toString());
metricsChartModel.setMetrics(metrics);
}
void setNewDependencyGraph() {
setInputFiles(new LinkedList<>());
setClassfileLoaderDispatcher(new ModifiedOnlyDispatcher(ClassfileLoaderEventSource.DEFAULT_DISPATCHER));
var factory = new NodeFactory();
setNodeFactory(factory);
var collector = new CodeDependencyCollector(factory);
var deletingVisitor = new DeletingVisitor(factory);
setMonitor(new Monitor(collector, deletingVisitor));
resetQuery();
}
public static void showError(CommandLineUsage clu, String msg) {
System.err.println(msg);
showError(clu);
}
public static void showError(CommandLineUsage clu) {
System.err.println(clu);
}
public static void main(String[] args) {
// Parsing the command line
CommandLine commandLine = new CommandLine(new NullParameterStrategy());
commandLine.addToggleSwitch("minimize");
commandLine.addToggleSwitch("maximize");
commandLine.addSingleValueSwitch("encoding", com.jeantessier.dependency.XMLPrinter.DEFAULT_ENCODING);
commandLine.addSingleValueSwitch("dtd-prefix", com.jeantessier.dependency.XMLPrinter.DEFAULT_DTD_PREFIX);
commandLine.addSingleValueSwitch("indent-text", com.jeantessier.dependency.XMLPrinter.DEFAULT_INDENT_TEXT);
commandLine.addToggleSwitch("help");
CommandLineUsage usage = new CommandLineUsage("DependencyFinder");
commandLine.accept(usage);
try {
commandLine.parse(args);
} catch (IllegalArgumentException ex) {
showError(usage, ex.toString());
System.exit(1);
}
if (commandLine.getToggleSwitch("help")) {
showError(usage);
System.exit(1);
}
if (commandLine.getToggleSwitch("maximize") && commandLine.getToggleSwitch("minimize")) {
showError(usage, "Only one of -maximize or -minimize allowed");
}
/*
* Beginning of main processing
*/
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
// Ignore
}
DependencyFinder model = new DependencyFinder(commandLine);
model.setMaximize(commandLine.getToggleSwitch("maximize"));
model.setMinimize(commandLine.getToggleSwitch("minimize"));
model.setVisible(true);
}
}