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.util.ArrayList;
10 import java.util.Collection;
11 import java.util.Enumeration;
12 import java.util.HashMap;
13 import java.util.Iterator;
14 import java.util.Map;
15
16 import javax.swing.AbstractAction;
17 import javax.swing.Action;
18 import javax.swing.DefaultListModel;
19 import javax.swing.JButton;
20 import javax.swing.JComponent;
21 import javax.swing.JFileChooser;
22 import javax.swing.JLabel;
23 import javax.swing.JList;
24 import javax.swing.JPanel;
25 import javax.swing.JScrollPane;
26 import javax.swing.JTextField;
27 import javax.swing.ListSelectionModel;
28 import javax.swing.text.JTextComponent;
29 import javax.swing.JCheckBox;
30
31 import org.apache.commons.io.FileUtils;
32 import org.apache.commons.io.filefilter.DirectoryFileFilter;
33 import org.apache.commons.lang.SystemUtils;
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36
37 import com.jgoodies.forms.builder.PanelBuilder;
38 import com.jgoodies.forms.factories.Borders;
39 import com.jgoodies.forms.factories.ButtonBarFactory;
40 import com.jgoodies.forms.layout.CellConstraints;
41 import com.jgoodies.forms.layout.FormLayout;
42 import com.jgoodies.uif.AbstractDialog;
43 import com.jgoodies.uif.action.ActionManager;
44 import com.jgoodies.uif.util.ResourceUtils;
45 import com.jgoodies.uif.util.Worker;
46 import com.jgoodies.uifextras.panel.HeaderPanel;
47 import com.jgoodies.validation.Severity;
48 import com.melloware.jukes.db.HibernateDao;
49 import com.melloware.jukes.db.HibernateUtil;
50 import com.melloware.jukes.exception.InfrastructureException;
51 import com.melloware.jukes.file.MusicDirectory;
52 import com.melloware.jukes.file.filter.FilterFactory;
53 import com.melloware.jukes.gui.tool.Actions;
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
73 @SuppressWarnings("unchecked")
74 public final class DiscFindDialog extends AbstractDialog {
75
76 private static final Log LOG = LogFactory.getLog(DiscFindDialog.class);
77 private static final String HQL_DISC_LOCATIONS = ResourceUtils.getString("hql.disc.locations");
78 private final Map mapDiscs = new HashMap();
79 private DefaultListModel listModel;
80 private JButton buttonSave;
81 private JButton buttonApply;
82 private JButton buttonCancel;
83 private JButton buttonClose;
84 private JComponent buttonBar;
85 private JList list;
86 private JPanel panel;
87 private JLabel currentDirectory;
88 private JTextComponent directory;
89 private final Settings settings;
90 private Worker worker;
91 private static boolean closeDialog = false;
92 private JCheckBox flagErrorsOnly;
93
94
95
96
97
98 public DiscFindDialog(Frame owner, Settings settings) {
99 super(owner);
100 LOG.debug("Disc Finder created.");
101 this.settings = settings;
102
103
104 HibernateUtil.setCompact(true);
105
106
107 loadAllDiscs();
108 }
109
110 public DiscFindDialog(Frame owner, Settings settings, boolean startFinder) {
111 super(owner);
112 LOG.debug("Disc Finder created.");
113 DiscFindDialog.closeDialog = startFinder;
114 this.settings = settings;
115
116
117 HibernateUtil.setCompact(true);
118
119
120 loadAllDiscs();
121
122 if (startFinder) {
123 this.build();
124 doApply();
125 }
126 }
127
128
129
130
131
132 public void doApply() {
133 LOG.debug("Apply pressed.");
134 buttonCancel.setEnabled(true);
135 buttonApply.setEnabled(false);
136 buttonClose.setEnabled(false);
137 buttonSave.setEnabled(false);
138 GuiUtil.setBusyCursor(this, true);
139 LOG.info("[START] Disc Finder");
140
141
142
143
144
145
146
147 worker = new Worker() {
148 public Object construct() {
149 return doWork(flagErrorsOnly.isSelected());
150 }
151
152 public void finished() {
153 if (closeDialog) {
154 ActionManager.get(Actions.REFRESH_ID).actionPerformed(null);
155 LOG.info("[CLOSE] Disc Finder");
156 dispose();
157 } else {
158 threadFinished(get());
159 }
160 }
161 };
162 worker.start();
163
164 }
165
166
167
168
169
170 public void doCancel() {
171 LOG.debug("Cancel Pressed.");
172 if (worker != null) {
173 worker.interrupt();
174 }
175 buttonCancel.setEnabled(false);
176 buttonApply.setEnabled(true);
177 buttonClose.setEnabled(true);
178 buttonSave.setEnabled(true);
179 }
180
181
182
183
184
185 protected JComponent buildContent() {
186 final JPanel content = new JPanel(new BorderLayout());
187 content.add(buildMainPanel(), BorderLayout.CENTER);
188 content.add(buttonBar, BorderLayout.SOUTH);
189 return content;
190 }
191
192
193
194
195
196 protected JComponent buildHeader() {
197 return new HeaderPanel(Resources.getString("label.discfinder"), Resources.getString("label.discfindermessage"),
198 Resources.DISC_FINDER_ICON);
199 }
200
201
202
203
204
205 protected JComponent buildMainPanel() {
206 final JButton[] buttons = new JButton[4];
207 final JButton button = createApplyButton();
208 button.setText(Resources.getString("label.Find"));
209 final Action export = new AbstractAction(Resources.getString("label.saveerrorreport"), Resources.FILE_TEXT_ICON) {
210
211 public void actionPerformed(ActionEvent evt) {
212 saveErrorReport(evt);
213 }
214 };
215 buttonSave = new JButton(export);
216 buttonCancel = createCancelButton();
217 buttonApply = button;
218 buttonClose = createCloseButton(true);
219 buttonClose.setText(Resources.getString("label.Close"));
220 buttonCancel.setEnabled(false);
221 buttonCancel.setText(Resources.getString("label.Cancel"));
222 buttons[0] = buttonApply;
223 buttons[1] = buttonCancel;
224 buttons[2] = buttonSave;
225 buttons[3] = buttonClose;
226 buttonBar = ButtonBarFactory.buildRightAlignedBar(buttons);
227 final FormLayout layout = new FormLayout("3px, pref, fill:pref:grow", "p, p, p, 4px, p, 4px, p");
228 final PanelBuilder builder = new PanelBuilder(layout);
229 builder.setDefaultDialogBorder();
230 final CellConstraints cc = new CellConstraints();
231 int row = 1;
232 builder.addSeparator(Resources.getString("label.Find"), cc.xyw(1, row++, 3));
233 builder.add(buildDirectoryPanel(), cc.xyw(1, row++, 3));
234 builder.add(buildListPanel(), cc.xyw(1, row, 3));
235
236 flagErrorsOnly = new JCheckBox(Resources.getString("label.errorsonly"), false);
237 builder.add(flagErrorsOnly, cc.xyw(2, 5, 2));
238 panel = builder.getPanel();
239 panel.setBorder(Borders.DIALOG_BORDER);
240 return panel;
241 }
242
243
244
245
246
247
248 protected void saveErrorReport(final ActionEvent aEvt) {
249 LOG.debug("Save Error Report pressed.");
250 final JFileChooser chooser = new JFileChooser();
251 chooser.setDialogTitle(Resources.getString("label.saveerrorreport"));
252
253 chooser.setFileFilter(FilterFactory.textFileFilter());
254 chooser.setMultiSelectionEnabled(false);
255 chooser.setFileHidingEnabled(true);
256 final int returnVal = chooser.showSaveDialog(this);
257 if (returnVal != JFileChooser.APPROVE_OPTION) {
258 return;
259 }
260 File file = chooser.getSelectedFile();
261 if (LOG.isDebugEnabled()) {
262 LOG.debug("Absolute: " + file.getAbsolutePath());
263 }
264
265
266 file = FilterFactory.forceTextExtension(file);
267
268 try {
269
270 final ArrayList results = new ArrayList();
271 final Enumeration enumeration = listModel.elements();
272 while (enumeration.hasMoreElements()) {
273 final JukesValidationMessage message = (JukesValidationMessage) enumeration.nextElement();
274 if ((message.severity() == Severity.ERROR) || (message.severity() == Severity.WARNING)) {
275
276 results.add("DIR: " + message.formattedText());
277
278 results.add(message.severity().toString().toUpperCase() + ": " + message.getToolTip());
279
280 results.add(" ");
281 }
282 }
283
284 FileUtils.writeLines(file, null, results);
285
286 MessageUtil.showInformation(this, Resources.getString("label.reportsaved"));
287 } catch (IOException ex) {
288 final String errorMessage = ResourceUtils.getString("label.Errorwritingfile") + "\n\n" + ex.getMessage();
289 MessageUtil.showError(this, errorMessage);
290 LOG.error(errorMessage, ex);
291 } catch (InfrastructureException ex) {
292 final String errorMessage = ResourceUtils.getString("label.Errorwritingfile") + "\n\n" + ex.getMessage();
293 MessageUtil.showError(this, errorMessage);
294 LOG.error(errorMessage, ex);
295 } catch (Exception ex) {
296 final String errorMessage = ResourceUtils.getString("label.Errorwritingfile");
297 MessageUtil.showError(this, errorMessage);
298 LOG.error(errorMessage, ex);
299 }
300 }
301
302
303
304
305
306 protected void doCloseWindow() {
307 super.doClose();
308 }
309
310
311
312
313
314
315 private JComponent buildDirectoryPanel() {
316 directory = new JTextField();
317 currentDirectory = new JLabel();
318 ((JTextField) directory).setColumns(50);
319 directory.setText(this.settings.getStartInDirectory().getAbsolutePath());
320 final FormLayout layout = new FormLayout(
321 "right:max(14dlu;pref), 4dlu, left:min(60dlu;pref):grow, pref, 40dlu, ,pref, pref", "p, 4px, p, 4px");
322
323
324
325
326
327 final PanelBuilder builder = new PanelBuilder(layout);
328 final CellConstraints cc = new CellConstraints();
329
330 builder.addLabel(Resources.getString("label.searchdirectory"), cc.xy(1, 1));
331 builder.add(directory, cc.xyw(3, 1, 3));
332 builder.add(ComponentFactory.createDirectoryChooserButton(directory), cc.xy(6, 1));
333 builder.addLabel(Resources.getString("label.Processing"), cc.xy(1, 3));
334 builder.add(currentDirectory, cc.xyw(3, 3, 3));
335
336 return builder.getPanel();
337 }
338
339
340
341
342
343
344 private JComponent buildListPanel() {
345 listModel = new DefaultListModel();
346
347 list = new JList(listModel);
348 list.setFocusable(false);
349 list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
350 list.setSelectedIndex(0);
351 list.setCellRenderer(new MessageCellRenderer());
352 list.setVisibleRowCount(19);
353 return new JScrollPane(list);
354 }
355
356
357
358
359
360 private Object doWork(boolean flagErrorsOnly) {
361 Object result = null;
362 final JukesValidationMessage message = new JukesValidationMessage("", Severity.OK);;
363 try {
364
365 listModel.removeAllElements();
366
367
368 String directory = this.directory.getText();
369 if (!directory.endsWith(SystemUtils.FILE_SEPARATOR)) {
370 directory = directory + SystemUtils.FILE_SEPARATOR;
371 }
372 final File dir = new File(directory);
373
374 if ((!dir.isDirectory()) || (!dir.exists())) {
375 final String errorMessage = ResourceUtils.getString("messages.SelectValidDirectory");
376 LOG.error(errorMessage);
377 MessageUtil.showError(this, errorMessage);
378 throw new InterruptedException();
379 }
380
381 if (this.settings.isCopyImagesToDirectory()) {
382 final String imagesDirectory = this.settings.getImagesLocation().getAbsolutePath();
383 final File imagesDir = new File(imagesDirectory);
384
385 if ((!imagesDir.isDirectory()) || (!imagesDir.exists())) {
386 final String errorMessage = ResourceUtils.getString("messages.SelectImageDirectory") + imagesDirectory + " " +
387 ResourceUtils.getString("messages.DoesntExist");
388 LOG.error(errorMessage);
389 MessageUtil.showError(this, errorMessage);
390 throw new InterruptedException();
391 }
392 }
393
394 recurseDirectories(dir, flagErrorsOnly);
395
396 if (Thread.interrupted()) {
397 LOG.debug("Thread interrupted.");
398 throw new InterruptedException();
399 }
400
401 } catch (InterruptedException e) {
402 return result;
403 }
404 message.setMessage(Resources.getString("label.allsubdirectoriesprocessed"));
405 updateList(message);
406 return result;
407
408 }
409
410
411
412
413
414
415
416
417 private boolean hasDiscAlready(final String aDirectory) {
418 return mapDiscs.containsKey(aDirectory);
419 }
420
421
422
423
424 private void loadAllDiscs() {
425 final Collection discs = HibernateDao.findByQuery(HQL_DISC_LOCATIONS);
426
427 for (final Iterator iter = discs.iterator(); iter.hasNext();) {
428 final Object queryResult = (Object) iter.next();
429 mapDiscs.put(queryResult, queryResult);
430 }
431 }
432
433
434
435
436
437
438
439
440
441 private void recurseDirectories(final File aDirectory, boolean flagErrorsOnly) throws InterruptedException {
442 final String[] files = aDirectory.list(DirectoryFileFilter.INSTANCE);
443 boolean hasSubDirectory = false;
444 if (Thread.interrupted()) {
445 LOG.debug("Thread interrupted.");
446 throw new InterruptedException();
447 }
448
449
450 if (files.length > 0) {
451 for (int i = 0; i < files.length; i++) {
452 final File f = new File(aDirectory, files[i]);
453
454 if (f.isDirectory() & (!f.isHidden())) {
455 hasSubDirectory = true;
456 recurseDirectories(f, flagErrorsOnly);
457 }
458 }
459 }
460
461 updateProcessing(aDirectory.getAbsolutePath());
462 if (hasDiscAlready(aDirectory.getAbsolutePath())) {
463 return;
464 }
465
466 final JukesValidationMessage message = MusicDirectory.loadDiscFromDirectory(aDirectory, false);
467
468 if (message.getSeverity() == Severity.OK) {
469 if (!flagErrorsOnly) {
470 message.setMessage(aDirectory.getAbsolutePath());
471 updateList(message);
472 }
473 } else if (message.getSeverity() == Severity.WARNING) {
474 if ((!flagErrorsOnly) & (!hasSubDirectory)) {
475 message.setMessage(aDirectory.getAbsolutePath());
476 updateList(message);
477 }
478 } else {
479 message.setMessage("ERROR " + aDirectory.getAbsolutePath());
480 updateList(message);
481 }
482 hasSubDirectory = false;
483 }
484
485
486
487
488
489
490 private void threadFinished(Object result) {
491 if (LOG.isDebugEnabled()) {
492 LOG.debug("Thread Finished");
493 LOG.debug(result);
494 }
495 GuiUtil.setBusyCursor(this, false);
496 buttonCancel.setEnabled(false);
497 buttonApply.setEnabled(true);
498 buttonClose.setEnabled(true);
499 buttonSave.setEnabled(true);
500
501
502 ActionManager.get(Actions.REFRESH_ID).actionPerformed(null);
503 LOG.info("[STOP] Disc Finder");
504 }
505
506
507
508
509
510
511 private void updateList(final JukesValidationMessage aMessage) {
512 final Runnable updateList = new Runnable() {
513 public void run() {
514 listModel.addElement(aMessage);
515 final int index = listModel.indexOf(aMessage);
516 list.setSelectedIndex(index);
517 list.ensureIndexIsVisible(index);
518 }
519 };
520 EventQueue.invokeLater(updateList);
521 }
522
523
524
525
526
527
528 private void updateProcessing(final String aDirectory) {
529 final Runnable updateList = new Runnable() {
530 public void run() {
531 currentDirectory.setText(aDirectory);
532 }
533 };
534 EventQueue.invokeLater(updateList);
535 }
536
537 }