001: /**
002: * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003: */package net.sourceforge.pmd.cpd;
004:
005: import javax.swing.*;
006: import javax.swing.event.ListSelectionEvent;
007: import javax.swing.event.ListSelectionListener;
008: import javax.swing.event.TableModelListener;
009: import javax.swing.table.DefaultTableCellRenderer;
010: import javax.swing.table.JTableHeader;
011: import javax.swing.table.TableColumn;
012: import javax.swing.table.TableColumnModel;
013: import javax.swing.table.TableModel;
014: import java.awt.BorderLayout;
015: import java.awt.Component;
016: import java.awt.Dimension;
017: import java.awt.Point;
018: import java.awt.Toolkit;
019: import java.awt.datatransfer.StringSelection;
020: import java.awt.event.ActionEvent;
021: import java.awt.event.ActionListener;
022: import java.awt.event.ItemEvent;
023: import java.awt.event.ItemListener;
024: import java.awt.event.KeyEvent;
025: import java.awt.event.MouseAdapter;
026: import java.awt.event.MouseEvent;
027: import java.io.File;
028: import java.io.FileOutputStream;
029: import java.io.IOException;
030: import java.io.PrintWriter;
031: import java.util.ArrayList;
032: import java.util.Collections;
033: import java.util.Comparator;
034: import java.util.HashMap;
035: import java.util.HashSet;
036: import java.util.Iterator;
037: import java.util.List;
038: import java.util.Map;
039: import java.util.Properties;
040: import java.util.Set;
041: import net.sourceforge.pmd.PMD;
042:
043: public class GUI implements CPDListener {
044:
045: private interface Renderer {
046: String render(Iterator<Match> items);
047: }
048:
049: private static final Object[][] rendererSets = new Object[][] {
050: { "Text", new Renderer() {
051: public String render(Iterator<Match> items) {
052: return new SimpleRenderer().render(items);
053: }
054: } }, { "XML", new Renderer() {
055: public String render(Iterator<Match> items) {
056: return new XMLRenderer().render(items);
057: }
058: } }, { "CSV (comma)", new Renderer() {
059: public String render(Iterator<Match> items) {
060: return new CSVRenderer(',').render(items);
061: }
062: } }, { "CSV (tab)", new Renderer() {
063: public String render(Iterator<Match> items) {
064: return new CSVRenderer('\t').render(items);
065: }
066: } } };
067:
068: private interface LanguageConfig {
069: Language languageFor(LanguageFactory lf, Properties p);
070:
071: boolean ignoreLiteralsByDefault();
072:
073: String[] extensions();
074: };
075:
076: private static final Object[][] languageSets = new Object[][] {
077: { "Java", new LanguageConfig() {
078: public Language languageFor(LanguageFactory lf,
079: Properties p) {
080: return lf.createLanguage("java");
081: }
082:
083: public boolean ignoreLiteralsByDefault() {
084: return true;
085: }
086:
087: public String[] extensions() {
088: return new String[] { ".java", ".class" };
089: };
090: } }, { "JSP", new LanguageConfig() {
091: public Language languageFor(LanguageFactory lf,
092: Properties p) {
093: return lf.createLanguage("jsp");
094: }
095:
096: public boolean ignoreLiteralsByDefault() {
097: return false;
098: }
099:
100: public String[] extensions() {
101: return new String[] { ".jsp" };
102: };
103: } }, { "C++", new LanguageConfig() {
104: public Language languageFor(LanguageFactory lf,
105: Properties p) {
106: return lf.createLanguage("cpp");
107: }
108:
109: public boolean ignoreLiteralsByDefault() {
110: return false;
111: }
112:
113: public String[] extensions() {
114: return new String[] { ".cpp", ".c" };
115: };
116: } }, { "Ruby", new LanguageConfig() {
117: public Language languageFor(LanguageFactory lf,
118: Properties p) {
119: return lf.createLanguage("ruby");
120: }
121:
122: public boolean ignoreLiteralsByDefault() {
123: return false;
124: }
125:
126: public String[] extensions() {
127: return new String[] { ".rb" };
128: };
129: } }, { "Fortran", new LanguageConfig() {
130: public Language languageFor(LanguageFactory lf,
131: Properties p) {
132: return lf.createLanguage("fortran");
133: }
134:
135: public boolean ignoreLiteralsByDefault() {
136: return false;
137: }
138:
139: public String[] extensions() {
140: return new String[] { ".rb" };
141: };
142: } }, { "by extension...", new LanguageConfig() {
143: public Language languageFor(LanguageFactory lf,
144: Properties p) {
145: return lf.createLanguage(
146: LanguageFactory.BY_EXTENSION, p);
147: }
148:
149: public boolean ignoreLiteralsByDefault() {
150: return false;
151: }
152:
153: public String[] extensions() {
154: return new String[] { "" };
155: };
156: } }, { "PHP", new LanguageConfig() {
157: public Language languageFor(LanguageFactory lf,
158: Properties p) {
159: return lf.createLanguage("php");
160: }
161:
162: public boolean ignoreLiteralsByDefault() {
163: return false;
164: }
165:
166: public String[] extensions() {
167: return new String[] { ".php" };
168: };
169: } }, };
170:
171: private static final int defaultCPDMinimumLength = 75;
172: private static final Map langConfigsByLabel = new HashMap(
173: languageSets.length);
174: private static final KeyStroke copy = KeyStroke.getKeyStroke(
175: KeyEvent.VK_C, ActionEvent.CTRL_MASK, false);
176: private static final KeyStroke delete = KeyStroke.getKeyStroke(
177: KeyEvent.VK_DELETE, 0);
178:
179: private class ColumnSpec {
180: private String label;
181: private int alignment;
182: private int width;
183: private Comparator<Match> sorter;
184:
185: public ColumnSpec(String aLabel, int anAlignment, int aWidth,
186: Comparator<Match> aSorter) {
187: label = aLabel;
188: alignment = anAlignment;
189: width = aWidth;
190: sorter = aSorter;
191: }
192:
193: public String label() {
194: return label;
195: };
196:
197: public int alignment() {
198: return alignment;
199: };
200:
201: public int width() {
202: return width;
203: };
204:
205: public Comparator<Match> sorter() {
206: return sorter;
207: };
208: }
209:
210: private final ColumnSpec[] matchColumns = new ColumnSpec[] {
211: new ColumnSpec("Source", SwingConstants.LEFT, -1,
212: Match.LabelComparator),
213: new ColumnSpec("Matches", SwingConstants.RIGHT, 60,
214: Match.MatchesComparator),
215: new ColumnSpec("Lines", SwingConstants.RIGHT, 45,
216: Match.LinesComparator), };
217:
218: static {
219: for (int i = 0; i < languageSets.length; i++) {
220: langConfigsByLabel.put(languageSets[i][0],
221: languageSets[i][1]);
222: }
223: }
224:
225: private static LanguageConfig languageConfigFor(String label) {
226: return (LanguageConfig) langConfigsByLabel.get(label);
227: }
228:
229: private static class CancelListener implements ActionListener {
230: public void actionPerformed(ActionEvent e) {
231: System.exit(0);
232: }
233: }
234:
235: private class GoListener implements ActionListener {
236: public void actionPerformed(ActionEvent e) {
237: new Thread(new Runnable() {
238: public void run() {
239: tokenizingFilesBar.setValue(0);
240: tokenizingFilesBar.setString("");
241: resultsTextArea.setText("");
242: phaseLabel.setText("");
243: timeField.setText("");
244: go();
245: }
246: }).start();
247: }
248: }
249:
250: private class SaveListener implements ActionListener {
251:
252: final Renderer renderer;
253:
254: public SaveListener(Renderer theRenderer) {
255: renderer = theRenderer;
256: }
257:
258: public void actionPerformed(ActionEvent evt) {
259: JFileChooser fcSave = new JFileChooser();
260: int ret = fcSave.showSaveDialog(GUI.this .frame);
261: File f = fcSave.getSelectedFile();
262: if (f == null || ret != JFileChooser.APPROVE_OPTION)
263: return;
264:
265: if (!f.canWrite()) {
266: PrintWriter pw = null;
267: try {
268: pw = new PrintWriter(new FileOutputStream(f));
269: pw.write(renderer.render(matches.iterator()));
270: pw.flush();
271: JOptionPane.showMessageDialog(frame, "Saved "
272: + matches.size() + " matches");
273: } catch (IOException e) {
274: error("Couldn't save file" + f.getAbsolutePath(), e);
275: } finally {
276: if (pw != null)
277: pw.close();
278: }
279: } else {
280: error("Could not write to file " + f.getAbsolutePath(),
281: null);
282: }
283: }
284:
285: private void error(String message, Exception e) {
286: if (e != null) {
287: e.printStackTrace();
288: }
289: JOptionPane.showMessageDialog(GUI.this .frame, message);
290: }
291:
292: }
293:
294: private class BrowseListener implements ActionListener {
295: public void actionPerformed(ActionEvent e) {
296: JFileChooser fc = new JFileChooser(rootDirectoryField
297: .getText());
298: fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
299: fc.showDialog(frame, "Select");
300: if (fc.getSelectedFile() != null) {
301: rootDirectoryField.setText(fc.getSelectedFile()
302: .getAbsolutePath());
303: }
304: }
305: }
306:
307: private class AlignmentRenderer extends DefaultTableCellRenderer {
308:
309: private int[] alignments;
310:
311: public AlignmentRenderer(int[] theAlignments) {
312: alignments = theAlignments;
313: };
314:
315: public Component getTableCellRendererComponent(JTable table,
316: Object value, boolean isSelected, boolean hasFocus,
317: int row, int column) {
318: super .getTableCellRendererComponent(table, value,
319: isSelected, hasFocus, row, column);
320:
321: setHorizontalAlignment(alignments[column]);
322:
323: return this ;
324: }
325: }
326:
327: private JTextField rootDirectoryField = new JTextField(System
328: .getProperty("user.home"));
329: private JTextField minimumLengthField = new JTextField(Integer
330: .toString(defaultCPDMinimumLength));
331: private JTextField timeField = new JTextField(6);
332: private JLabel phaseLabel = new JLabel();
333: private JProgressBar tokenizingFilesBar = new JProgressBar();
334: private JTextArea resultsTextArea = new JTextArea();
335: private JCheckBox recurseCheckbox = new JCheckBox("", true);
336: private JCheckBox ignoreLiteralsCheckbox = new JCheckBox("", false);
337: private JComboBox languageBox = new JComboBox();
338: private JTextField extensionField = new JTextField();
339: private JLabel extensionLabel = new JLabel("Extension:",
340: SwingConstants.RIGHT);
341: private JTable resultsTable = new JTable();
342: private JButton goButton;
343: private JButton cancelButton;
344: private JPanel progressPanel;
345: private JFrame frame;
346: private boolean trimLeadingWhitespace;
347:
348: private List<Match> matches = new ArrayList<Match>();
349:
350: private void addSaveOptionsTo(JMenu menu) {
351:
352: JMenuItem saveItem;
353:
354: for (int i = 0; i < rendererSets.length; i++) {
355: saveItem = new JMenuItem("Save as " + rendererSets[i][0]);
356: saveItem.addActionListener(new SaveListener(
357: (Renderer) rendererSets[i][1]));
358: menu.add(saveItem);
359: }
360: }
361:
362: public GUI() {
363: frame = new JFrame("PMD Duplicate Code Detector (v "
364: + PMD.VERSION + ')');
365:
366: timeField.setEditable(false);
367:
368: JMenu fileMenu = new JMenu("File");
369: fileMenu.setMnemonic('f');
370:
371: addSaveOptionsTo(fileMenu);
372:
373: JMenuItem exitItem = new JMenuItem("Exit");
374: exitItem.setMnemonic('x');
375: exitItem.addActionListener(new CancelListener());
376: fileMenu.add(exitItem);
377: JMenu viewMenu = new JMenu("View");
378: fileMenu.setMnemonic('v');
379: JMenuItem trimItem = new JCheckBoxMenuItem(
380: "Trim leading whitespace");
381: trimItem.addItemListener(new ItemListener() {
382: public void itemStateChanged(ItemEvent e) {
383: AbstractButton button = (AbstractButton) e.getItem();
384: GUI.this .trimLeadingWhitespace = button.isSelected();
385: }
386: });
387: viewMenu.add(trimItem);
388: JMenuBar menuBar = new JMenuBar();
389: menuBar.add(fileMenu);
390: menuBar.add(viewMenu);
391: frame.setJMenuBar(menuBar);
392:
393: // first make all the buttons
394: JButton browseButton = new JButton("Browse");
395: browseButton.setMnemonic('b');
396: browseButton.addActionListener(new BrowseListener());
397: goButton = new JButton("Go");
398: goButton.setMnemonic('g');
399: goButton.addActionListener(new GoListener());
400: cancelButton = new JButton("Cancel");
401: cancelButton.addActionListener(new CancelListener());
402:
403: JPanel settingsPanel = makeSettingsPanel(browseButton,
404: goButton, cancelButton);
405: progressPanel = makeProgressPanel();
406: JPanel resultsPanel = makeResultsPanel();
407:
408: adjustLanguageControlsFor((LanguageConfig) languageSets[0][1]);
409:
410: frame.getContentPane().setLayout(new BorderLayout());
411: JPanel topPanel = new JPanel();
412: topPanel.setLayout(new BorderLayout());
413: topPanel.add(settingsPanel, BorderLayout.NORTH);
414: topPanel.add(progressPanel, BorderLayout.CENTER);
415: setProgressControls(false); // not running now
416: frame.getContentPane().add(topPanel, BorderLayout.NORTH);
417: frame.getContentPane().add(resultsPanel, BorderLayout.CENTER);
418: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
419: frame.pack();
420: frame.setVisible(true);
421: }
422:
423: private void adjustLanguageControlsFor(LanguageConfig current) {
424: ignoreLiteralsCheckbox.setEnabled(current
425: .ignoreLiteralsByDefault());
426: extensionField.setText(current.extensions()[0]);
427: boolean enableExtension = current.extensions()[0].length() == 0;
428: extensionField.setEnabled(enableExtension);
429: extensionLabel.setEnabled(enableExtension);
430: }
431:
432: private JPanel makeSettingsPanel(JButton browseButton,
433: JButton goButton, JButton cxButton) {
434: JPanel settingsPanel = new JPanel();
435: GridBagHelper helper = new GridBagHelper(settingsPanel,
436: new double[] { 0.2, 0.7, 0.1, 0.1 });
437: helper.addLabel("Root source directory:");
438: helper.add(rootDirectoryField);
439: helper.add(browseButton, 2);
440: helper.nextRow();
441: helper.addLabel("Report duplicate chunks larger than:");
442: minimumLengthField.setColumns(4);
443: helper.add(minimumLengthField);
444: helper.addLabel("Language:");
445: for (int i = 0; i < languageSets.length; i++) {
446: languageBox.addItem(languageSets[i][0]);
447: }
448: languageBox.addActionListener(new ActionListener() {
449: public void actionPerformed(ActionEvent e) {
450: adjustLanguageControlsFor(languageConfigFor((String) languageBox
451: .getSelectedItem()));
452: }
453: });
454: helper.add(languageBox);
455: helper.nextRow();
456: helper.addLabel("Also scan subdirectories?");
457: helper.add(recurseCheckbox);
458:
459: helper.add(extensionLabel);
460: helper.add(extensionField);
461:
462: helper.nextRow();
463: helper.addLabel("Ignore literals and identifiers?");
464: helper.add(ignoreLiteralsCheckbox);
465: helper.add(goButton);
466: helper.add(cxButton);
467: helper.nextRow();
468: // settingsPanel.setBorder(BorderFactory.createTitledBorder("Settings"));
469: return settingsPanel;
470: }
471:
472: private JPanel makeProgressPanel() {
473: JPanel progressPanel = new JPanel();
474: final double[] weights = { 0.0, 0.8, 0.4, 0.2 };
475: GridBagHelper helper = new GridBagHelper(progressPanel, weights);
476: helper.addLabel("Tokenizing files:");
477: helper.add(tokenizingFilesBar, 3);
478: helper.nextRow();
479: helper.addLabel("Phase:");
480: helper.add(phaseLabel);
481: helper.addLabel("Time elapsed:");
482: helper.add(timeField);
483: helper.nextRow();
484: progressPanel.setBorder(BorderFactory
485: .createTitledBorder("Progress"));
486: return progressPanel;
487: }
488:
489: private JPanel makeResultsPanel() {
490: JPanel resultsPanel = new JPanel();
491: resultsPanel.setLayout(new BorderLayout());
492: JScrollPane areaScrollPane = new JScrollPane(resultsTextArea);
493: resultsTextArea.setEditable(false);
494: areaScrollPane
495: .setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
496: areaScrollPane.setPreferredSize(new Dimension(600, 300));
497:
498: resultsPanel.add(makeMatchList(), BorderLayout.WEST);
499: resultsPanel.add(areaScrollPane, BorderLayout.CENTER);
500: return resultsPanel;
501: }
502:
503: private void populateResultArea() {
504: int[] selectionIndices = resultsTable.getSelectedRows();
505: TableModel model = resultsTable.getModel();
506: List<Match> selections = new ArrayList<Match>(
507: selectionIndices.length);
508: for (int i = 0; i < selectionIndices.length; i++) {
509: selections.add((Match) model.getValueAt(
510: selectionIndices[i], 99));
511: }
512: String report = new SimpleRenderer(trimLeadingWhitespace)
513: .render(selections.iterator());
514: resultsTextArea.setText(report);
515: resultsTextArea.setCaretPosition(0); // move to the top
516: }
517:
518: private void copyMatchListSelectionsToClipboard() {
519:
520: int[] selectionIndices = resultsTable.getSelectedRows();
521: int colCount = resultsTable.getColumnCount();
522:
523: StringBuffer sb = new StringBuffer();
524:
525: for (int r = 0; r < selectionIndices.length; r++) {
526: if (r > 0)
527: sb.append('\n');
528: sb.append(resultsTable.getValueAt(selectionIndices[r], 0));
529: for (int c = 1; c < colCount; c++) {
530: sb.append('\t');
531: sb.append(resultsTable.getValueAt(selectionIndices[r],
532: c));
533: }
534: }
535:
536: StringSelection ss = new StringSelection(sb.toString());
537: Toolkit.getDefaultToolkit().getSystemClipboard().setContents(
538: ss, null);
539: }
540:
541: private void deleteMatchlistSelections() {
542:
543: int[] selectionIndices = resultsTable.getSelectedRows();
544:
545: for (int i = selectionIndices.length - 1; i >= 0; i--) {
546: matches.remove(selectionIndices[i]);
547: }
548:
549: resultsTable.getSelectionModel().clearSelection();
550: resultsTable.addNotify();
551: }
552:
553: private JComponent makeMatchList() {
554:
555: resultsTable.getSelectionModel().addListSelectionListener(
556: new ListSelectionListener() {
557: public void valueChanged(ListSelectionEvent e) {
558: populateResultArea();
559: }
560: });
561:
562: resultsTable.registerKeyboardAction(new ActionListener() {
563: public void actionPerformed(ActionEvent e) {
564: copyMatchListSelectionsToClipboard();
565: }
566: }, "Copy", copy, JComponent.WHEN_FOCUSED);
567:
568: resultsTable.registerKeyboardAction(new ActionListener() {
569: public void actionPerformed(ActionEvent e) {
570: deleteMatchlistSelections();
571: }
572: }, "Del", delete, JComponent.WHEN_FOCUSED);
573:
574: int[] alignments = new int[matchColumns.length];
575: for (int i = 0; i < alignments.length; i++)
576: alignments[i] = matchColumns[i].alignment();
577:
578: resultsTable.setDefaultRenderer(Object.class,
579: new AlignmentRenderer(alignments));
580:
581: final JTableHeader header = resultsTable.getTableHeader();
582: header.addMouseListener(new MouseAdapter() {
583: public void mouseClicked(MouseEvent e) {
584: sortOnColumn(header.columnAtPoint(new Point(e.getX(), e
585: .getY())));
586: }
587: });
588:
589: return new JScrollPane(resultsTable);
590: }
591:
592: private boolean isLegalPath(String path, LanguageConfig config) {
593: String[] extensions = config.extensions();
594: for (int i = 0; i < extensions.length; i++) {
595: if (path.endsWith(extensions[i])
596: && extensions[i].length() > 0)
597: return true;
598: }
599: return false;
600: }
601:
602: private String setLabelFor(Match match) {
603:
604: Set<String> sourceIDs = new HashSet<String>(match
605: .getMarkCount());
606: for (Iterator<TokenEntry> occurrences = match.iterator(); occurrences
607: .hasNext();) {
608: sourceIDs.add(occurrences.next().getTokenSrcID());
609: }
610: String label;
611:
612: if (sourceIDs.size() == 1) {
613: String sourceId = sourceIDs.iterator().next();
614: int separatorPos = sourceId.lastIndexOf(File.separatorChar);
615: label = "..." + sourceId.substring(separatorPos);
616: } else {
617: label = "(" + sourceIDs.size() + " separate files)";
618: }
619:
620: match.setLabel(label);
621: return label;
622: }
623:
624: private void setProgressControls(boolean isRunning) {
625: progressPanel.setVisible(isRunning);
626: goButton.setEnabled(!isRunning);
627: cancelButton.setEnabled(isRunning);
628: }
629:
630: private void go() {
631: String dirPath = rootDirectoryField.getText();
632: try {
633: if (!(new File(dirPath)).exists()) {
634: JOptionPane.showMessageDialog(frame,
635: "Can't read from that root source directory",
636: "Error", JOptionPane.ERROR_MESSAGE);
637: return;
638: }
639:
640: setProgressControls(true);
641:
642: Properties p = new Properties();
643: p.setProperty(JavaTokenizer.IGNORE_LITERALS, String
644: .valueOf(ignoreLiteralsCheckbox.isSelected()));
645: p.setProperty(LanguageFactory.EXTENSION, extensionField
646: .getText());
647: LanguageConfig conf = languageConfigFor((String) languageBox
648: .getSelectedItem());
649: Language language = conf.languageFor(new LanguageFactory(),
650: p);
651: CPD cpd = new CPD(Integer.parseInt(minimumLengthField
652: .getText()), language);
653: cpd.setCpdListener(this );
654: tokenizingFilesBar.setMinimum(0);
655: phaseLabel.setText("");
656: if (isLegalPath(dirPath, conf)) { // should use the language file filter instead?
657: cpd.add(new File(dirPath));
658: } else {
659: if (recurseCheckbox.isSelected()) {
660: cpd.addRecursively(dirPath);
661: } else {
662: cpd.addAllInDirectory(dirPath);
663: }
664: }
665: final long start = System.currentTimeMillis();
666: Timer t = new Timer(1000, new ActionListener() {
667: public void actionPerformed(ActionEvent e) {
668: long now = System.currentTimeMillis();
669: long elapsedMillis = now - start;
670: long elapsedSeconds = elapsedMillis / 1000;
671: long minutes = (long) Math
672: .floor(elapsedSeconds / 60);
673: long seconds = elapsedSeconds - (minutes * 60);
674: timeField.setText(munge(String.valueOf(minutes))
675: + ':' + munge(String.valueOf(seconds)));
676: }
677:
678: private String munge(String in) {
679: if (in.length() < 2) {
680: in = "0" + in;
681: }
682: return in;
683: }
684: });
685: t.start();
686: cpd.go();
687: t.stop();
688:
689: matches = new ArrayList<Match>();
690: Match match;
691: for (Iterator<Match> i = cpd.getMatches(); i.hasNext();) {
692: match = i.next();
693: setLabelFor(match);
694: matches.add(match);
695: }
696:
697: String report = new SimpleRenderer().render(cpd
698: .getMatches());
699: if (report.length() == 0) {
700: JOptionPane.showMessageDialog(frame,
701: "Done; couldn't find any duplicates longer than "
702: + minimumLengthField.getText()
703: + " tokens");
704: } else {
705: resultsTextArea.setText(report);
706: setListDataFrom(cpd.getMatches());
707:
708: }
709: } catch (IOException t) {
710: t.printStackTrace();
711: JOptionPane.showMessageDialog(frame, "Halted due to "
712: + t.getClass().getName() + "; " + t.getMessage());
713: } catch (RuntimeException t) {
714: t.printStackTrace();
715: JOptionPane.showMessageDialog(frame, "Halted due to "
716: + t.getClass().getName() + "; " + t.getMessage());
717: }
718: setProgressControls(false);
719: }
720:
721: private interface SortingTableModel<E> extends TableModel {
722: public int sortColumn();
723:
724: public void sortColumn(int column);
725:
726: public boolean sortDescending();
727:
728: public void sortDescending(boolean flag);
729:
730: public void sort(Comparator<E> comparator);
731: }
732:
733: private TableModel tableModelFrom(final List<Match> items) {
734:
735: TableModel model = new SortingTableModel<Match>() {
736:
737: private int sortColumn;
738: private boolean sortDescending;
739:
740: public Object getValueAt(int rowIndex, int columnIndex) {
741: Match match = items.get(rowIndex);
742: switch (columnIndex) {
743: case 0:
744: return match.getLabel();
745: case 2:
746: return Integer.toString(match.getLineCount());
747: case 1:
748: return match.getMarkCount() > 2 ? Integer
749: .toString(match.getMarkCount()) : "";
750: case 99:
751: return match;
752: }
753: return "";
754: }
755:
756: public int getColumnCount() {
757: return matchColumns.length;
758: }
759:
760: public int getRowCount() {
761: return items.size();
762: }
763:
764: public boolean isCellEditable(int rowIndex, int columnIndex) {
765: return false;
766: }
767:
768: public Class<?> getColumnClass(int columnIndex) {
769: return Object.class;
770: }
771:
772: public void setValueAt(Object aValue, int rowIndex,
773: int columnIndex) {
774: }
775:
776: public String getColumnName(int i) {
777: return matchColumns[i].label();
778: }
779:
780: public void addTableModelListener(TableModelListener l) {
781: }
782:
783: public void removeTableModelListener(TableModelListener l) {
784: }
785:
786: public int sortColumn() {
787: return sortColumn;
788: };
789:
790: public void sortColumn(int column) {
791: sortColumn = column;
792: };
793:
794: public boolean sortDescending() {
795: return sortDescending;
796: };
797:
798: public void sortDescending(boolean flag) {
799: sortDescending = flag;
800: };
801:
802: public void sort(Comparator<Match> comparator) {
803: Collections.sort(items, comparator);
804: if (sortDescending)
805: Collections.reverse(items);
806: }
807: };
808:
809: return model;
810: }
811:
812: private void sortOnColumn(int columnIndex) {
813: Comparator<Match> comparator = matchColumns[columnIndex]
814: .sorter();
815: SortingTableModel<Match> model = (SortingTableModel<Match>) resultsTable
816: .getModel();
817: if (model.sortColumn() == columnIndex) {
818: model.sortDescending(!model.sortDescending());
819: }
820: model.sortColumn(columnIndex);
821: model.sort(comparator);
822:
823: resultsTable.getSelectionModel().clearSelection();
824: resultsTable.repaint();
825: }
826:
827: private void setListDataFrom(Iterator iter) {
828:
829: resultsTable.setModel(tableModelFrom(matches));
830:
831: TableColumnModel colModel = resultsTable.getColumnModel();
832: TableColumn column;
833: int width;
834:
835: for (int i = 0; i < matchColumns.length; i++) {
836: if (matchColumns[i].width() > 0) {
837: column = colModel.getColumn(i);
838: width = matchColumns[i].width();
839: column.setPreferredWidth(width);
840: column.setMinWidth(width);
841: column.setMaxWidth(width);
842: }
843: }
844: }
845:
846: // CPDListener
847: public void phaseUpdate(int phase) {
848: phaseLabel.setText(getPhaseText(phase));
849: }
850:
851: public String getPhaseText(int phase) {
852: switch (phase) {
853: case CPDListener.INIT:
854: return "Initializing";
855: case CPDListener.HASH:
856: return "Hashing";
857: case CPDListener.MATCH:
858: return "Matching";
859: case CPDListener.GROUPING:
860: return "Grouping";
861: case CPDListener.DONE:
862: return "Done";
863: default:
864: return "Unknown";
865: }
866: }
867:
868: public void addedFile(int fileCount, File file) {
869: tokenizingFilesBar.setMaximum(fileCount);
870: tokenizingFilesBar.setValue(tokenizingFilesBar.getValue() + 1);
871: }
872:
873: // CPDListener
874:
875: public static void main(String[] args) {
876: //this should prevent the disk not found popup
877: // System.setSecurityManager(null);
878: new GUI();
879: }
880:
881: }
|