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