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