View Javadoc

1   package com.melloware.jukes.gui.view.dialogs;
2   
3   import java.awt.BorderLayout;
4   import java.awt.Dimension;
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.Collection;
10  import java.util.Iterator;
11  import java.util.List;
12  import java.util.ArrayList;
13  
14  import javax.swing.AbstractAction;
15  import javax.swing.Action;
16  import javax.swing.JButton;
17  import javax.swing.JComponent;
18  import javax.swing.JFileChooser;
19  import javax.swing.JPanel;
20  import javax.swing.JPopupMenu;
21  import javax.swing.JTable;
22  import javax.swing.JProgressBar;
23  import javax.swing.ListSelectionModel;
24  import javax.swing.RowSorter;
25  import javax.swing.event.ListSelectionEvent;
26  import javax.swing.event.ListSelectionListener;
27  import javax.swing.table.TableModel;
28  import javax.swing.table.TableRowSorter;
29  
30  import org.apache.commons.io.FileUtils;
31  import org.apache.commons.lang.StringUtils;
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  
35  import com.jgoodies.forms.builder.PanelBuilder;
36  import com.jgoodies.forms.factories.ButtonBarFactory;
37  import com.jgoodies.forms.layout.CellConstraints;
38  import com.jgoodies.forms.layout.FormLayout;
39  import com.jgoodies.uif.AbstractDialog;
40  import com.jgoodies.uif.application.Application;
41  import com.jgoodies.uif.util.Resizer;
42  import com.jgoodies.uif.util.ResourceUtils;
43  import com.jgoodies.uif.util.Worker;
44  import com.jgoodies.uifextras.panel.HeaderPanel;
45  import com.jgoodies.uifextras.util.UIFactory;
46  import com.melloware.jukes.db.HibernateDao;
47  import com.melloware.jukes.db.orm.Disc;
48  import com.melloware.jukes.exception.InfrastructureException;
49  import com.melloware.jukes.file.Disclist;
50  import com.melloware.jukes.file.filter.FilterFactory;
51  import com.melloware.jukes.file.tag.MusicTag;
52  import com.melloware.jukes.gui.tool.MainModule;
53  import com.melloware.jukes.gui.tool.Resources;
54  import com.melloware.jukes.gui.tool.Settings;
55  import com.melloware.jukes.gui.view.MainFrame;
56  import com.melloware.jukes.gui.view.MainMenuBuilder;
57  import com.melloware.jukes.gui.view.component.EnhancedTableHeader;
58  import com.melloware.jukes.gui.view.component.ExportableTable;
59  import com.melloware.jukes.util.GuiUtil;
60  import com.melloware.jukes.util.MessageUtil;
61  
62  /**
63   * Searches the database by criteria and returns results in Disc format.
64   * <p>
65   * Copyright (c) 1999-2007 Melloware, Inc. <http://www.melloware.com>
66   * @author Emil A. Lefkof III <info@melloware.com>
67   * @version 4.0
68   * AZ 2009, 2010
69   */
70  public final class GenresToolDialog
71      extends AbstractDialog {
72  
73      private static final Log LOG = LogFactory.getLog(SearchDialog.class);
74      private final EnhancedTableHeader header;
75      private final ExportableTable resultsTable;
76      private JButton buttonCancel;
77      private JButton buttonSearch;
78      private JButton buttonSelect;
79      private JButton buttonStop;
80      private JButton buttonSave;
81      private JButton buttonAddToDiscList;
82      private JComponent buttonBar;
83      private JProgressBar progressBar;
84      private Object[] results;
85      private DiscTableModel tableModel;
86      private final Settings settings;
87      private RowSorter<TableModel> sorter;
88      private Disc selection;
89      private Worker worker;
90      private JPopupMenu addDiscMenu;
91  
92      /**
93       * Constructs a default about dialog using the given owner.
94       *
95       * @param owner   the dialog's owner
96       */
97      public GenresToolDialog(Frame owner, Settings settings) {
98          super(owner);
99          LOG.debug("Genre Tool Dialog created.");
100         this.settings = settings;
101         this.settings.getDatabaseLocation();
102 
103         resultsTable = new ExportableTable();
104         resultsTable.setEvenColor(this.settings.getRowColorEven());
105         resultsTable.setOddColor(this.settings.getRowColorOdd());
106         header = new EnhancedTableHeader(resultsTable.getColumnModel(), resultsTable);
107         resultsTable.setTableHeader(header);
108         addDiscMenu = MainMenuBuilder.buildDiscExportableTablePopupMenu(resultsTable);
109         resultsTable.addPopupMenu(addDiscMenu);
110 
111         this.setPreferredSize(new Dimension(800, 576));
112 
113     }
114 
115     /**
116      * Gets the selection.
117      * <p>
118      * @return Returns the selection.
119      */
120     public Disc getSelection() {
121         return this.selection;
122     }
123 
124     /* (non-Javadoc)
125      * @see com.jgoodies.swing.AbstractDialog#doApply()
126      */
127     public void doAccept() {
128         LOG.debug("Select pressed.");
129         super.doAccept();
130         final MainFrame mainFrame = (MainFrame)Application.getDefaultParentFrame();
131         String query = " and upper(disc.name) = '" + selection.getName().toUpperCase() + "'";
132         String filter = MainModule.SETTINGS.getFilter();
133         
134         if (!(this.settings.isShowDefaultTree() && !StringUtils.isNotBlank(filter))) {
135         	filter  = query;
136         }
137         MainModule.SETTINGS.setFilter(filter);
138         mainFrame.getMainModule().refreshTree();
139         mainFrame.getMainModule().selectNodeInTree(selection);
140     }
141 
142     /* (non-Javadoc)
143      * @see com.jgoodies.swing.AbstractDialog#doCancel()
144      */
145     public void doCancel() {
146         LOG.debug("Cancel Pressed.");
147         super.doCancel();
148     }
149 
150     /**
151      * Builds and answers the dialog's content.
152      *
153      * @return the dialog's content with table pane and button bar
154      */
155     protected JComponent buildContent() {
156         JPanel content = new JPanel(new BorderLayout());
157         JButton[] buttons = new JButton[6];
158         JButton button = createAcceptButton(Resources.getString("label.Select"), false);
159         button.setEnabled(false);
160         buttonSelect = button;
161         buttonCancel = createCancelButton();
162         buttonCancel.setText(Resources.getString("label.Close")); //AZ
163         
164         // Create an action with an icon
165         final Action export = new AbstractAction(Resources.getString("label.SaveReport"), Resources.FILE_TEXT_ICON) {
166             // This method is called when the button is pressed
167             public void actionPerformed(ActionEvent evt) {
168                saveReport(evt);
169             }
170          };
171          buttonSave = new JButton(export);
172          
173         // Create an action with an icon
174         Action search = new AbstractAction(Resources.getString("label.StartCheck"), Resources.THREAD_START_ICON) {
175             public void actionPerformed(ActionEvent evt) {
176                 doSearch();
177             }
178         };
179         buttonSearch = new JButton(search);
180         
181         // Create an action with an icon
182         Action stop = new AbstractAction(Resources.getString("label.Cancel"), Resources.THREAD_STOP_ICON) {
183             public void actionPerformed(ActionEvent evt) {
184                 doStop();
185             }
186         };
187         buttonStop = new JButton(stop);
188         buttonStop.setEnabled(false);
189         
190         // Create an action with an icon
191         Action adddisclist = new AbstractAction(Resources.getString("label.AddToDiscList"), ResourceUtils.getIcon("disc.queue.icon")) {
192             public void actionPerformed(ActionEvent evt) {
193                 doAddToDiscList();
194             }
195         };
196         buttonAddToDiscList = new JButton(adddisclist);
197 
198 
199         buttons[0] = buttonSearch;
200         buttons[1] = buttonStop;
201         buttons[2] = buttonSelect;
202         buttons[3] = buttonSave;     
203         buttons[4] = buttonAddToDiscList;
204         buttons[5] = buttonCancel;
205         buttonBar = ButtonBarFactory.buildCenteredBar(buttons);
206 
207         content.add(buildMainPanel(), BorderLayout.CENTER);
208         content.add(buttonBar, BorderLayout.SOUTH);
209         return content;
210     }
211 
212     /**
213      * Builds and returns the dialog's header.
214      *
215      * @return the dialog's header component
216      */
217     protected JComponent buildHeader() {
218         final HeaderPanel header = new HeaderPanel(Resources.getString("label.GenresCheck"),
219         		                                   Resources.getString("label.GenresCheckText"),
220         		                                   Resources.GENRES_TOOL_ICON);
221 
222         return header;
223     }
224 
225     /**
226      * Builds and returns the dialog's pane.
227      *
228      * @return the dialog's  pane component
229      */
230     protected JComponent buildMainPanel() {
231         FormLayout layout = new FormLayout("fill:pref:grow", "p, 4px, p, 4px");
232         PanelBuilder builder = new PanelBuilder(layout);
233         builder.setDefaultDialogBorder();
234         CellConstraints cc = new CellConstraints();
235         builder.add(buildProgressPanel(), cc.xy(1, 1));
236         builder.add(buildResultsTablePanel(), cc.xy(1, 3));
237         return builder.getPanel();
238     }
239 
240     /**
241     * Executes the search.
242     */
243     protected void doSearch() {
244         LOG.debug("Searching...");
245         
246         GuiUtil.setBusyCursor(this, true);
247         buttonSearch.setEnabled(false);
248         buttonCancel.setEnabled(false);
249         buttonSelect.setEnabled(false);
250         buttonSave.setEnabled(false);
251         buttonAddToDiscList.setEnabled(false);
252         buttonStop.setEnabled(true);
253 
254         /* Invoking start() on the SwingWorker causes a new Thread
255          * to be created that will call construct(), and then finished().  Note that finished() is called even if the
256          * worker is interrupted because we catch the InterruptedException in doWork().
257          */
258         worker = new Worker() {
259                 public Object construct() {
260                     return doWork();
261                 }
262 
263                 public void finished() {
264                     threadFinished(get());
265                 }
266             };
267         worker.start();
268     }
269 
270     /**
271      * Stops the search.
272      */
273     protected void doStop() {
274         LOG.debug("Stopping...");
275         GuiUtil.setBusyCursor(this, false);
276         worker.interrupt();
277         buttonSearch.setEnabled(true);
278         buttonCancel.setEnabled(true);
279         buttonSave.setEnabled(true);
280         buttonAddToDiscList.setEnabled(true);
281         buttonStop.setEnabled(false);
282     }
283 
284     /**
285      * Resizes the given component to give it a quadratic aspect ratio.
286      *
287      * @param component   the component to be resized
288      */
289     protected void resizeHook(JComponent component) {
290         Resizer.ONE2ONE.resizeDialogContent(component);
291     }
292 
293     /**
294     * When the thread is finished this method is called.
295     * <p>
296     * @param result the Object return from the doWork thread.
297     */
298     protected void threadFinished(Object result) {
299         if (LOG.isDebugEnabled()) {
300             LOG.debug("Thread Finished");
301         }
302         buttonSearch.setEnabled(true);
303         buttonCancel.setEnabled(true);
304         buttonSave.setEnabled(true);
305         buttonAddToDiscList.setEnabled(true);
306         buttonStop.setEnabled(false);
307         GuiUtil.setBusyCursor(this, false);
308 
309         if (result == null) {
310             results = null;
311             tableModel = new DiscTableModel(results);
312             tableModel.setData(null);
313             tableModel.fireTableDataChanged();
314         } else {
315             final Collection items = (Collection)result;
316             results = items.toArray();
317             tableModel = new DiscTableModel(results);
318             sorter = new TableRowSorter<TableModel>(tableModel);
319             resultsTable.setModel(tableModel);
320             resultsTable.setRowSorter(sorter);
321             header.autoSizeColumns();
322         }
323 
324         resultsTable.updateUI();
325 
326     }
327 
328     /**
329      * Builds the panel with the JTable tags in it.
330      * <p>
331      * @return the panel used to display messages
332      */
333     private JComponent buildResultsTablePanel() {
334 
335         // build the table and model
336         resultsTable.setShowGrid(false);
337         resultsTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
338         resultsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
339         // Ask to be notified of selection changes.
340         final ListSelectionModel rowSM = resultsTable.getSelectionModel();
341         rowSM.addListSelectionListener(new ListSelectionListener() {
342                 public void valueChanged(ListSelectionEvent e) {
343                     // Ignore extra messages.
344                     if (e.getValueIsAdjusting()) {
345                         return;
346                     }
347 
348                     final ListSelectionModel lsm = (ListSelectionModel)e.getSource();
349                     if (lsm.isSelectionEmpty()) {
350                         selection = null;
351                         buttonSelect.setEnabled(false);
352                         
353                     } else {
354                         int selectedRow = resultsTable.getSelectedRow();
355                         selectedRow = sorter.convertRowIndexToModel(selectedRow);
356                         selection = (Disc)results[selectedRow];
357                         buttonSelect.setEnabled(true);
358 
359                     }
360                 }
361             });
362 
363         final JComponent resultsPane = UIFactory.createTablePanel(resultsTable);
364         resultsPane.setPreferredSize(new Dimension(300, 350));
365 
366         // build the form
367         final FormLayout layout = new FormLayout("fill:pref:grow", "p");
368         final PanelBuilder builder = new PanelBuilder(layout);
369         final CellConstraints cc = new CellConstraints();
370         builder.add(resultsPane, cc.xy(1, 1));
371         return builder.getPanel();
372     }
373 
374     /**
375     * This method represents the application code that we'd like to
376     * run on a separate thread.
377     */
378     private Object doWork() {
379         ArrayList<Disc> selectedDiscs = new ArrayList<Disc>();
380         boolean Stop = false;
381         try {
382             // do the search
383             try {              
384                 // get a list of all discs in the system
385                 //final Collection discs = HibernateDao.findAll(Disc.class, Disc.PROPERTYNAME_NAME);
386             	//final String hql = "select distinct disc from Disc as disc inner join disc.artist as artist order by upper(artist.name)";
387             	final String hql = "select disc from Disc as disc inner join disc.artist as artist order by upper(artist.name)";
388             	final Collection discs = HibernateDao.findByQuery(hql);
389                 if (LOG.isDebugEnabled()) {
390                     LOG.debug("Disc Count = " + discs.size());
391                 }
392                 int count = 0;
393                 // set the progress-bar bounds
394                 progressBar.setMaximum(discs.size());
395                 progressBar.setValue(0);
396                 progressBar.setIndeterminate(false);
397                 for (final Iterator iter = discs.iterator(); ((iter.hasNext())&&(!Stop));) {
398                     final Disc disc = (Disc)iter.next();
399                     count++;
400                     final boolean success = checkGenre(disc.getGenre());
401                     progressBar.setValue(count);
402                     if (!success) {
403                       //AZ: Process Notes
404                       if (!(disc.getNotes()==null)) {
405                     	//AZ: Replace CR and LF with SPACE
406                     	//AZ: Trim notes
407                     	int charLength = disc.getNotes().length();
408                     	if (charLength > 30) {
409                     		charLength=30;
410                     	};
411                     	
412                     	if (charLength > 0) {
413                     	String noteStr = disc.getNotes().substring(0,charLength-1);
414                     	String noteStrNew = "";
415                     	for ( int i=0; i<charLength-1; i++ ) {
416                     		if (((int)noteStr.charAt(i)==10)) {
417                     			noteStrNew= noteStrNew + " ";
418                     		}
419                     		else if (((int)noteStr.charAt(i)==13)) {
420                     			noteStrNew= noteStrNew + " ";
421                     		}
422                     		else {
423                     			noteStrNew= noteStrNew + noteStr.charAt(i);
424                     		}
425                     	}
426                     	disc.setNotes(noteStrNew);
427                     	}
428                     	LOG.info("Erroneous genre found: " + disc.getArtist().getName() + " - " + disc.getName() + " : " + disc.getGenre());//AZ
429                     	selectedDiscs.add(disc); 
430                       }
431                     }
432                     if (Thread.interrupted()) {
433                     	Stop = true;
434                     }
435                 }
436                 
437             } catch (RuntimeException ex) {
438 
439                 final String message = "Unexpected error occured performing search.";
440                 LOG.debug(message, ex);
441                 throw new InterruptedException(message);   
442             }
443 
444             if (Thread.interrupted()) {
445                 LOG.debug("Thread interrupted.");
446                 throw new InterruptedException();
447             }
448 
449         } catch (InterruptedException e) {
450             return selectedDiscs;    // SwingWorker.get() returns this
451         }
452         return selectedDiscs;    // or this
453     }
454     
455     /**
456      * AZ 2009
457      * Try to find specified genre in the standard table of genres  
458      */     
459     private boolean checkGenre(String genre){
460     	boolean isGenre = false;
461     	final List GENRE_LIST = MusicTag.getGenreTypes();
462         String BasicGenre = genre;
463         String[] separatorList = {":", ";", ".", "-", ",", "/"};
464         int index = genre.length();
465         if (genre.equalsIgnoreCase("<none>")) {
466         	return isGenre;	
467         }
468         for(int i=0; i<separatorList.length; i++){
469         	final int currentIndex = genre.indexOf(separatorList[i]);
470         	if ((currentIndex > 0) & (currentIndex < index) ){
471             	index = currentIndex;
472             }
473         }
474     	BasicGenre = genre.substring(0, index);
475         if (containsIgnoreCase(GENRE_LIST, BasicGenre)) {
476         	isGenre = true;
477         }
478         return isGenre; 
479     }   
480     /**
481      * Return True if String s is a member of List l
482      */
483     public boolean containsIgnoreCase(List <String> l, String s){	
484    	 Iterator<String> it = l.iterator();
485    	 while(it.hasNext()){ 		 
486    	  if(it.next().compareToIgnoreCase(s)==0) {
487    	  return true;
488    	  }
489    	 }
490    	 return false;
491    	}
492     /**
493      * Builds the progress-bar panel.
494      * <p>
495      * @return the panel
496      */
497     private JComponent buildProgressPanel() {
498         progressBar = new JProgressBar();
499         progressBar.setIndeterminate(false);
500         progressBar.setStringPainted(true);
501         final FormLayout layout = new FormLayout("right:max(14dlu;pref), 4dlu, p, fill:pref:grow",
502                                            "p, 4px, p");   
503         final PanelBuilder builder = new PanelBuilder(layout);
504         final CellConstraints cc = new CellConstraints();
505 
506         builder.addLabel(Resources.getString("label.Progress"), cc.xy(1, 1));
507         builder.add(progressBar, cc.xyw(3, 1, 2));
508 
509         return builder.getPanel();
510     }
511     
512     /**AZ
513      * Saves the found genres errors report to a TXT file.
514      * <p>
515      * @param aEvt the event fired
516      */
517     protected void saveReport(final ActionEvent aEvt) {
518        LOG.debug("Save Found Genres Errors Report pressed.");
519        final JFileChooser chooser = new JFileChooser();
520        chooser.setDialogTitle(Resources.getString("label.SaveReport"));
521        String messageString;
522 
523        chooser.setFileFilter(FilterFactory.textFileFilter());
524        chooser.setMultiSelectionEnabled(false);
525        chooser.setFileHidingEnabled(true);
526        final int returnVal = chooser.showSaveDialog(this);
527        if (returnVal != JFileChooser.APPROVE_OPTION) {
528           return;
529        }
530        File file = chooser.getSelectedFile();
531        if (LOG.isDebugEnabled()) {
532           LOG.debug("Absolute: " + file.getAbsolutePath());
533        }
534 
535        // add the extension if missing
536        file = FilterFactory.forceTextExtension(file);
537 
538        try {
539           // now print errors and warns to the file
540           final ArrayList results = new ArrayList();
541           final int col = resultsTable.getColumnCount();
542           for(int i=0; i<resultsTable.getRowCount(); i++){	
543         	    messageString = i+Character.toString((char)9);
544                 for (int ii=0; ii<col; ii++) {
545                 	messageString = messageString + resultsTable.getValueAt(i, ii).toString() + Character.toString((char)9);
546                 }
547                 results.add(messageString);     
548            }
549 
550           FileUtils.writeLines(file, null, results);
551 
552           MessageUtil.showInformation(this, Resources.getString("label.reportsaved"));
553        } catch (IOException ex) {
554     	  final String errorMessage = ResourceUtils.getString("label.Errorwritingfile"); 
555           MessageUtil.showError(this, errorMessage); //AZ 
556           LOG.error(errorMessage + "\n\n" + ex.getMessage(), ex);
557        } catch (InfrastructureException ex) {
558      	  final String errorMessage = ResourceUtils.getString("label.Errorwritingfile"); 
559           MessageUtil.showError(this, errorMessage); //AZ 
560           LOG.error(errorMessage + "\n\n" + ex.getMessage(), ex);
561        } catch (Exception ex) {
562      	  final String errorMessage = ResourceUtils.getString("label.Errorwritingfile"); 
563           MessageUtil.showError(this, errorMessage); //AZ 
564           LOG.error(errorMessage, ex);
565        }
566     }
567 
568     /**
569      * Add all found discs to discList.
570      */
571     protected void doAddToDiscList() {
572       final MainFrame mainFrame = (MainFrame) Application.getDefaultParentFrame();
573       final Disclist disclist = mainFrame.getDisclist();
574       LOG.debug("Adding found discs to discList");
575       
576       GuiUtil.setBusyCursor(this, true);
577       for (int i = 0; i < this.results.length; i++) {
578            disclist.add(tableModel.getData()[i]);
579       }  
580       GuiUtil.setBusyCursor(this, false);
581     }
582 
583 }