View Javadoc

    1   /***
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.cpd;
    5
    6   import javax.swing.*;
    7   import java.awt.BorderLayout;
    8   import java.awt.Dimension;
    9   import java.awt.event.ActionEvent;
    10  import java.awt.event.ActionListener;
    11  import java.io.File;
    12  import java.io.FileOutputStream;
    13  import java.io.IOException;
    14  import java.io.PrintWriter;
    15  import java.util.ArrayList;
    16  import java.util.Iterator;
    17  import java.util.List;
    18  import java.util.Properties;
    19
    20  public class GUI implements CPDListener {
    21
    22      private static class CancelListener implements ActionListener {
    23          public void actionPerformed(ActionEvent e) {
    24              System.exit(0);
    25          }
    26      }
    27
    28      private class GoListener implements ActionListener {
    29          public void actionPerformed(ActionEvent e) {
    30              new Thread(new Runnable() {
    31                  public void run() {
    32                      tokenizingFilesBar.setValue(0);
    33                      tokenizingFilesBar.setString("");
    34                      resultsTextArea.setText("");
    35                      phaseLabel.setText("");
    36                      timeField.setText("");
    37                      go();
    38                  }
    39              }).start();
    40          }
    41      }
    42
    43      private class SaveListener implements ActionListener {
    44          public void actionPerformed(ActionEvent evt) {
    45              int ret = fcSave.showSaveDialog(GUI.this.frame);
    46              File f = fcSave.getSelectedFile();
    47              if (f == null || ret != JFileChooser.APPROVE_OPTION) {
    48                  return;
    49              }
    50              if (!f.canWrite()) {
    51                  PrintWriter pw = null;
    52                  try {
    53                      pw = new PrintWriter(new FileOutputStream(f));
    54                      if (evt.getActionCommand().equals(".txt")) {
    55                          pw.write(new SimpleRenderer().render(matches.iterator()));
    56                      } else if (evt.getActionCommand().equals(".xml")) {
    57                          pw.write(new XMLRenderer().render(matches.iterator()));
    58                      } else if (evt.getActionCommand().equals(".csv")) {
    59                          pw.write(new CSVRenderer().render(matches.iterator()));
    60                      }
    61                      pw.flush();
    62                      JOptionPane.showMessageDialog(frame, "File saved");
    63                  } catch (IOException e) {
    64                      error("Couldn't save file" + f.getAbsolutePath(), e);
    65                  } finally {
    66                      if (pw != null) {
    67                          pw.close();
    68                      }
    69                  }
    70              } else {
    71                  error("Could not write to file " + f.getAbsolutePath(), null);
    72              }
    73          }
    74
    75          private void error(String message, Exception e) {
    76              if (e != null) {
    77                  e.printStackTrace();
    78              }
    79              JOptionPane.showMessageDialog(GUI.this.frame, message);
    80          }
    81
    82      }
    83
    84      private class BrowseListener implements ActionListener {
    85          public void actionPerformed(ActionEvent e) {
    86              JFileChooser fc = new JFileChooser(rootDirectoryField.getText());
    87              fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
    88              fc.showDialog(frame, "Select");
    89              if (fc.getSelectedFile() != null) {
    90                  rootDirectoryField.setText(fc.getSelectedFile().getAbsolutePath());
    91              }
    92          }
    93      }
    94
    95      private JTextField rootDirectoryField = new JTextField(System.getProperty("user.home"));
    96      private JTextField minimumLengthField = new JTextField("75");
    97      private JTextField timeField = new JTextField(6);
    98      private JLabel phaseLabel = new JLabel();
    99      private JProgressBar tokenizingFilesBar = new JProgressBar();
    100     private JTextArea resultsTextArea = new JTextArea();
    101     private JCheckBox recurseCheckbox = new JCheckBox("", true);
    102     private JCheckBox ignoreLiteralsCheckbox = new JCheckBox("", false);
    103     private JComboBox languageBox = new JComboBox();
    104     private JTextField extensionField = new JTextField(".java");
    105     private JFileChooser fcSave = new JFileChooser();
    106
    107     private JFrame frame;
    108
    109     private List matches = new ArrayList();
    110
    111     public GUI() {
    112         frame = new JFrame("PMD Duplicate Code Detector");
    113
    114         timeField.setEditable(false);
    115
    116         JMenu fileMenu = new JMenu("File");
    117         fileMenu.setMnemonic('f');
    118         JMenuItem saveItem = new JMenuItem("Save Text");
    119         saveItem.setMnemonic('s');
    120         saveItem.addActionListener(new SaveListener());
    121         fileMenu.add(saveItem);
    122         saveItem.setActionCommand(".txt");
    123         saveItem = new JMenuItem("Save XML");
    124         saveItem.addActionListener(new SaveListener());
    125         fileMenu.add(saveItem);
    126         saveItem.setActionCommand(".xml");
    127         saveItem = new JMenuItem("Save CSV");
    128         saveItem.setActionCommand(".csv");
    129         saveItem.addActionListener(new SaveListener());
    130         fileMenu.add(saveItem);
    131         JMenuItem exitItem = new JMenuItem("Exit");
    132         exitItem.setMnemonic('x');
    133         exitItem.addActionListener(new CancelListener());
    134         fileMenu.add(exitItem);
    135         JMenuBar menuBar = new JMenuBar();
    136         menuBar.add(fileMenu);
    137         frame.setJMenuBar(menuBar);
    138
    139         // first make all the buttons
    140         JButton browseButton = new JButton("Browse");
    141         browseButton.setMnemonic('b');
    142         browseButton.addActionListener(new BrowseListener());
    143         JButton goButton = new JButton("Go");
    144         goButton.setMnemonic('g');
    145         goButton.addActionListener(new GoListener());
    146         JButton cxButton = new JButton("Cancel");
    147         cxButton.addActionListener(new CancelListener());
    148
    149         JPanel settingsPanel = makeSettingsPanel(browseButton, goButton, cxButton);
    150         JPanel progressPanel = makeProgressPanel();
    151         JPanel resultsPanel = makeResultsPanel();
    152
    153         frame.getContentPane().setLayout(new BorderLayout());
    154         JPanel topPanel = new JPanel();
    155         topPanel.setLayout(new BorderLayout());
    156         topPanel.add(settingsPanel, BorderLayout.NORTH);
    157         topPanel.add(progressPanel, BorderLayout.CENTER);
    158         frame.getContentPane().add(topPanel, BorderLayout.NORTH);
    159         frame.getContentPane().add(resultsPanel, BorderLayout.CENTER);
    160         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    161         frame.pack();
    162         frame.show();
    163     }
    164
    165     private JPanel makeSettingsPanel(JButton browseButton, JButton goButton, JButton cxButton) {
    166         JPanel settingsPanel = new JPanel();
    167         GridBagHelper helper = new GridBagHelper(settingsPanel, new double[]{0.2, 0.7, 0.1, 0.1});
    168         helper.addLabel("Root source directory:");
    169         helper.add(rootDirectoryField);
    170         helper.add(browseButton, 2);
    171         helper.nextRow();
    172         helper.addLabel("Report duplicate chunks larger than:");
    173         minimumLengthField.setColumns(4);
    174         helper.add(minimumLengthField);
    175         helper.addLabel("Language:");
    176         languageBox.addItem("Java");
    177         languageBox.addItem("C++");
    178         languageBox.addItem("PHP");
    179         languageBox.addItem("Ruby");
    180         languageBox.addItem("by extension...");
    181         languageBox.addActionListener(new ActionListener() {
    182             public void actionPerformed(ActionEvent e) {
    183                 ignoreLiteralsCheckbox.setEnabled(languageBox.getSelectedItem().equals("Java"));
    184                 extensionField.setEnabled(languageBox.getSelectedItem().equals("by extension..."));
    185             }
    186         });
    187         helper.add(languageBox);
    188         helper.nextRow();
    189         helper.addLabel("Also scan subdirectories?");
    190         helper.add(recurseCheckbox);
    191
    192         helper.addLabel("Extension:");
    193         helper.add(extensionField);
    194         extensionField.setEnabled(false);
    195
    196         helper.nextRow();
    197         helper.addLabel("Ignore literals and identifiers?");
    198         helper.add(ignoreLiteralsCheckbox);
    199         helper.add(goButton);
    200         helper.add(cxButton);
    201         helper.nextRow();
    202         settingsPanel.setBorder(BorderFactory.createTitledBorder("Settings"));
    203         return settingsPanel;
    204     }
    205
    206     private JPanel makeProgressPanel() {
    207         JPanel progressPanel = new JPanel();
    208         final double[] weights = {0.0, 0.8, 0.4, 0.2};
    209         GridBagHelper helper = new GridBagHelper(progressPanel, weights);
    210         helper.addLabel("Tokenizing files:");
    211         helper.add(tokenizingFilesBar, 3);
    212         helper.nextRow();
    213         helper.addLabel("Phase:");
    214         helper.add(phaseLabel);
    215         helper.addLabel("Time elapsed:");
    216         helper.add(timeField);
    217         helper.nextRow();
    218         progressPanel.setBorder(BorderFactory.createTitledBorder("Progress"));
    219         return progressPanel;
    220     }
    221
    222     private JPanel makeResultsPanel() {
    223         JPanel resultsPanel = new JPanel();
    224         resultsPanel.setLayout(new BorderLayout());
    225         JScrollPane areaScrollPane = new JScrollPane(resultsTextArea);
    226         areaScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
    227         areaScrollPane.setPreferredSize(new Dimension(600, 300));
    228         resultsPanel.add(areaScrollPane, BorderLayout.CENTER);
    229         resultsPanel.setBorder(BorderFactory.createTitledBorder("Results"));
    230         return resultsPanel;
    231     }
    232
    233     private void go() {
    234         try {
    235             if (!(new File(rootDirectoryField.getText())).exists()) {
    236                 JOptionPane.showMessageDialog(frame,
    237                         "Can't read from that root source directory",
    238                         "Error",
    239                         JOptionPane.ERROR_MESSAGE);
    240                 return;
    241             }
    242
    243             Language language = null;
    244             LanguageFactory lf = new LanguageFactory();
    245             Properties p = new Properties();
    246             p.setProperty(JavaTokenizer.IGNORE_LITERALS, String.valueOf(ignoreLiteralsCheckbox.isSelected()));
    247             p.setProperty(LanguageFactory.EXTENSION, extensionField.getText());
    248             if (languageBox.getSelectedItem().equals("Java")) {
    249                 language = lf.createLanguage(LanguageFactory.JAVA_KEY, p);
    250             } else if (languageBox.getSelectedItem().equals("C++")) {
    251                 language = lf.createLanguage(LanguageFactory.CPP_KEY);
    252             } else if (languageBox.getSelectedItem().equals("Ruby")) {
    253                 language = lf.createLanguage(LanguageFactory.RUBY_KEY);
    254             } else if (languageBox.getSelectedItem().equals("by extension...")) {
    255                 language = lf.createLanguage(LanguageFactory.BY_EXTENSION, p);
    256             } else if (languageBox.getSelectedItem().equals("PHP")) {
    257                 language = lf.createLanguage(LanguageFactory.PHP_KEY);
    258             }
    259             CPD cpd = new CPD(Integer.parseInt(minimumLengthField.getText()), language);
    260             cpd.setCpdListener(this);
    261             tokenizingFilesBar.setMinimum(0);
    262             phaseLabel.setText("");
    263             if (rootDirectoryField.getText().endsWith(".class")
    264                     || rootDirectoryField.getText().endsWith(".php")
    265                     || rootDirectoryField.getText().endsWith(".rb")
    266                     || rootDirectoryField.getText().endsWith(".java")
    267                     || rootDirectoryField.getText().endsWith(".cpp")
    268                     || rootDirectoryField.getText().endsWith(".c")) {
    269                 cpd.add(new File(rootDirectoryField.getText()));
    270             } else {
    271                 if (recurseCheckbox.isSelected()) {
    272                     cpd.addRecursively(rootDirectoryField.getText());
    273                 } else {
    274                     cpd.addAllInDirectory(rootDirectoryField.getText());
    275                 }
    276             }
    277             final long start = System.currentTimeMillis();
    278             Timer t = new Timer(1000, new ActionListener() {
    279                 public void actionPerformed(ActionEvent e) {
    280                     long now = System.currentTimeMillis();
    281                     long elapsedMillis = now - start;
    282                     long elapsedSeconds = elapsedMillis / 1000;
    283                     long minutes = (long) Math.floor(elapsedSeconds / 60);
    284                     long seconds = elapsedSeconds - (minutes * 60);
    285                     timeField.setText(""
    286                             + munge(String.valueOf(minutes))
    287                             + ":"
    288                             + munge(String.valueOf(seconds)));
    289                 }
    290
    291                 private String munge(String in) {
    292                     if (in.length() < 2) {
    293                         in = "0" + in;
    294                     }
    295                     return in;
    296                 }
    297
    298             });
    299             t.start();
    300             cpd.go();
    301             t.stop();
    302             matches = new ArrayList();
    303             for (Iterator i = cpd.getMatches(); i.hasNext();) {
    304                 Match m = (Match) i.next();
    305                 matches.add(m);
    306             }
    307
    308             String report = new SimpleRenderer().render(cpd.getMatches());
    309             if (report.length() == 0) {
    310                 JOptionPane.showMessageDialog(frame,
    311                         "Done; couldn't find any duplicates longer than " + minimumLengthField.getText() + " tokens");
    312             } else {
    313                 resultsTextArea.setText(report);
    314             }
    315         } catch (Throwable t) {
    316             t.printStackTrace();
    317             JOptionPane.showMessageDialog(frame, "Halted due to " + t.getClass().getName() + "; " + t.getMessage());
    318         }
    319     }
    320
    321     // CPDListener
    322     public void phaseUpdate(int phase) {
    323         phaseLabel.setText(getPhaseText(phase));
    324     }
    325
    326     public String getPhaseText(int phase) {
    327         switch (phase) {
    328             case CPDListener.INIT:
    329                 return "Initializing";
    330             case CPDListener.HASH:
    331                 return "Hashing";
    332             case CPDListener.MATCH:
    333                 return "Matching";
    334             case CPDListener.GROUPING:
    335                 return "Grouping";
    336             case CPDListener.DONE:
    337                 return "Done";
    338             default :
    339                 return "Unknown";
    340         }
    341     }
    342
    343     public void addedFile(int fileCount, File file) {
    344         tokenizingFilesBar.setMaximum(fileCount);
    345         tokenizingFilesBar.setValue(tokenizingFilesBar.getValue() + 1);
    346     }
    347     // CPDListener
    348
    349     public static void main(String[] args) {
    350         //this should prevent the disk not found popup
    351         System.setSecurityManager(null);
    352         new GUI();
    353     }
    354
    355 }