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
62
63
64
65
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
89
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
103
104
105 @Override
106 public void doCancel() {
107 LOG.debug("Cancel Pressed.");
108 super.doCancel();
109 }
110
111
112
113
114
115 @Override
116 protected JComponent buildContent() {
117 JPanel content = new JPanel(new BorderLayout());
118 JButton[] buttons = new JButton[3];
119
120 buttonCancel = createCancelButton();
121 buttonCancel.setText(Resources.getString("label.Close"));
122
123
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
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
152
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
164
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
177
178
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
198
199 protected void doSearch() {
200 LOG.debug("Importing...");
201
202
203 buttonSearch.setEnabled(false);
204 buttonCancel.setEnabled(false);
205 buttonStop.setEnabled(true);
206
207
208
209
210
211
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
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
241
242
243 @Override
244 protected void resizeHook(JComponent component) {
245 Resizer.ONE2ONE.resizeDialogContent(component);
246 }
247
248
249
250
251
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
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
272
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
284 progressBar.setMaximum(100);
285 progressBar.setValue(0);
286 try {
287
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();
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
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();
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()) {
331 LOG.info("XML Import: Cancelled by User");
332 throw new InterruptedException();
333 }
334 buf.setLength(0);
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
361 if (toPersist) {
362 try {
363 HibernateUtil.beginTransaction();
364 HibernateDao.saveOrUpdate(artist);
365 HibernateUtil.commitTransaction();
366
367
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
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;
443 progressCount = progressCount + 1;
444 if (progressCount > 100) {
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
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 }