1 /***
2 * Ambient - A music player for the Android platform
3 Copyright (C) 2007 Martin Vysny
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 package sk.baka.ambient.collection;
19
20 import java.io.File;
21 import java.io.Serializable;
22
23 import sk.baka.ambient.commons.MiscUtils;
24
25 /***
26 * Contains metadata about a track. Immutable, thread-safe.
27 *
28 * @author Martin Vysny
29 */
30 public final class TrackMetadataBean implements Serializable {
31 /***
32 *
33 */
34 private static final long serialVersionUID = 7772018128569847012L;
35
36 /***
37 * Database track id.
38 */
39 private transient final long trackId;
40
41 /***
42 * The year released.
43 */
44 private final String yearReleased;
45
46 /***
47 * Frequency in hz.
48 */
49 private final int frequency;
50
51 /***
52 * You can buy the album here
53 */
54 private final String buyURL;
55
56 /***
57 * The license URL
58 */
59 private final String license;
60
61 /***
62 * A link to artist's page
63 */
64 private final String artistURL;
65
66 /***
67 * The artist's description.
68 */
69 private final String artistDesc;
70
71 /***
72 * Creates new metadata bean.
73 *
74 * @param trackId
75 * the database track id
76 * @param track
77 * the track.
78 */
79 private TrackMetadataBean(final long trackId, final Builder track) {
80 super();
81 if (track.bitrate < 0)
82 throw new IllegalArgumentException("bitrate must be >=0");
83 if (track.location == null)
84 throw new IllegalArgumentException("location must not be null");
85 if (track.origin == null)
86 throw new IllegalArgumentException("origin must not be null");
87 this.trackId = trackId;
88 this.origin = track.origin;
89 this.title = track.title;
90 this.artist = track.artist;
91 this.composer = track.composer;
92 this.album = track.album;
93 this.genre = track.genre;
94 this.trackNumber = fixTrackNumber(track.trackNumber);
95 this.location = track.location;
96 this.length = track.length;
97 this.bitrate = track.bitrate;
98 this.fileSize = track.fileSize;
99 this.yearReleased = track.yearReleased;
100 this.frequency = track.frequency;
101 this.license = track.license;
102 this.artistURL = track.artistURL;
103 this.buyURL = track.buyURL;
104 this.artistDesc = track.artistDesc;
105 }
106
107 private static String fixTrackNumber(final String tn) {
108 if (tn == null) {
109 return tn;
110 }
111 if (tn.length() == 1 && Character.isDigit(tn.charAt(0))) {
112 return "0" + tn;
113 }
114 return tn;
115 }
116
117 /***
118 * Returns the name of the track. Returns {@link #getTitle()} unless it's
119 * <code>null</code> - in this case returns the file name.
120 *
121 * @return the track name, never <code>null</code>.
122 */
123 public final String getDisplayableName() {
124 if (displayableName == null) {
125 if (MiscUtils.isEmptyOrWhitespace(title)) {
126 if (origin.online) {
127 displayableName = location;
128 } else {
129 displayableName = location.substring(location
130 .lastIndexOf('/') + 1);
131 }
132 } else {
133 displayableName = title;
134 }
135 }
136 return displayableName;
137 }
138
139 private String displayableName;
140
141 /***
142 * Track title.
143 */
144 private final String title;
145
146 /***
147 * Artist.
148 */
149 private final String artist;
150
151 /***
152 * Original composer.
153 */
154 private final String composer;
155
156 /***
157 * Album name.
158 */
159 private final String album;
160
161 /***
162 * Genre.
163 */
164 private final String genre;
165
166 /***
167 * Track number.
168 */
169 private final String trackNumber;
170
171 /***
172 * Location on the filesystem. May be URL if it is an Internet stream.
173 */
174 private final String location;
175
176 /***
177 * Length in seconds. May be 0 if not known.
178 */
179 private final int length;
180
181 /***
182 * Bitrate in kbps. If zero then it is not known.
183 */
184 private final int bitrate;
185
186 /***
187 * File size in bytes. -1 if the track is an Internet stream.
188 */
189 private final long fileSize;
190
191 /***
192 * The track location.
193 */
194 private final TrackOriginEnum origin;
195
196 /***
197 * Returns length in the <code>[hh:]mm:ss</code> format.
198 *
199 * @param length
200 * the length in seconds
201 * @return displayable length. If zero then empty string is returned.
202 */
203 public static String getDisplayableLength(int length) {
204 if (length == 0)
205 return "";
206 final StringBuilder result = new StringBuilder(7);
207 appendDisplayableLength(length, result, true);
208 return result.toString();
209 }
210
211 /***
212 * Appends length in the <code>[hh:]mm:ss</code> format.
213 *
214 * @param length
215 * the length in seconds
216 * @param builder
217 * append the time here.
218 * @param emptyStringOnZero
219 * if <code>true</code> then empty string is returned on zero.
220 */
221 public static void appendDisplayableLength(final int length,
222 StringBuilder builder, boolean emptyStringOnZero) {
223 int len = length;
224 if ((len == 0) && emptyStringOnZero)
225 return;
226 boolean hours = false;
227 if (len >= 60 * 60) {
228 builder.append(len / (60 * 60));
229 builder.append(':');
230 hours = true;
231 }
232 len %= (60 * 60);
233 final int minutes = len / 60;
234 if ((hours) && (minutes < 10))
235 builder.append('0');
236 builder.append(minutes);
237 builder.append(':');
238 final int seconds = len % 60;
239 if (seconds < 10)
240 builder.append('0');
241 builder.append(seconds);
242 }
243
244 /***
245 * Returns length in the <code>[hh:]mm:ss</code> format.
246 *
247 * @return displayable length. If zero then empty string is returned.
248 */
249 public String getDisplayableLength() {
250 return getDisplayableLength(length);
251 }
252
253 /***
254 * Album name.
255 *
256 * @return the album
257 */
258 public String getAlbum() {
259 return album;
260 }
261
262 /***
263 * Artist name.
264 *
265 * @return the artist
266 */
267 public String getArtist() {
268 return artist;
269 }
270
271 /***
272 * Bitrate in kbps. If zero then it is not known.
273 *
274 * @return the bitrate
275 */
276 public int getBitrate() {
277 return bitrate;
278 }
279
280 /***
281 * Original composer.
282 *
283 * @return the composer
284 */
285 public String getComposer() {
286 return composer;
287 }
288
289 /***
290 * File size in bytes. -1 if the track is an Internet stream.
291 *
292 * @return the fileSize
293 */
294 public long getFileSize() {
295 return fileSize;
296 }
297
298 /***
299 * The year released.
300 *
301 * @return the yearReleased
302 */
303 public String getYearReleased() {
304 return yearReleased;
305 }
306
307 /***
308 * Genre.
309 *
310 * @return the genre
311 */
312 public String getGenre() {
313 return genre;
314 }
315
316 /***
317 * Length in seconds. May be 0 if not known.
318 *
319 * @return the length
320 */
321 public int getLength() {
322 return length;
323 }
324
325 /***
326 * Location on the filesystem. May be URL if it is an Internet stream.
327 *
328 * @return the location
329 */
330 public String getLocation() {
331 return location;
332 }
333
334 /***
335 * Checks if the track denoted by this object is a local one (you can pass
336 * {@link #getLocation()} to a {@link File} object) , or an Internet stream
337 * ({@link #getLocation()} is valid URL).
338 *
339 * @return <code>true</code> if the file is local.
340 */
341 public boolean isLocal() {
342 return !getOrigin().online;
343 }
344
345 /***
346 * @return the title
347 */
348 public String getTitle() {
349 return title;
350 }
351
352 /***
353 * @return the trackNumber
354 */
355 public String getTrackNumber() {
356 return trackNumber;
357 }
358
359 @Override
360 public boolean equals(Object o) {
361 if (!(o instanceof TrackMetadataBean))
362 return false;
363 return location.equals(((TrackMetadataBean) o).location);
364 }
365
366 @Override
367 public int hashCode() {
368 return location.hashCode();
369 }
370
371 @Override
372 public String toString() {
373 return "Track: " + location;
374 }
375
376 /***
377 * Frequency in hz.
378 *
379 * @return the frequency in hz, or 0 if not known.
380 */
381 public int getFrequency() {
382 return frequency;
383 }
384
385 /***
386 * Database track id.
387 *
388 * @return the trackId
389 */
390 public long getTrackId() {
391 return trackId;
392 }
393
394 /***
395 * Creates new bean having equal fields <code>null</code>ed.
396 *
397 * @param other
398 * the other bean
399 * @return bean with equal fields <code>null</code>ed.
400 */
401 public TrackMetadataBean nullsEqualFields(final TrackMetadataBean other) {
402 return nullsEqualFields(other, false);
403 }
404
405 /***
406 * Creates new bean having equal fields <code>null</code>ed.
407 *
408 * @param other
409 * the other bean
410 * @param sensibleFieldsOnly
411 * if <code>true</code> then only sensible fields are compared -
412 * i.e. the {@link #bitrate}, {@link #fileSize},
413 * {@link #frequency}, {@link #length}, {@link #title} and
414 * {@link #trackNumber} are not nulled even when equal.
415 * @return bean with equal fields <code>null</code>ed.
416 */
417 public TrackMetadataBean nullsEqualFields(final TrackMetadataBean other,
418 final boolean sensibleFieldsOnly) {
419 final Builder result = newBuilder();
420 result.album = MiscUtils.nullCompare(this.album, other.album, true) == 0 ? null
421 : this.album;
422 result.artist = MiscUtils.nullCompare(this.artist, other.artist, true) == 0 ? null
423 : this.artist;
424 result.composer = MiscUtils.nullCompare(this.composer, other.composer,
425 true) == 0 ? null : this.composer;
426 result.genre = MiscUtils.nullCompare(this.genre, other.genre, true) == 0 ? null
427 : this.genre;
428 result.yearReleased = MiscUtils.nullCompare(this.yearReleased,
429 other.yearReleased, true) == 0 ? null : this.yearReleased;
430 result.buyURL = MiscUtils.nullCompare(this.buyURL, other.buyURL, true) == 0 ? null
431 : this.buyURL;
432 result.artistURL = MiscUtils.nullCompare(this.artistURL,
433 other.artistURL, true) == 0 ? null : this.artistURL;
434 result.artistDesc = MiscUtils.nullCompare(this.artistDesc,
435 other.artistDesc, true) == 0 ? null : this.artistDesc;
436 result.license = MiscUtils.nullCompare(this.license, other.license,
437 true) == 0 ? null : this.license;
438 result.location = this.location;
439 result.origin = this.origin;
440 if (sensibleFieldsOnly) {
441 result.bitrate = this.bitrate;
442 result.fileSize = this.fileSize;
443 result.frequency = this.frequency;
444 result.length = this.length;
445 result.title = this.title;
446 result.trackNumber = this.trackNumber;
447 } else {
448 result.bitrate = this.bitrate == other.bitrate ? 0 : this.bitrate;
449 result.fileSize = this.fileSize == other.fileSize ? 0
450 : this.fileSize;
451 result.frequency = this.frequency == other.frequency ? 0
452 : this.frequency;
453 result.length = this.length == other.length ? 0 : this.length;
454 result.title = MiscUtils.nullCompare(this.title, other.title, true) == 0 ? null
455 : this.title;
456 result.trackNumber = MiscUtils.nullCompare(this.trackNumber,
457 other.trackNumber, true) == 0 ? null : this.trackNumber;
458 }
459 return result.build(trackId);
460 }
461
462 /***
463 * An empty bean.
464 */
465 public final static TrackMetadataBean EMPTY;
466 static {
467 EMPTY = newBuilder().setOrigin(TrackOriginEnum.LocalFs)
468 .setLocation("/").build(-1);
469 }
470
471 /***
472 * The track origin.
473 *
474 * @return origin, never <code>null</code>.
475 */
476 public TrackOriginEnum getOrigin() {
477
478
479 return origin == null ? TrackOriginEnum.LocalFs : origin;
480 }
481
482 /***
483 * You can buy the album here
484 *
485 * @return the buyURL
486 */
487 public String getBuyURL() {
488 return buyURL;
489 }
490
491 /***
492 * The license URL
493 *
494 * @return the license
495 */
496 public String getLicense() {
497 return license;
498 }
499
500 /***
501 * A link to artist's page
502 *
503 * @return the artistURL
504 */
505 public String getArtistURL() {
506 return artistURL;
507 }
508
509 /***
510 * The artist's description.
511 *
512 * @return the artistDesc
513 */
514 public String getArtistDesc() {
515 return artistDesc;
516 }
517
518 /***
519 * Returns new track metadata builder instance.
520 *
521 * @return the builder instance.
522 */
523 public static Builder newBuilder() {
524 return new Builder();
525 }
526
527 /***
528 * The mutable version of {@link TrackMetadataBean}. Use it to gradually
529 * build the bean. Thread-unsafe.
530 *
531 * @author Martin Vysny
532 */
533 public final static class Builder {
534 /***
535 * The year released.
536 */
537 public String yearReleased;
538
539 /***
540 * The year released.
541 *
542 * @param yearReleased
543 * the yearReleased to set
544 * @return this
545 */
546 public Builder setYearReleased(String yearReleased) {
547 this.yearReleased = yearReleased;
548 return this;
549 }
550
551 /***
552 * Frequency in hz.
553 *
554 * @param frequency
555 * the frequency to set
556 * @return this
557 */
558 public Builder setFrequency(int frequency) {
559 this.frequency = frequency;
560 return this;
561 }
562
563 /***
564 * You can buy the album here.
565 *
566 * @param buyURL
567 * the buyURL to set
568 * @return this
569 */
570 public Builder setBuyURL(String buyURL) {
571 this.buyURL = buyURL;
572 return this;
573 }
574
575 /***
576 * The license URL
577 *
578 * @param license
579 * the license to set
580 * @return this
581 */
582 public Builder setLicense(String license) {
583 this.license = license;
584 return this;
585 }
586
587 /***
588 * A link to artist's page
589 *
590 * @param artistURL
591 * the artistURL to set
592 * @return this
593 */
594 public Builder setArtistURL(String artistURL) {
595 this.artistURL = artistURL;
596 return this;
597 }
598
599 /***
600 * The artist's description.
601 *
602 * @param artistDesc
603 * the artistDesc to set
604 * @return this
605 */
606 public Builder setArtistDesc(String artistDesc) {
607 this.artistDesc = artistDesc;
608 return this;
609 }
610
611 /***
612 * Track title.
613 *
614 * @param title
615 * the title to set
616 * @return this
617 */
618 public Builder setTitle(String title) {
619 this.title = title;
620 return this;
621 }
622
623 /***
624 * @param artist
625 * the artist to set
626 * @return this
627 */
628 public Builder setArtist(String artist) {
629 this.artist = artist;
630 return this;
631 }
632
633 /***
634 * Original composer.
635 *
636 * @param composer
637 * the composer to set
638 * @return this
639 */
640 public Builder setComposer(String composer) {
641 this.composer = composer;
642 return this;
643 }
644
645 /***
646 * Album name.
647 *
648 * @param album
649 * the album to set
650 * @return this
651 */
652 public Builder setAlbum(String album) {
653 this.album = album;
654 return this;
655 }
656
657 /***
658 * Genre.
659 *
660 * @param genre
661 * the genre to set
662 * @return this
663 */
664 public Builder setGenre(String genre) {
665 this.genre = genre;
666 return this;
667 }
668
669 /***
670 * Track number in the album ordering.
671 *
672 * @param trackNumber
673 * the trackNumber to set
674 * @return this
675 */
676 public Builder setTrackNumber(String trackNumber) {
677 this.trackNumber = trackNumber;
678 return this;
679 }
680
681 /***
682 * Location on the filesystem. May be URL if it is an Internet stream.
683 *
684 * @param location
685 * the location to set
686 * @return this
687 */
688 public Builder setLocation(String location) {
689 this.location = location;
690 return this;
691 }
692
693 /***
694 * Length in seconds. May be 0 if not known.
695 *
696 * @param length
697 * the length to set
698 * @return this
699 */
700 public Builder setLength(int length) {
701 this.length = length;
702 return this;
703 }
704
705 /***
706 * Bitrate in kbps. If zero then it is not known.
707 *
708 * @param bitrate
709 * the bitrate to set
710 * @return this
711 */
712 public Builder setBitrate(int bitrate) {
713 this.bitrate = bitrate;
714 return this;
715 }
716
717 /***
718 * File size in bytes. -1 if the track is an Internet stream.
719 *
720 * @param fileSize
721 * the fileSize to set
722 * @return this
723 */
724 public Builder setFileSize(long fileSize) {
725 this.fileSize = fileSize;
726 return this;
727 }
728
729 /***
730 * The track location.
731 *
732 * @param origin
733 * the origin to set
734 * @return this
735 */
736 public Builder setOrigin(TrackOriginEnum origin) {
737 this.origin = origin;
738 return this;
739 }
740
741 /***
742 * Frequency in hz.
743 */
744 public int frequency;
745
746 /***
747 * You can buy the album here
748 */
749 public String buyURL;
750
751 /***
752 * The license URL
753 */
754 public String license;
755
756 /***
757 * A link to artist's page
758 */
759 public String artistURL;
760
761 /***
762 * The artist's description.
763 */
764 public String artistDesc;
765
766 /***
767 * Track title.
768 */
769 public String title;
770
771 /***
772 * Artist.
773 */
774 public String artist;
775
776 /***
777 * Original composer.
778 */
779 public String composer;
780
781 /***
782 * Album name.
783 */
784 public String album;
785
786 /***
787 * Genre.
788 */
789 public String genre;
790
791 /***
792 * Track number.
793 */
794 public String trackNumber;
795
796 /***
797 * Location on the filesystem. May be URL if it is an Internet stream.
798 */
799 public String location;
800
801 /***
802 * Length in seconds. May be 0 if not known.
803 */
804 public int length;
805
806 /***
807 * Bitrate in kbps. If zero then it is not known.
808 */
809 public int bitrate;
810
811 /***
812 * File size in bytes. -1 if the track is an Internet stream.
813 */
814 public long fileSize;
815
816 /***
817 * The track location.
818 */
819 public TrackOriginEnum origin;
820
821 /***
822 * Creates new metadata bean.
823 *
824 * @param trackId
825 * the database track id
826 * @return new bean instance
827 */
828 public TrackMetadataBean build(final long trackId) {
829 return new TrackMetadataBean(trackId, this);
830 }
831
832 /***
833 * Polls data from given track.
834 *
835 * @param track
836 * copy data from given track.
837 * @return this
838 */
839 public Builder getData(final TrackMetadataBean track) {
840 album = track.album;
841 artist = track.artist;
842 artistDesc = track.artistDesc;
843 artistURL = track.artistURL;
844 bitrate = track.bitrate;
845 buyURL = track.buyURL;
846 composer = track.composer;
847 fileSize = track.fileSize;
848 frequency = track.frequency;
849 genre = track.genre;
850 length = track.length;
851 license = track.license;
852 location = track.location;
853 origin = track.origin;
854 title = track.title;
855 trackNumber = track.trackNumber;
856 yearReleased = track.yearReleased;
857 return this;
858 }
859 }
860 }