View Javadoc

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 		// deserialization of older format could leave this field null. Handle
478 		// this case
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 }