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.FileInputStream;
9   import java.io.IOException;
10  import java.io.InputStream;
11  import java.io.InputStreamReader;
12  import java.io.Reader;
13  import java.text.DateFormat;
14  import java.text.MessageFormat;
15  import java.text.ParseException;
16  import java.text.SimpleDateFormat;
17  import java.util.ArrayList;
18  import java.util.Date;
19  import java.util.Iterator;
20  
21  import javax.swing.AbstractAction;
22  import javax.swing.Action;
23  import javax.swing.JButton;
24  import javax.swing.JComponent;
25  import javax.swing.JPanel;
26  import javax.swing.JProgressBar;
27  import javax.xml.parsers.SAXParser;
28  import javax.xml.parsers.SAXParserFactory;
29  
30  import org.apache.commons.lang.StringEscapeUtils;
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.hibernate.Session;
34  import org.xml.sax.Attributes;
35  import org.xml.sax.InputSource;
36  import org.xml.sax.SAXException;
37  import org.xml.sax.helpers.DefaultHandler;
38  
39  import com.jgoodies.forms.builder.PanelBuilder;
40  import com.jgoodies.forms.factories.ButtonBarFactory;
41  import com.jgoodies.forms.layout.CellConstraints;
42  import com.jgoodies.forms.layout.FormLayout;
43  import com.jgoodies.uif.AbstractDialog;
44  import com.jgoodies.uif.action.ActionManager;
45  import com.jgoodies.uif.util.Resizer;
46  import com.jgoodies.uif.util.ResourceUtils;
47  import com.jgoodies.uif.util.Worker;
48  import com.jgoodies.uifextras.panel.HeaderPanel;
49  import com.melloware.jukes.db.HibernateDao;
50  import com.melloware.jukes.db.HibernateUtil;
51  import com.melloware.jukes.db.orm.Artist;
52  import com.melloware.jukes.db.orm.Disc;
53  import com.melloware.jukes.db.orm.Track;
54  import com.melloware.jukes.gui.tool.Actions;
55  import com.melloware.jukes.gui.tool.Resources;
56  import com.melloware.jukes.gui.tool.Settings;
57  import com.melloware.jukes.util.GuiUtil;
58  import com.melloware.jukes.util.MessageUtil;
59  
60  /**
61   * Import database objects from XML-file
62   * <p>
63   * Copyright (c) 1999-2007 Melloware, Inc. <http://www.melloware.com>
64   * @author Emil A. Lefkof III <info@melloware.com>
65   * @version 4.0 AZ 2010
66   */
67  public final class XMLImportDialog extends AbstractDialog {
68  
69     private static final Log LOG = LogFactory.getLog(SearchDialog.class);
70     private JButton buttonCancel;
71     private JButton buttonSearch;
72     private JButton buttonStop;
73     private JComponent buttonBar;
74     private JProgressBar progressBar;
75     private final Settings settings;
76     private Worker worker;
77     private final File file;
78     private int count = 0;
79     private int artistCount = 0;
80     private int discCount = 0;
81     private int progressCount = 0;
82     private Artist artist;
83     private Disc disc;
84     private Track track;
85     final Session s = HibernateUtil.getSession();
86  
87     /**
88      * Constructs XMLImport dialog using the given owner.
89      * @param owner the dialog's owner
90      */
91     public XMLImportDialog(Frame owner, Settings settings, File file) {
92        super(owner);
93        LOG.debug("XMLImport Dialog created.");
94        this.settings = settings;
95        this.settings.getDatabaseLocation();
96        this.file = file;
97  
98        this.setPreferredSize(new Dimension(700, 300));
99     }
100 
101    /*
102     * (non-Javadoc)
103     * @see com.jgoodies.swing.AbstractDialog#doCancel()
104     */
105    @Override
106    public void doCancel() {
107       LOG.debug("Cancel Pressed.");
108       super.doCancel();
109    }
110 
111    /**
112     * Builds and answers the dialog's content.
113     * @return the dialog's content with table pane and button bar
114     */
115    @Override
116    protected JComponent buildContent() {
117       JPanel content = new JPanel(new BorderLayout());
118       JButton[] buttons = new JButton[3];
119       // Create Cancel button
120       buttonCancel = createCancelButton();
121       buttonCancel.setText(Resources.getString("label.Close"));
122 
123       // Create an action with an icon
124       Action search = new AbstractAction(Resources.getString("label.Start"), Resources.THREAD_START_ICON) {
125          public void actionPerformed(ActionEvent evt) {
126             doSearch();
127          }
128       };
129       buttonSearch = new JButton(search);
130 
131       // Create an action with an icon
132       Action stop = new AbstractAction(Resources.getString("label.Cancel"), Resources.THREAD_STOP_ICON) {
133          public void actionPerformed(ActionEvent evt) {
134             doStop();
135          }
136       };
137       buttonStop = new JButton(stop);
138       buttonStop.setEnabled(false);
139 
140       buttons[0] = buttonSearch;
141       buttons[1] = buttonStop;
142       buttons[2] = buttonCancel;
143       buttonBar = ButtonBarFactory.buildCenteredBar(buttons);
144 
145       content.add(buildMainPanel(), BorderLayout.CENTER);
146       content.add(buttonBar, BorderLayout.SOUTH);
147       return content;
148    }
149 
150    /**
151     * Builds and returns the dialog's header.
152     * @return the dialog's header component
153     */
154    @Override
155    protected JComponent buildHeader() {
156       final HeaderPanel header = new HeaderPanel(Resources.getString("label.XMLimport"), Resources
157                .getString("label.XMLimportText"), Resources.XML_EXPORT_ICON);
158 
159       return header;
160    }
161 
162    /**
163     * Builds and returns the dialog's pane.
164     * @return the dialog's pane component
165     */
166    protected JComponent buildMainPanel() {
167       FormLayout layout = new FormLayout("fill:pref:grow", "p, 4px, p, 4px");
168       PanelBuilder builder = new PanelBuilder(layout);
169       builder.setDefaultDialogBorder();
170       CellConstraints cc = new CellConstraints();
171       builder.add(buildProgressPanel(), cc.xy(1, 1));
172       return builder.getPanel();
173    }
174 
175    /**
176     * Builds the progress-bar panel.
177     * <p>
178     * @return the panel
179     */
180    private JComponent buildProgressPanel() {
181       progressBar = new JProgressBar();
182       progressBar.setIndeterminate(false);
183       progressBar.setStringPainted(true);
184       progressBar.setString(" ");
185 
186       final FormLayout layout = new FormLayout("right:max(14dlu;pref), 4dlu, p, fill:pref:grow", "p, 4px, p");
187       final PanelBuilder builder = new PanelBuilder(layout);
188       final CellConstraints cc = new CellConstraints();
189 
190       builder.addLabel(Resources.getString("label.Progress"), cc.xy(1, 1));
191       builder.add(progressBar, cc.xyw(3, 1, 2));
192 
193       return builder.getPanel();
194    }
195 
196    /**
197     * Executes the search.
198     */
199    protected void doSearch() {
200       LOG.debug("Importing...");
201 
202       // GuiUtil.setBusyCursor(this, true);
203       buttonSearch.setEnabled(false);
204       buttonCancel.setEnabled(false);
205       buttonStop.setEnabled(true);
206 
207       /*
208        * Invoking start() on the SwingWorker causes a new Thread to be created
209        * that will call construct(), and then finished(). Note that finished()
210        * is called even if the worker is interrupted because we catch the
211        * InterruptedException in doWork().
212        */
213       worker = new Worker() {
214          @Override
215          public Object construct() {
216             doWork();
217             return null;
218          }
219 
220          @Override
221          public void finished() {
222             threadFinished(get());
223          }
224       };
225       worker.start();
226    }
227 
228    /**
229     * Stops the search.
230     */
231    protected void doStop() {
232       GuiUtil.setBusyCursor(this, false);
233       worker.interrupt();
234       buttonSearch.setEnabled(true);
235       buttonCancel.setEnabled(true);
236       buttonStop.setEnabled(false);
237    }
238 
239    /**
240     * Resizes the given component to give it a quadratic aspect ratio.
241     * @param component the component to be resized
242     */
243    @Override
244    protected void resizeHook(JComponent component) {
245       Resizer.ONE2ONE.resizeDialogContent(component);
246    }
247 
248    /**
249     * When the thread is finished this method is called.
250     * <p>
251     * @param result the Object return from the doWork thread.
252     */
253    protected void threadFinished(Object result) {
254       if (LOG.isDebugEnabled()) {
255          LOG.debug("Thread Finished");
256       }
257       buttonSearch.setEnabled(true);
258       buttonCancel.setEnabled(true);
259       buttonStop.setEnabled(false);
260       GuiUtil.setBusyCursor(this, false);
261 
262       // refresh the tree
263       ActionManager.get(Actions.REFRESH_ID).actionPerformed(null);
264       LOG.info("XML Import: Finished");
265       LOG.info("XML Import: Total Discs Processed " + count);
266       LOG.info("XML Import: Total New Artists Imported " + artistCount);
267       LOG.info("XML Import: Total New Discs Imported " + discCount);
268    }
269 
270    /**
271     * This method represents the application code that we'd like to run on a
272     * separate thread.
273     */
274    private void doWork() {
275       final String LINE_BREAK = "\n";
276       final String ERROR_READING_FILE = Resources.getString("label.Errorreadingfile");
277       artistCount = 0;
278       discCount = 0;
279       count = 0;
280       progressCount = 0;
281 
282       LOG.info("XML Import: Starting");
283       // Set Progress Bar
284       progressBar.setMaximum(100);
285       progressBar.setValue(0);
286       try {
287          // open file for import, create XML-parser
288          SAXParserFactory factory = SAXParserFactory.newInstance();
289          SAXParser saxParser = factory.newSAXParser();
290 
291          InputStream inputStream = new FileInputStream(file);
292          Reader reader = new InputStreamReader(inputStream, "UTF-8");
293 
294          InputSource inputSource = new InputSource(reader);
295          inputSource.setEncoding("UTF-8");
296 
297          DefaultHandler handler = XMLHandler();
298          saxParser.parse(inputSource, handler);
299          progressBar.setValue(100);
300       } catch (IOException ex) {
301          final String message = ERROR_READING_FILE + LINE_BREAK + ex.getMessage(); // AZ
302          LOG.error(ERROR_READING_FILE + LINE_BREAK + ex.getMessage(), ex);
303          MessageUtil.showError(null, message);
304       } catch (SAXException ex) {
305          final String message = "SAXException: " + ex.getMessage();
306          // If InterruptedException then do nothing
307          if (!(ex.getMessage().equals("InterruptedException"))) {
308             LOG.error("SAXException: " + ex.getMessage() + LINE_BREAK, ex);
309             MessageUtil.showError(null, message);
310          }
311       } catch (Exception ex) {
312          final String message = ERROR_READING_FILE + ": " + ex.getMessage(); // AZ
313          LOG.error("Unexpected error reading file.", ex);
314          MessageUtil.showError(null, message);
315       }
316    }
317 
318    public DefaultHandler XMLHandler() {
319       DefaultHandler handler = new DefaultHandler() {
320          boolean toPersist = false;
321          boolean toPersistDisc = false;
322          ArrayList<String> discNames = new ArrayList();
323          final ArrayList<Disc> discs = new ArrayList();
324          StringBuffer buf = new StringBuffer();
325 
326          @Override
327          public void startElement(String uri, String localName, String qName, Attributes attributes)
328                   throws SAXException {
329             try {
330                if (Thread.interrupted()) {// Thread interrupted by user
331                   LOG.info("XML Import: Cancelled by User");
332                   throw new InterruptedException();
333                }
334                buf.setLength(0);// Clear buffer
335 
336                if (qName.equalsIgnoreCase("ARTIST")) {
337                   artist = new Artist();
338                }
339 
340                if (qName.equalsIgnoreCase("DISC")) {
341                   disc = new Disc();
342                }
343 
344                if (qName.equalsIgnoreCase("TRACK")) {
345                   track = new Track();
346                }
347 
348             } catch (InterruptedException ex) {
349                throw new SAXException("InterruptedException");
350             }
351          }
352 
353          @Override
354          public void endElement(String uri, String localName, String qName) throws SAXException {
355             Artist foundArtist;
356             String nodeString = new String(buf);
357             DateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");
358 
359             if (qName.equalsIgnoreCase("ARTIST")) {
360                // commit changes
361                if (toPersist) {
362                   try {
363                      HibernateUtil.beginTransaction();
364                      HibernateDao.saveOrUpdate(artist);
365                      HibernateUtil.commitTransaction();
366 
367                      // free memory
368                      s.evict(artist);
369                   } catch (Exception ex) {
370                      final String message = "Error writing database: " + ex.getMessage();
371                      LOG.error(message, ex);
372                      throw new SAXException(message);
373                   }
374                }
375                toPersist = false;
376             }
377 
378             if (qName.equalsIgnoreCase("ArtistName")) {
379                if (nodeString.length() > 100) {
380                   nodeString = nodeString.substring(0, 99);
381                   LOG.info("XML Import: Truncate String - " + nodeString);
382                }
383                artist.setName(nodeString);
384                // find or create the artist
385                try {
386                   String resource = ResourceUtils.getString("hql.artist.find");
387                   String hql = MessageFormat.format(resource, new Object[] { StringEscapeUtils.escapeSql(artist
388                            .getName()) });
389                   foundArtist = (Artist) HibernateDao.findUniqueByQuery(hql);
390 
391                   if (foundArtist == null) {
392                      artistCount = artistCount + 1;
393                      toPersist = true;
394                      LOG.info("XML Import: New Artist - " + artist.getName());
395                      discNames.clear();
396                   } else {
397                      artist = foundArtist;
398                      LOG.info("XML Import: Artist exists already - " + artist.getName());
399                      discs.addAll(artist.getDiscs());
400                      discNames.clear();
401                      for (final Iterator iter = discs.iterator(); iter.hasNext();) {
402                         final Disc disc = (Disc) iter.next();
403                         discNames.add(disc.getName());
404                      }
405                   }
406                } catch (Exception ex) {
407                   final String message = "Unexpected error occured performing import: " + ex.getMessage();
408                   LOG.error(message, ex);
409                   throw new SAXException(message);
410                }
411             }
412             if (qName.equalsIgnoreCase("ArtistNotes")) {
413                if (nodeString.length() > 500) {
414                   nodeString = nodeString.substring(0, 499);
415                   LOG.info("XML Import: Truncate String - " + nodeString);
416                }
417                artist.setNotes(nodeString);
418             }
419             if (qName.equalsIgnoreCase("ArtistCreatedUser")) {
420                artist.setCreatedUser(nodeString);
421             }
422             if (qName.equalsIgnoreCase("ArtistModifiedUser")) {
423                artist.setModifiedUser(nodeString);
424             }
425             if (qName.equalsIgnoreCase("ArtistCreatedDate")) {
426                try {
427                   Date createdDate = df.parse(nodeString);
428                   artist.setCreatedDate(createdDate);
429                } catch (ParseException e) {
430                   LOG.warn("Import XML: " + e.getMessage());
431                }
432             }
433             if (qName.equalsIgnoreCase("ArtistModifiedDate")) {
434                try {
435                   Date modifiedDate = df.parse(nodeString);
436                   artist.setModifiedDate(modifiedDate);
437                } catch (ParseException e) {
438                   LOG.warn("Import XML: " + e.getMessage());
439                }
440             }
441             if (qName.equalsIgnoreCase("DISC")) {
442                count = count + 1;// Total number of processed discs
443                progressCount = progressCount + 1;// counter for progress bar
444                if (progressCount > 100) {// Re-set Progress Bar Counter
445                   progressCount = progressCount - 100;
446                }
447                progressBar.setValue(progressCount);
448                progressBar.setString(Integer.toString(count));
449                if (toPersistDisc) {
450                   artist.addDisc(disc);
451                }
452                toPersistDisc = false;
453             }
454             if (qName.equalsIgnoreCase("DiscName")) {
455                if (nodeString.length() > 100) {
456                   nodeString = nodeString.substring(0, 99);
457                   LOG.info("XML Import: Truncate String - " + nodeString);
458                }
459                disc.setName(nodeString);
460                // find or create the disc
461                try {
462                   if ((!discNames.contains(nodeString)) || discNames.isEmpty()) {
463                      discCount = discCount + 1;
464                      toPersist = true;
465                      toPersistDisc = true;
466                      LOG.info("XML Import: New Disc - " + disc.getName());
467                   } else {
468                      LOG.info("XML Import: Disc exists already - " + disc.getName());
469                   }
470                } catch (Exception ex) {
471                   final String message = "Unexpected error occured performing import: " + ex.getMessage();
472                   LOG.error(message, ex);
473                   throw new SAXException(message);
474                }
475             }
476             if (qName.equalsIgnoreCase("DiscYear")) {
477                disc.setYear(nodeString);
478             }
479             if (qName.equalsIgnoreCase("DiscGenre")) {
480                if (nodeString.length() > 100) {
481                   nodeString = nodeString.substring(0, 99);
482                   LOG.info("XML Import: Truncate String - " + nodeString);
483                }
484                disc.setGenre(nodeString);
485             }
486             if (qName.equalsIgnoreCase("DiscBitrate")) {
487                try {
488                   Long LongBitrate = Long.parseLong(nodeString);
489                   disc.setBitrate(LongBitrate);
490                } catch (NumberFormatException e) {
491                   LOG.warn("Import XML: " + e.getMessage());
492                }
493             }
494             if (qName.equalsIgnoreCase("DiscCoverUrl")) {
495                disc.setCoverUrl(nodeString);
496             }
497             if (qName.equalsIgnoreCase("DiscCoverSize")) {
498                try {
499                   Long LongCoverSize = Long.parseLong(nodeString);
500                   disc.setCoverSize(LongCoverSize);
501                } catch (NumberFormatException e) {
502                   LOG.warn("Import XML: " + e.getMessage());
503                }
504             }
505             if (qName.equalsIgnoreCase("DiscDurationTime")) {
506                disc.setDurationTime(nodeString);
507             }
508             if (qName.equalsIgnoreCase("DiscDuration")) {
509                try {
510                   Long LongDuraton = Long.parseLong(nodeString);
511                   disc.setDuration(LongDuraton);
512                } catch (NumberFormatException e) {
513                   LOG.warn("Import XML: " + e.getMessage());
514                }
515             }
516             if (qName.equalsIgnoreCase("DiscLocation")) {
517                disc.setLocation(nodeString);
518             }
519             if (qName.equalsIgnoreCase("DiscNotes")) {
520                if (nodeString.length() > 500) {
521                   nodeString = nodeString.substring(0, 499);
522                   LOG.info("XML Import: Truncate String - " + nodeString);
523                }
524                disc.setNotes(nodeString);
525             }
526             if (qName.equalsIgnoreCase("DiscCreatedUser")) {
527                disc.setCreatedUser(nodeString);
528             }
529             if (qName.equalsIgnoreCase("DiscModifiedUser")) {
530                disc.setModifiedUser(nodeString);
531             }
532             if (qName.equalsIgnoreCase("DiscCreatedDate")) {
533                try {
534                   Date createdDate = df.parse(nodeString);
535                   disc.setCreatedDate(createdDate);
536                } catch (ParseException e) {
537                   LOG.warn("Import XML: " + e.getMessage());
538                }
539             }
540             if (qName.equalsIgnoreCase("DiscModifiedDate")) {
541                try {
542                   Date modifiedDate = df.parse(nodeString);
543                   disc.setModifiedDate(modifiedDate);
544                } catch (ParseException e) {
545                   LOG.warn("Import XML: " + e.getMessage());
546                }
547             }
548             if (qName.equalsIgnoreCase("TRACK")) {
549                if (toPersistDisc) {
550                   disc.addTrack(track);
551                }
552             }
553             if (qName.equalsIgnoreCase("TrackName")) {
554                if (nodeString.length() > 100) {
555                   nodeString = nodeString.substring(0, 99);
556                   LOG.info("XML Import: Truncate String - " + nodeString);
557                }
558                track.setName(nodeString);
559             }
560             if (qName.equalsIgnoreCase("TrackNumber")) {
561                track.setTrackNumber(nodeString);
562             }
563             if (qName.equalsIgnoreCase("TrackBitrate")) {
564                try {
565                   Long LongBitrate = Long.parseLong(nodeString);
566                   track.setBitrate(LongBitrate);
567                } catch (NumberFormatException e) {
568                   LOG.warn("Import XML: " + e.getMessage());
569                }
570             }
571             if (qName.equalsIgnoreCase("TrackUrl")) {
572                track.setTrackUrl(nodeString);
573             }
574             if (qName.equalsIgnoreCase("TrackSize")) {
575                try {
576                   Long LongTrackSize = Long.parseLong(nodeString);
577                   track.setTrackSize(LongTrackSize);
578                } catch (NumberFormatException e) {
579                   LOG.warn("Import XML: " + e.getMessage());
580                }
581             }
582             if (qName.equalsIgnoreCase("TrackDurationTime")) {
583                track.setDurationTime(nodeString);
584             }
585             if (qName.equalsIgnoreCase("TrackDuration")) {
586                try {
587                   Long LongDuraton = Long.parseLong(nodeString);
588                   track.setDuration(LongDuraton);
589                } catch (NumberFormatException e) {
590                   LOG.warn("Import XML: " + e.getMessage());
591                }
592             }
593             if (qName.equalsIgnoreCase("TrackComment")) {
594                if (nodeString.length() > 254) {
595                   nodeString = nodeString.substring(0, 253);
596                   LOG.info("XML Import: Truncate String - " + nodeString);
597                }
598                track.setComment(nodeString);
599             }
600             if (qName.equalsIgnoreCase("TrackCreatedUser")) {
601                track.setCreatedUser(nodeString);
602             }
603             if (qName.equalsIgnoreCase("TrackModifiedUser")) {
604                track.setModifiedUser(nodeString);
605             }
606             if (qName.equalsIgnoreCase("TrackCreatedDate")) {
607                try {
608                   Date createdDate = df.parse(nodeString);
609                   track.setCreatedDate(createdDate);
610                } catch (ParseException e) {
611                   LOG.warn("Import XML: " + e.getMessage());
612                }
613             }
614             if (qName.equalsIgnoreCase("TrackModifiedDate")) {
615                try {
616                   Date modifiedDate = df.parse(nodeString);
617                   track.setModifiedDate(modifiedDate);
618                } catch (ParseException e) {
619                   LOG.warn("Import XML: " + e.getMessage());
620                }
621             }
622          }
623 
624          @Override
625          public void characters(char ch[], int start, int length) throws SAXException {
626             buf.append(ch, start, length);
627          }
628 
629       };
630       return handler;
631    }
632 
633 }