/**
* This work is licensed under the Creative Commons Attribution-NonCommercial-
* NoDerivs 3.0 Unported License. To view a copy of this license, visit
* http://creativecommons.org/licenses/by-nc-nd/3.0/ or send a letter to
* Creative Commons, 444 Castro Street, Suite 900, Mountain View, California,
* 94041, USA.
*
* Use of this work is permitted only in accordance with license rights granted.
* Materials provided "AS IS"; no representations or warranties provided.
*
* Copyright � 2012 Marcus Parkkinen, Aki K�kel�, Fredrik �hs.
**/
package edu.chalmers.dat255.audiobookplayer.model;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import edu.chalmers.dat255.audiobookplayer.constants.Constants;
import edu.chalmers.dat255.audiobookplayer.interfaces.IBookUpdates;
/**
* Represents a collection of Track objects. Null tracks are not allowed (and
* will be ignored when added).
*
* @author Marcus Parkkinen, Aki K�kel�
* @version 0.6
*/
public final class Book implements IBookUpdates, Serializable {
private static final String TAG = "Book.java";
private static final int NO_TRACK_SELECTED = Constants.Value.NO_TRACK_SELECTED;
private static final long serialVersionUID = 2;
private List<Track> tracks;
private int selectedTrackIndex = NO_TRACK_SELECTED;
private String author; // immutable
private String title;
private int duration;
/**
* Used when no author is given.
*
* @param title
* The title of the book.
*/
public Book(String title) {
this(title, Constants.Message.NO_AUTHOR);
}
/**
* Creates an empty book with the given title and author.
*/
public Book(String title, String author) {
tracks = new LinkedList<Track>();
setSelectedBookTitle(title);
// when a book is created, it should be ensured that the author is
// appropriate.
if (author == null || author.isEmpty()) {
this.author = Constants.Message.NO_AUTHOR;
} else {
this.author = author;
}
}
/**
* Creates a book from the referenced collection of Tracks. This constructor
* assumes that the provided collection holds tracks.
*
* @param col
* The collection of tracks.
* @param title
* The title of the book.
* @param author
* The author of the book.
*/
public Book(Collection<Track> col, String title, String author) {
this(title, author);
for (Track t : col) {
if (t != null) {
tracks.add(t);
duration += t.getDuration();
}
}
// adjust the track index now that we have tracks
selectedTrackIndex = 0;
}
/**
* Copy constructor.
*
* @param original
* The book to copy.
*/
public Book(Book original) {
this(original.getSelectedBookTitle(), original.getSelectedBookAuthor());
// copy primitive member variables
this.duration = original.duration;
this.selectedTrackIndex = original.selectedTrackIndex;
setAuthor(original.getSelectedBookAuthor());
// also create deep copies of the tracks
for (Track t : original.tracks) {
this.tracks.add(new Track(t));
}
}
/**
* Creates a book from the referenced collection of Tracks.
*
* @param col
* Collection of tracks.
* @param title
* Title of the book.
*/
public Book(Collection<Track> col, String title) {
this(col, title, Constants.Message.NO_AUTHOR);
}
/* IBookUpdates */
/*
* (non-Javadoc)
*
* @see
* edu.chalmers.dat255.audiobookplayer.interfaces.IBookUpdates#removeTrack
* (int)
*/
public void removeTrack(int trackIndex) {
checkTrackIndexLegal(trackIndex);
// remove the track and adjust the duration
duration -= tracks.remove(trackIndex).getDuration();
// check whether this was the last track
if (tracks.size() == 0) {
setSelectedTrackIndex(NO_TRACK_SELECTED);
} else {
if (trackIndex < selectedTrackIndex) {
// adjust the index if we removed one earlier in the list
selectedTrackIndex--;
} else if (trackIndex == selectedTrackIndex) {
// if we removed the selected one then mark the first
selectedTrackIndex = 0;
}
}
}
/*
* (non-Javadoc)
*
* @see
* edu.chalmers.dat255.audiobookplayer.interfaces.IBookUpdates#addTrack(
* edu.chalmers.dat255.audiobookplayer.model.Track)
*/
public void addTrack(Track t) {
if (t != null) {
// add the track
tracks.add(t);
// adjust the duration
duration += t.getDuration();
if (tracks.size() == 1) {
selectedTrackIndex = 0;
}
}
}
/*
* (non-Javadoc)
*
* @see
* edu.chalmers.dat255.audiobookplayer.interfaces.IBookUpdates#swapTracks
* (int, int)
*/
public void swapTracks(int firstIndex, int secondIndex) {
checkTrackIndexLegal(firstIndex);
checkTrackIndexLegal(secondIndex);
Collections.swap(tracks, firstIndex, secondIndex);
}
/*
* (non-Javadoc)
*
* @see
* edu.chalmers.dat255.audiobookplayer.interfaces.IBookUpdates#moveTrack
* (int, int)
*/
public void moveTrack(int fromIndex, int toIndex) {
checkTrackIndexLegal(fromIndex);
checkTrackIndexLegal(toIndex);
Track t = tracks.remove(fromIndex);
tracks.add(toIndex, t);
}
/*
* (non-Javadoc)
*
* @see edu.chalmers.dat255.audiobookplayer.interfaces.IBookUpdates#
* setSelectedTrackIndex(int)
*/
public void setSelectedTrackIndex(int index) {
if (index < -1 || index > this.tracks.size() + 1) {
throw new IndexOutOfBoundsException(TAG
+ " setSelectedTrackIndex with index out of bounds: "
+ index + ", list size: " + this.tracks.size());
}
selectedTrackIndex = index;
}
/*
* (non-Javadoc)
*
* @see edu.chalmers.dat255.audiobookplayer.interfaces.IBookUpdates#
* setSelectedBookTitle(java.lang.String)
*/
public void setSelectedBookTitle(String title) {
if (title == null) {
throw new IllegalArgumentException(TAG
+ " setBookTitle to null title is illegal");
}
this.title = title;
}
/*
* (non-Javadoc)
*
* @see edu.chalmers.dat255.audiobookplayer.interfaces.IBookUpdates#
* updateBookDuration()
*/
public void updateSelectedBookDuration() {
this.duration = 0;
for (Track t : tracks) {
this.duration += t.getDuration();
}
}
/*
* (non-Javadoc)
*
* @see edu.chalmers.dat255.audiobookplayer.interfaces.IBookUpdates#
* getSelectedBookAuthor()
*/
public String getSelectedBookAuthor() {
return author;
}
/*
* (non-Javadoc)
*
* @see edu.chalmers.dat255.audiobookplayer.interfaces.IBookUpdates#
* getSelectedBookTitle()
*/
public String getSelectedBookTitle() {
return title;
}
/* End IBookUpdates */
/**
* @param author
* The author to set to.
*/
private void setAuthor(String author) {
this.author = author;
}
/* ITrackUpdates */
/*
* (non-Javadoc)
*
* @see edu.chalmers.dat255.audiobookplayer.interfaces.ITrackUpdates#
* setSelectedTrackElapsedTime(int)
*/
public void setSelectedTrackElapsedTime(int newTime) {
checkTrackIndexLegal(selectedTrackIndex);
this.tracks.get(selectedTrackIndex)
.setSelectedTrackElapsedTime(newTime);
}
/*
* (non-Javadoc)
*
* @see
* edu.chalmers.dat255.audiobookplayer.interfaces.ITrackUpdates#addTag(int)
*/
public void addTag(int time) {
checkTrackIndexLegal(selectedTrackIndex);
this.tracks.get(selectedTrackIndex).addTag(time);
}
/*
* (non-Javadoc)
*
* @see
* edu.chalmers.dat255.audiobookplayer.interfaces.ITrackUpdates#removeTagAt
* (int)
*/
public void removeTagAt(int tagIndex) {
checkTrackIndexLegal(selectedTrackIndex);
this.tracks.get(selectedTrackIndex).removeTagAt(tagIndex);
}
/* End ITrackUpdates */
/**
* Checks whether a provided index is within the legal bounds of the list of
* tracks.
*
* @param index
* Index to check.
* @return True if the given index is within bounds of the track list.
*/
public boolean isLegalTrackIndex(int index) {
return index >= 0 && index < tracks.size();
}
/**
* Returns the number of elements (tracks) in the book.
*
* @return a Number of elements (tracks).
*/
public int getNumberOfTracks() {
return tracks.size();
}
/**
* Returns the index of the currently selected track.
*
* @return int index
*/
public int getSelectedTrackIndex() {
return this.selectedTrackIndex;
}
/**
* The duration of the current track in milliseconds.
*
* @return
*/
public int getSelectedTrackDuration() {
checkTrackIndexLegal(selectedTrackIndex);
return tracks.get(selectedTrackIndex).getDuration();
}
/**
* Returns the elapsed time of the selected track in the selected book.
*
* @return elapsed time
*/
public int getSelectedTrackElapsedTime() {
checkTrackIndexLegal(selectedTrackIndex);
return this.tracks.get(selectedTrackIndex).getElapsedTime();
}
/**
* Gets the track path of the currently selected track.
*
* @return
*/
public String getSelectedTrackPath() {
// TODO: checkTrackLegal...
return getTrackPathAt(selectedTrackIndex);
}
public String getTrackPathAt(int trackIndex) {
// TODO: check legal
return tracks.get(trackIndex).getTrackPath();
}
/**
* Returns a list containing references to all tracks contained in this
* book.
*
* @return the list
*/
public List<String> getTrackPaths() {
List<String> paths = new LinkedList<String>();
for (Track t : tracks) {
paths.add(t.getTrackPath());
}
return paths;
}
/**
* Get a list of all the tracktitles of the book.
*
* @return The title.
*/
public List<String> getTrackTitles() {
List<String> trackTitles = new LinkedList<String>();
for (Track t : tracks) {
trackTitles.add(t.getTrackTitle());
}
return trackTitles;
}
/**
* Gets the duration of the book.
*
* @return duration (ms)
*/
public int getDuration() {
return duration;
}
/**
* Returns the elapsed time of the current book.
*
* @return The elapsed time of the book.
*/
public int getBookElapsedTime() {
int bookElapsedTime = 0;
// add the duration of all previous tracks
for (int i = 0; i < this.selectedTrackIndex; i++) {
bookElapsedTime += tracks.get(i).getDuration();
}
// add the elapsed time of the current track
bookElapsedTime += tracks.get(this.selectedTrackIndex).getElapsedTime();
return bookElapsedTime;
}
/**
* Returns the duration (in ms) of the track located at the specified index.
*
* @param trackIndex
* Index of the track
* @return int duration (ms)
*/
public int getTrackDurationAt(int trackIndex) {
checkTrackIndexLegal(trackIndex);
return tracks.get(trackIndex).getDuration();
}
/**
* Returns the title of the track.
*
* @return String title
*/
public String getTrackTitle() {
checkTrackIndexLegal(selectedTrackIndex);
return this.tracks.get(selectedTrackIndex).getTrackTitle();
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return new HashCodeBuilder().append(tracks).append(selectedTrackIndex)
.append(author).append(title).append(duration).toHashCode();
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (obj instanceof Book) {
final Book other = (Book) obj;
return new EqualsBuilder().append(tracks, other.tracks)
.append(selectedTrackIndex, other.selectedTrackIndex)
.append(author, other.author).append(title, other.title)
.append(duration, other.duration).isEquals();
} else {
return false;
}
}
/**
* Returns all tag times.
*
* @return Array of all tag times.
*/
public int[] getTagTimes() {
checkTrackIndexLegal(selectedTrackIndex);
return this.tracks.get(selectedTrackIndex).getTagTimes();
}
/*
* FOR TESTING PURPOSES ONLY
*/
/**
* Returns a reference to the currently selected track. NOTE: Only for
* testing purposes.
*
* @return the currently selected track
*/
public Track getSelectedTrack() {
return tracks.get(selectedTrackIndex);
}
/*
* END TESTING PURPOSES ONLY
*/
/**
* Gets the track title of a given track index.
*
* @param trackIndex
* Index to get the title from.
* @return Track title at given index.
*/
public String getTrackTitleAt(int trackIndex) {
if (trackIndex >= 0 && trackIndex < tracks.size()) {
return tracks.get(trackIndex).getTrackTitle();
}
return null;
}
/**
* Throws an IndexOutOfBoundsException if the given index is not legal.
*
* @param trackIndex
* The given index.
*/
private void checkTrackIndexLegal(int trackIndex) {
if (!isLegalTrackIndex(trackIndex)) {
throw new IndexOutOfBoundsException("Track index is illegal: "
+ trackIndex);
}
}
}