1 package com.melloware.jukes.gui.view.dialogs;
2
3 import java.awt.BorderLayout;
4 import java.awt.EventQueue;
5 import java.awt.Frame;
6 import java.awt.event.ActionEvent;
7 import java.io.File;
8 import java.io.IOException;
9 import java.lang.reflect.InvocationTargetException;
10 import java.util.ArrayList;
11 import java.util.Collection;
12 import java.util.Collections;
13 import java.util.Enumeration;
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Locale;
18 import java.util.Map;
19
20 import javax.swing.AbstractAction;
21 import javax.swing.Action;
22 import javax.swing.DefaultListModel;
23 import javax.swing.JButton;
24 import javax.swing.JCheckBox;
25 import javax.swing.JComponent;
26 import javax.swing.JFileChooser;
27 import javax.swing.JList;
28 import javax.swing.JPanel;
29 import javax.swing.JProgressBar;
30 import javax.swing.JScrollPane;
31 import javax.swing.JTextField;
32 import javax.swing.ListSelectionModel;
33 import javax.swing.text.JTextComponent;
34
35 import org.apache.commons.collections.list.SetUniqueList;
36 import org.apache.commons.io.FileUtils;
37 import org.apache.commons.lang.StringUtils;
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40
41 import com.jgoodies.forms.builder.PanelBuilder;
42 import com.jgoodies.forms.factories.Borders;
43 import com.jgoodies.forms.factories.ButtonBarFactory;
44 import com.jgoodies.forms.layout.CellConstraints;
45 import com.jgoodies.forms.layout.FormLayout;
46 import com.jgoodies.uif.AbstractDialog;
47 import com.jgoodies.uif.util.Worker;
48 import com.jgoodies.uifextras.panel.HeaderPanel;
49 import com.jgoodies.validation.Severity;
50 import com.jgoodies.validation.ValidationMessage;
51 import com.melloware.jukes.db.HibernateDao;
52 import com.melloware.jukes.exception.InfrastructureException;
53 import com.melloware.jukes.file.filter.FilterFactory;
54 import com.melloware.jukes.gui.tool.Resources;
55 import com.melloware.jukes.gui.tool.Settings;
56 import com.melloware.jukes.gui.view.component.ComponentFactory;
57 import com.melloware.jukes.gui.view.component.MessageCellRenderer;
58 import com.melloware.jukes.util.GuiUtil;
59 import com.melloware.jukes.util.JukesValidationMessage;
60 import com.melloware.jukes.util.MessageUtil;
61
62
63
64
65
66
67
68
69
70
71 @SuppressWarnings("unchecked")
72 public final class DifferenceToolDialog
73 extends AbstractDialog {
74
75 private static final Log LOG = LogFactory.getLog(DifferenceToolDialog.class);
76 private static final String BREAK = " - ";
77 private final Map mapDiscs = new HashMap();
78 private DefaultListModel listModel;
79 private JButton buttonCancel;
80 private JButton buttonClose;
81 private JButton buttonDiff;
82 private JButton buttonIntersection;
83 private JButton buttonSave;
84 private JButton buttonUnion;
85 private JComponent buttonBar;
86 private JList list;
87 private JPanel panel;
88 private JProgressBar progressBar;
89 private JTextComponent file;
90 private JCheckBox caseSensitive;
91 private final Settings settings;
92 private Worker worker;
93
94
95
96
97
98
99 public DifferenceToolDialog(Frame owner, Settings settings) {
100 super(owner);
101 LOG.debug("Difference Tool created.");
102 this.settings = settings;
103 this.settings.getStartInDirectory();
104 }
105
106
107
108
109 public void doCancel() {
110 LOG.debug("Cancel Pressed.");
111 if (worker != null) {
112 worker.interrupt();
113 }
114 }
115
116
117
118
119
120
121 protected JComponent buildContent() {
122 JPanel content = new JPanel(new BorderLayout());
123 content.add(buildMainPanel(), BorderLayout.CENTER);
124 content.add(buttonBar, BorderLayout.SOUTH);
125 return content;
126 }
127
128
129
130
131
132
133 protected JComponent buildHeader() {
134 return new HeaderPanel("Difference Analyzer Tool ",
135 "Select an exported catalog text file and this tool will \n"
136 + "compare all differences, unions, or intersections with the current catalog.\n",
137 Resources.DIFFERENCE_TOOL_ICON);
138 }
139
140
141
142
143
144
145 protected JComponent buildMainPanel() {
146
147 Action diff = new AbstractAction("Differences", Resources.DIFFERENCE_TOOL_DIFF_ICON) {
148
149 public void actionPerformed(ActionEvent evt) {
150 doCompare(evt);
151 }
152 };
153 Action intersection = new AbstractAction("Intersection", Resources.DIFFERENCE_TOOL_INTERSECTION_ICON) {
154
155 public void actionPerformed(ActionEvent evt) {
156 doCompare(evt);
157 }
158 };
159 Action union = new AbstractAction("Union", Resources.DIFFERENCE_TOOL_UNION_ICON) {
160
161 public void actionPerformed(ActionEvent evt) {
162 doCompare(evt);
163 }
164 };
165 Action export = new AbstractAction("Save", Resources.FILE_TEXT_ICON) {
166
167 public void actionPerformed(ActionEvent evt) {
168 save(evt);
169 }
170 };
171 buttonSave = new JButton(export);
172 buttonDiff = new JButton(diff);
173 buttonIntersection = new JButton(intersection);
174 buttonUnion = new JButton(union);
175 buttonCancel = createCancelButton();
176 buttonClose = createCloseButton(true);
177 buttonCancel.setEnabled(false);
178 JButton[] buttons = new JButton[6];
179 buttons[0] = buttonDiff;
180 buttons[1] = buttonIntersection;
181 buttons[2] = buttonUnion;
182 buttons[3] = buttonSave;
183 buttons[4] = buttonCancel;
184 buttons[5] = buttonClose;
185 buttonBar = ButtonBarFactory.buildCenteredBar(buttons);
186 FormLayout layout = new FormLayout("fill:pref:grow", "p, p, p, p, p, p, p, p");
187 PanelBuilder builder = new PanelBuilder(layout);
188 builder.setDefaultDialogBorder();
189 CellConstraints cc = new CellConstraints();
190 int row = 1;
191 builder.addSeparator("Find", cc.xy(1, row++));
192 builder.add(buildFilePanel(), cc.xy(1, row++));
193 builder.add(buildProgressPanel(), cc.xy(1, row++));
194 builder.add(buildOptionsPanel(), cc.xy(1, row++));
195 builder.add(buildListPanel(), cc.xy(1, 8));
196 panel = builder.getPanel();
197 panel.setBorder(Borders.DIALOG_BORDER);
198 return panel;
199 }
200
201
202
203
204 protected void doCloseWindow() {
205 super.doClose();
206 }
207
208
209
210
211
212
213 protected void doCompare(ActionEvent aEvt) {
214 LOG.debug(aEvt.getActionCommand());
215
216 final String command = aEvt.getActionCommand();
217 buttonCancel.setEnabled(true);
218 buttonDiff.setEnabled(false);
219 buttonUnion.setEnabled(false);
220 buttonIntersection.setEnabled(false);
221 buttonClose.setEnabled(false);
222 buttonSave.setEnabled(false);
223 GuiUtil.setBusyCursor(this, true);
224
225
226
227
228
229 worker = new Worker() {
230 public Object construct() {
231 return doWork(command);
232 }
233
234 public void finished() {
235 threadFinished(get());
236 }
237 };
238 worker.start();
239
240 }
241
242
243
244
245
246
247 protected void resizeHook(JComponent component) {
248
249 }
250
251
252
253
254
255
256 protected void save(ActionEvent aEvt) {
257 LOG.debug("Save pressed.");
258 JFileChooser chooser = new JFileChooser();
259 chooser.setDialogTitle("Save Report");
260
261 chooser.setFileFilter(FilterFactory.textFileFilter());
262 chooser.setMultiSelectionEnabled(false);
263 chooser.setFileHidingEnabled(true);
264 final int returnVal = chooser.showSaveDialog(this);
265 if (returnVal != JFileChooser.APPROVE_OPTION) {
266 return;
267 }
268 File file = chooser.getSelectedFile();
269 if (LOG.isDebugEnabled()) {
270 LOG.debug("Absolute: " + file.getAbsolutePath());
271 }
272
273
274 file = FilterFactory.forceTextExtension(file);
275
276 try {
277
278 ArrayList results = new ArrayList();
279 Enumeration enumeration = listModel.elements();
280 while (enumeration.hasMoreElements()) {
281 ValidationMessage message = (ValidationMessage)enumeration.nextElement();
282 results.add(message.formattedText());
283 }
284
285 FileUtils.writeLines(file, null, results);
286
287 MessageUtil.showInformation(this, "Report saved successfully.");
288 } catch (IOException ex) {
289 LOG.error("Error writing file: \n\n" + ex.getMessage(), ex);
290 } catch (InfrastructureException ex) {
291 LOG.error("Error writing file: \n\n" + ex.getMessage(), ex);
292 } catch (Exception ex) {
293 LOG.error("Unexpected error writing file.", ex);
294 }
295 }
296
297
298
299
300
301
302
303
304 private boolean isHashMatch(String aArtistName, String aDiscName) {
305 final String key = aArtistName + BREAK + aDiscName;
306 return mapDiscs.containsKey(key.toUpperCase(Locale.US));
307 }
308
309
310
311
312
313
314 private JComponent buildFilePanel() {
315 file = new JTextField();
316 ((JTextField)file).setColumns(50);
317 file.setText("");
318 final FormLayout layout = new FormLayout("right:max(14dlu;pref), 4dlu, left:min(60dlu;pref):grow, pref, 40dlu, ,pref, pref",
319 "p");
320 final PanelBuilder builder = new PanelBuilder(layout);
321 final CellConstraints cc = new CellConstraints();
322
323 builder.addLabel("Select File:", cc.xy(1, 1));
324 builder.add(file, cc.xyw(3, 1, 3));
325 builder.add(ComponentFactory.createFileChooserButton(file, "Select Catalog", FilterFactory.textFileFilter()),
326 cc.xy(6, 1));
327
328 return builder.getPanel();
329 }
330
331
332
333
334
335
336 private JComponent buildListPanel() {
337 listModel = new DefaultListModel();
338
339 list = new JList(listModel);
340 list.setFocusable(false);
341 list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
342 list.setSelectedIndex(0);
343 list.setCellRenderer(new MessageCellRenderer());
344 list.setVisibleRowCount(18);
345 return new JScrollPane(list);
346 }
347
348
349
350
351
352
353 private JComponent buildProgressPanel() {
354 progressBar = new JProgressBar();
355 progressBar.setIndeterminate(false);
356 progressBar.setStringPainted(true);
357 final FormLayout layout = new FormLayout("right:max(14dlu;pref), 4dlu, fill:pref:grow", "p");
358 final PanelBuilder builder = new PanelBuilder(layout);
359 final CellConstraints cc = new CellConstraints();
360
361 builder.addLabel("Progress:", cc.xy(1, 1));
362 builder.add(progressBar, cc.xy(3, 1));
363
364 return builder.getPanel();
365 }
366
367
368
369
370
371
372 private JComponent buildOptionsPanel() {
373 caseSensitive = new JCheckBox();
374 caseSensitive.setSelected(true);
375 final FormLayout layout = new FormLayout("right:max(14dlu;pref), 4dlu, fill:pref:grow", "p, 4px, p");
376
377 layout.setRowGroups(new int[][] {
378 { 1, 3 }
379 });
380 final PanelBuilder builder = new PanelBuilder(layout);
381 final CellConstraints cc = new CellConstraints();
382
383 builder.addLabel("Case Sensitive:", cc.xy(1, 1));
384 builder.add(caseSensitive, cc.xy(3, 1));
385
386 return builder.getPanel();
387 }
388
389
390
391
392
393
394
395 private void comparison(File aFile, String aCommand)
396 throws InterruptedException {
397
398
399 final Collection allDiscs = loadAllDiscs();
400 for (Iterator iter = allDiscs.iterator(); iter.hasNext();) {
401 final String key = (String)iter.next();
402 mapDiscs.put(key.toUpperCase(Locale.US), key);
403 }
404
405
406 Collection lines;
407 List unionList = new ArrayList();
408 try {
409 lines = FileUtils.readLines(aFile, null);
410
411 if (!caseSensitive.isSelected()) {
412 List ucList = new ArrayList();
413 final Iterator itLines = lines.iterator();
414 while (itLines.hasNext()) {
415 String element = (String) itLines.next();
416 ucList.add(element.toUpperCase());
417 }
418 lines = ucList;
419 }
420
421 int counter = 0;
422 resetProgressBar(lines.size());
423
424 for (Iterator iter = lines.iterator(); iter.hasNext();) {
425 counter++;
426 String line = (String)iter.next();
427 if (Thread.interrupted()) {
428 LOG.debug("Thread interrupted.");
429 throw new InterruptedException();
430 }
431 final String[] value = StringUtils.split(line, Resources.TAB);
432 final String artist = value[0];
433 final String disc = value[1];
434 final String discString = artist + BREAK + disc;
435 if ("Differences".equals(aCommand)) {
436 if (!isHashMatch(artist, disc)) {
437 final String output = "TAKE " + discString;
438 final String message = output;
439 final Severity severity = Severity.WARNING;
440 updateList(message, severity, counter);
441 }
442 } else if ("Intersection".equals(aCommand)) {
443 if (isHashMatch(artist, disc)) {
444 final String message = discString;
445 final Severity severity = Severity.OK;
446 updateList(message, severity, counter);
447 }
448 } else if ("Union".equals(aCommand)) {
449 unionList.add(discString);
450 } else {
451 throw new InterruptedException("Not a valid command button.");
452 }
453 }
454
455 progressBar.setValue(counter);
456
457
458 if ("Differences".equals(aCommand)) {
459
460 final Collection discs = loadAllDiscs();
461 counter = 0;
462 resetProgressBar(discs.size());
463 for (Iterator iter = discs.iterator(); iter.hasNext();) {
464 counter++;
465 final String disc = (String)iter.next();
466 String line = StringUtils.replace(disc, BREAK, Resources.TAB, 1);
467 if (!lines.contains(line)) {
468 final String message = "GIVE " + disc;
469 final Severity severity = Severity.OK;
470 updateList(message, severity, counter);
471 }
472 }
473 progressBar.setValue(counter);
474 }
475
476
477 if ("Union".equals(aCommand)) {
478 final Collection discs = loadAllDiscs();
479
480 for (Iterator iter = discs.iterator(); iter.hasNext();) {
481 String disc = (String)iter.next();
482 unionList.add(disc);
483 }
484 Collections.sort(unionList);
485 final SetUniqueList uniqueList = SetUniqueList.decorate(unionList);
486 counter = 0;
487 resetProgressBar(uniqueList.size());
488 for (Iterator iter = uniqueList.iterator(); iter.hasNext();) {
489 counter++;
490 String element = (String)iter.next();
491 final Severity severity = Severity.OK;
492 updateList(element, severity, counter);
493 }
494 progressBar.setValue(counter);
495 }
496
497 } catch (IOException ex) {
498 final String error = "Error reading file: " + ex.getMessage();
499 LOG.error(error);
500 throw new InterruptedException(error);
501 } catch (InfrastructureException ex) {
502 final String error = "Error querying database: " + ex.getMessage();
503 LOG.error(error);
504 throw new InterruptedException(error);
505 } catch (Exception ex) {
506 final String error = "Error reading file: " + ex.getMessage();
507 LOG.error(error);
508 throw new InterruptedException(error);
509 }
510 }
511
512
513
514
515
516 private Object doWork(String aCommand) {
517 Object result = null;
518
519 try {
520
521
522 listModel.removeAllElements();
523
524
525 String filename = this.file.getText();
526 File file = new File(filename);
527
528 if (!file.exists()) {
529 LOG.error("Please select a valid file");
530 throw new InterruptedException();
531 }
532
533
534 comparison(file, aCommand);
535
536 if (Thread.interrupted()) {
537 LOG.debug("Thread interrupted.");
538 throw new InterruptedException();
539 }
540
541 } catch (InterruptedException e) {
542 return result;
543 }
544 return result;
545 }
546
547
548
549
550
551
552
553 private Collection loadAllDiscs() {
554 ArrayList results = new ArrayList();
555 Collection discs = HibernateDao.findByQuery(Resources.getString("hql.export.catalog"));
556
557 for (Iterator iter = discs.iterator(); iter.hasNext();) {
558 Object[] queryResult = (Object[])iter.next();
559
560
561 String disc = queryResult[0] + BREAK + queryResult[1];
562 if (!caseSensitive.isSelected()) {
563 disc = disc.toUpperCase();
564 }
565 results.add(disc);
566 }
567 return results;
568 }
569
570
571
572
573
574
575 private void resetProgressBar(int aMaxProgress) {
576 progressBar.setMaximum(aMaxProgress);
577 progressBar.setValue(0);
578 progressBar.setIndeterminate(false);
579 }
580
581
582
583
584
585
586 private void threadFinished(Object result) {
587 if (LOG.isDebugEnabled()) {
588 LOG.debug("Thread Finished");
589 LOG.debug(result);
590 }
591 GuiUtil.setBusyCursor(this, false);
592 buttonCancel.setEnabled(false);
593 buttonDiff.setEnabled(true);
594 buttonUnion.setEnabled(true);
595 buttonIntersection.setEnabled(true);
596 buttonClose.setEnabled(true);
597 buttonSave.setEnabled(true);
598 }
599
600
601
602
603
604
605
606
607
608 private void updateList(final String aMessage, final Severity aSeverity, final int aProgress)
609 throws InterruptedException, InvocationTargetException {
610 Runnable updateList = new Runnable() {
611 public void run() {
612 progressBar.setValue(aProgress);
613 ValidationMessage message = new JukesValidationMessage(aMessage, aSeverity);
614 listModel.addElement(message);
615 int index = listModel.indexOf(message);
616 list.setSelectedIndex(index);
617 list.ensureIndexIsVisible(index);
618 }
619 };
620 EventQueue.invokeAndWait(updateList);
621 }
622
623 }