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 }