package jdepend.swingui; import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.*; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.*; import javax.swing.tree.*; import jdepend.framework.JavaClass; import jdepend.framework.JavaPackage; import jdepend.framework.PackageComparator; import jdepend.framework.PackageFilter; import jdepend.framework.ParserListener; /** * The <code>JDepend</code> class analyzes directories of Java class files, * generates metrics for each Java package, and reports the metrics in a Swing * tree. * * @author <b>Mike Clark</b> * @author Clarkware Consulting, Inc. */ public class JDepend implements ParserListener { private jdepend.framework.JDepend analyzer; private JFrame frame; private StatusPanel statusPanel; private JTextField statusField; private JProgressBar progressBar; private DependTree afferentTree; private DependTree efferentTree; private Hashtable resourceStrings; private Hashtable actions; private static Font BOLD_FONT = new Font("dialog", Font.BOLD, 12); /** * Constructs a <code>JDepend</code> instance. */ public JDepend() { analyzer = new jdepend.framework.JDepend(); analyzer.addParseListener(this); // // Force the cross platform L&F. // try { UIManager.setLookAndFeel(UIManager .getCrossPlatformLookAndFeelClassName()); //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { e.printStackTrace(); } // // Install the resource string table. // resourceStrings = new Hashtable(); resourceStrings.put("menubar", "File"); resourceStrings.put("File", "About Exit"); // // Install the action table. // actions = new Hashtable(); actions.put("About", new AboutAction()); actions.put("Exit", new ExitAction()); } /** * Adds the specified directory name to the collection of directories to be * analyzed. * * @param name Directory name. * @throws IOException If the directory does not exist. */ public void addDirectory(String name) throws IOException { analyzer.addDirectory(name); } /** * Sets the package filter. * * @param filter Package filter. */ public void setFilter(PackageFilter filter) { analyzer.setFilter(filter); } /** * Sets the comma-separated list of components. */ public void setComponents(String components) { analyzer.setComponents(components); } /** * Analyzes the registered directories, generates metrics for each Java * package, and reports the metrics in a graphical format. */ public void analyze() { display(); startProgressMonitor(analyzer.countClasses()); ArrayList packages = new ArrayList(analyzer.analyze()); Collections.sort(packages, new PackageComparator(PackageComparator .byName())); stopProgressMonitor(); updateTree(packages); } /** * Called whenever a Java source file is parsed into the specified * <code>JavaClass</code> instance. * * @param jClass Parsed Java class. */ public void onParsedJavaClass(final JavaClass jClass) { SwingUtilities.invokeLater(new Runnable() { public void run() { getProgressBar().setValue(getProgressBar().getValue() + 1); } }); } private void display() { frame = createUI(); frame.setVisible(true); } private void updateTree(ArrayList packages) { JavaPackage jPackage = new JavaPackage("root"); jPackage.setAfferents(packages); jPackage.setEfferents(packages); AfferentNode ah = new AfferentNode(null, jPackage); getAfferentTree().setModel(new DependTreeModel(ah)); EfferentNode eh = new EfferentNode(null, jPackage); getEfferentTree().setModel(new DependTreeModel(eh)); } private void startProgressMonitor(final int maxValue) { SwingUtilities.invokeLater(new Runnable() { public void run() { getProgressBar().setMinimum(0); getProgressBar().setMaximum(maxValue); getStatusPanel().setStatusComponent(getProgressBar()); } }); } private void stopProgressMonitor() { SwingUtilities.invokeLater(new Runnable() { public void run() { getStatusPanel().setStatusComponent(getStatusField()); int classCount = analyzer.countClasses(); int packageCount = analyzer.countPackages(); showStatusMessage("Analyzed " + packageCount + " packages (" + classCount + " classes)."); } }); } private JFrame createUI() { JFrame frame = createFrame("JDepend"); JMenuBar menuBar = createMenubar(); frame.setJMenuBar(menuBar); JPanel treePanel = createTreePanel(); StatusPanel statusPanel = getStatusPanel(); frame.getContentPane().add("Center", treePanel); frame.getContentPane().add("South", statusPanel); frame.pack(); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); int width = 700; int height = 500; int x = (screenSize.width - width) / 2; int y = (screenSize.height - height) / 2; frame.setBounds(x, y, width, height); frame.setSize(width, height); return frame; } private JFrame createFrame(String title) { JFrame frame = new JFrame(title); frame.getContentPane().setLayout(new BorderLayout()); frame.setBackground(SystemColor.control); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { new ExitAction().actionPerformed(null); } }); return frame; } private JPanel createTreePanel() { JPanel panel = new JPanel(); panel.setLayout(new GridLayout(2, 1)); panel.add(getEfferentTree()); panel.add(getAfferentTree()); /* * panel.setLayout(new GridLayout(1,1)); JSplitPane splitPane = new * JSplitPane(JSplitPane.VERTICAL_SPLIT); * splitPane.setOneTouchExpandable(true); * splitPane.setTopComponent(getEfferentTree()); * splitPane.setBottomComponent(getAfferentTree()); * panel.add(splitPane); */ return panel; } private StatusPanel createStatusPanel() { StatusPanel panel = new StatusPanel(); panel.setStatusComponent(getStatusField()); return panel; } private JProgressBar createProgressBar() { JProgressBar bar = new JProgressBar(); bar.setStringPainted(true); return bar; } private JTextField createStatusField() { JTextField statusField = new JTextField(); statusField.setFont(BOLD_FONT); statusField.setEditable(false); statusField.setForeground(Color.black); statusField.setBorder(BorderFactory .createBevelBorder(BevelBorder.LOWERED)); Insets insets = new Insets(5, 5, 5, 5); statusField.setMargin(insets); return statusField; } private JMenuBar createMenubar() { JMenuBar menuBar = new JMenuBar(); String[] menuKeys = tokenize((String) resourceStrings.get("menubar")); for (int i = 0; i < menuKeys.length; i++) { JMenu m = createMenu(menuKeys[i]); if (m != null) { menuBar.add(m); } } return menuBar; } private JMenu createMenu(String key) { String[] itemKeys = tokenize((String) resourceStrings.get(key)); JMenu menu = new JMenu(key); for (int i = 0; i < itemKeys.length; i++) { if (itemKeys[i].equals("-")) { menu.addSeparator(); } else { JMenuItem mi = createMenuItem(itemKeys[i]); menu.add(mi); } } char mnemonic = key.charAt(0); menu.setMnemonic(mnemonic); return menu; } private JMenuItem createMenuItem(String key) { JMenuItem mi = new JMenuItem(key); char mnemonic = key.charAt(0); mi.setMnemonic(mnemonic); char accelerator = key.charAt(0); mi.setAccelerator(KeyStroke.getKeyStroke(accelerator, java.awt.Event.CTRL_MASK)); String actionString = key; mi.setActionCommand(actionString); Action a = getActionForCommand(actionString); if (a != null) { mi.addActionListener(a); mi.setEnabled(a.isEnabled()); } else { mi.setEnabled(false); } return mi; } private void showStatusMessage(final String message) { getStatusField().setFont(BOLD_FONT); getStatusField().setForeground(Color.black); getStatusField().setText(" " + message); } private void showStatusError(final String message) { getStatusField().setFont(BOLD_FONT); getStatusField().setForeground(Color.red); getStatusField().setText(" " + message); } private DependTree getAfferentTree() { if (afferentTree == null) { afferentTree = new DependTree(); afferentTree.addTreeSelectionListener(new TreeListener()); } return afferentTree; } private DependTree getEfferentTree() { if (efferentTree == null) { efferentTree = new DependTree(); efferentTree.addTreeSelectionListener(new TreeListener()); } return efferentTree; } private StatusPanel getStatusPanel() { if (statusPanel == null) { statusPanel = createStatusPanel(); } return statusPanel; } private JProgressBar getProgressBar() { if (progressBar == null) { progressBar = createProgressBar(); } return progressBar; } private JTextField getStatusField() { if (statusField == null) { statusField = createStatusField(); } return statusField; } private Action getActionForCommand(String command) { return (Action) actions.get(command); } /* * Parses the specified string into an array of strings on whitespace * boundaries. @param input String to tokenize. @return Strings. */ private String[] tokenize(String input) { Vector v = new Vector(); StringTokenizer t = new StringTokenizer(input); while (t.hasMoreTokens()) { v.addElement(t.nextToken()); } String cmd[] = new String[v.size()]; for (int i = 0; i < cmd.length; i++) { cmd[i] = (String) v.elementAt(i); } return cmd; } private void postStatusMessage(final String message) { SwingUtilities.invokeLater(new Runnable() { public void run() { showStatusMessage(message); } }); } private void postStatusError(final String message) { SwingUtilities.invokeLater(new Runnable() { public void run() { showStatusError(message); } }); } // // Tree selection handler. // private class TreeListener implements TreeSelectionListener { /** * Constructs a <code>TreeListener</code> instance. */ TreeListener() { } /** * Callback method triggered whenever the value of the tree selection * changes. * * @param te Event that characterizes the change. */ public void valueChanged(TreeSelectionEvent te) { TreePath path = te.getNewLeadSelectionPath(); if (path != null) { PackageNode node = (PackageNode) path.getLastPathComponent(); showStatusMessage(node.toMetricsString()); } } } // // About action handler. // private class AboutAction extends AbstractAction { /** * Constructs an <code>AboutAction</code> instance. */ AboutAction() { super("About"); } /** * Handles the action. */ public void actionPerformed(ActionEvent e) { AboutDialog d = new AboutDialog(frame); d.setModal(true); d.setLocation(300, 300); d.show(); } } // // Exit action handler. // private class ExitAction extends AbstractAction { /** * Constructs an <code>ExitAction</code> instance. */ ExitAction() { super("Exit"); } /** * Handles the action. */ public void actionPerformed(ActionEvent e) { frame.dispose(); System.exit(0); } } private void usage(String message) { if (message != null) { System.err.println("\n" + message); } String baseUsage = "\nJDepend "; System.err.println(""); System.err.println("usage: "); System.err.println(baseUsage + "-components <components> " + "<directory> [directory2 [directory 3] ...]"); System.exit(1); } private void instanceMain(String[] args) { if (args.length < 1) { usage("Must specify at least one directory."); } int directoryCount = 0; for (int i = 0; i < args.length; i++) { if (args[i].startsWith("-")) { if (args[i].equalsIgnoreCase("-components")) { if (args.length <= i + 1) { usage("Components not specified."); } setComponents(args[++i]); } else { usage("Invalid argument: " + args[i]); } } else { try { addDirectory(args[i]); directoryCount++; } catch (IOException ioe) { usage("Directory does not exist: " + args[i]); } } } if (directoryCount == 0) { usage("Must specify at least one directory."); } analyze(); for (int i = 0; i < args.length; i++) { if (args[i].startsWith("-")) { usage("Invalid argument: " + args[i]); } try { addDirectory(args[i]); directoryCount++; } catch (IOException ioe) { usage("Directory does not exist: " + args[i]); } } if (directoryCount == 0) { usage("Must specify at least one directory."); } analyze(); } public static void main(String[] args) { new JDepend().instanceMain(args); } }