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