list: dont abuse comparators for sort

Likely causing crashes with how they are set up.
This commit is contained in:
Alexander Capehart 2024-06-29 18:24:16 -06:00
parent c8fa389267
commit 4c92ac0f85
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
2 changed files with 203 additions and 425 deletions

View file

@ -1,5 +1,10 @@
# Changelog
## dev
#### What's Fixed
- Fixed music loading failure from improper sort systems
## 3.5.0
#### What's New

View file

@ -18,17 +18,13 @@
package org.oxycblt.auxio.list.sort
import kotlin.math.max
import org.oxycblt.auxio.IntegerTable
import org.oxycblt.auxio.R
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.info.Date
import org.oxycblt.auxio.music.info.Disc
/**
* A sorting method.
@ -46,9 +42,9 @@ data class Sort(val mode: Mode, val direction: Direction) {
* @param songs The list of [Song]s.
* @return A new list of [Song]s sorted by this [Sort]'s configuration.
*/
fun <T : Song> songs(songs: Collection<T>): List<T> {
fun songs(songs: Collection<Song>): List<Song> {
val mutable = songs.toMutableList()
songsInPlace(mutable)
mode.sortSongs(mutable, direction)
return mutable
}
@ -58,9 +54,9 @@ data class Sort(val mode: Mode, val direction: Direction) {
* @param albums The list of [Album]s.
* @return A new list of [Album]s sorted by this [Sort]'s configuration.
*/
fun <T : Album> albums(albums: Collection<T>): List<T> {
fun albums(albums: Collection<Album>): List<Album> {
val mutable = albums.toMutableList()
albumsInPlace(mutable)
mode.sortAlbums(mutable, direction)
return mutable
}
@ -70,9 +66,9 @@ data class Sort(val mode: Mode, val direction: Direction) {
* @param artists The list of [Artist]s.
* @return A new list of [Artist]s sorted by this [Sort]'s configuration.
*/
fun <T : Artist> artists(artists: Collection<T>): List<T> {
fun artists(artists: Collection<Artist>): List<Artist> {
val mutable = artists.toMutableList()
artistsInPlace(mutable)
mode.sortArtists(mutable, direction)
return mutable
}
@ -82,9 +78,9 @@ data class Sort(val mode: Mode, val direction: Direction) {
* @param genres The list of [Genre]s.
* @return A new list of [Genre]s sorted by this [Sort]'s configuration.
*/
fun <T : Genre> genres(genres: Collection<T>): List<T> {
fun genres(genres: Collection<Genre>): List<Genre> {
val mutable = genres.toMutableList()
genresInPlace(mutable)
mode.sortGenres(mutable, direction)
return mutable
}
@ -94,37 +90,12 @@ data class Sort(val mode: Mode, val direction: Direction) {
* @param playlists The list of [Playlist]s.
* @return A new list of [Playlist]s sorted by this [Sort]'s configuration
*/
fun <T : Playlist> playlists(playlists: Collection<T>): List<T> {
fun playlists(playlists: Collection<Playlist>): List<Playlist> {
val mutable = playlists.toMutableList()
playlistsInPlace(mutable)
mode.sortPlaylists(mutable, direction)
return mutable
}
private fun songsInPlace(songs: MutableList<out Song>) {
val comparator = mode.getSongComparator(direction) ?: return
songs.sortWith(comparator)
}
private fun albumsInPlace(albums: MutableList<out Album>) {
val comparator = mode.getAlbumComparator(direction) ?: return
albums.sortWith(comparator)
}
private fun artistsInPlace(artists: MutableList<out Artist>) {
val comparator = mode.getArtistComparator(direction) ?: return
artists.sortWith(comparator)
}
private fun genresInPlace(genres: MutableList<out Genre>) {
val comparator = mode.getGenreComparator(direction) ?: return
genres.sortWith(comparator)
}
private fun playlistsInPlace(playlists: MutableList<out Playlist>) {
val comparator = mode.getPlaylistComparator(direction) ?: return
playlists.sortWith(comparator)
}
/**
* The integer representation of this instance.
*
@ -141,289 +112,254 @@ data class Sort(val mode: Mode, val direction: Direction) {
Direction.DESCENDING -> 0
}
/** Describes the type of data to sort with. */
sealed interface Mode {
/** The integer representation of this sort mode. */
val intCode: Int
/** The string resource of the human-readable name of this sort mode. */
val stringRes: Int
/**
* Get a [Comparator] that sorts [Song]s according to this [Mode].
*
* @param direction The direction to sort in.
* @return A [Comparator] that can be used to sort a [Song] list according to this [Mode],
* or null to not sort at all.
*/
fun getSongComparator(direction: Direction): Comparator<Song>? = null
fun sortSongs(songs: MutableList<Song>, direction: Direction) {
throw NotImplementedError("Sorting songs is not supported for this mode")
}
/**
* Get a [Comparator] that sorts [Album]s according to this [Mode].
*
* @param direction The direction to sort in.
* @return A [Comparator] that can be used to sort a [Album] list according to this [Mode],
* or null to not sort at all.
*/
fun getAlbumComparator(direction: Direction): Comparator<Album>? = null
fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
throw NotImplementedError("Sorting albums is not supported for this mode")
}
/**
* Return a [Comparator] that sorts [Artist]s according to this [Mode].
*
* @param direction The direction to sort in.
* @return A [Comparator] that can be used to sort a [Artist] list according to this [Mode].
* or null to not sort at all.
*/
fun getArtistComparator(direction: Direction): Comparator<Artist>? = null
fun sortArtists(artists: MutableList<Artist>, direction: Direction) {
throw NotImplementedError("Sorting artists is not supported for this mode")
}
/**
* Return a [Comparator] that sorts [Genre]s according to this [Mode].
*
* @param direction The direction to sort in.
* @return A [Comparator] that can be used to sort a [Genre] list according to this [Mode].
* or null to not sort at all.
*/
fun getGenreComparator(direction: Direction): Comparator<Genre>? = null
fun sortGenres(genres: MutableList<Genre>, direction: Direction) {
throw NotImplementedError("Sorting genres is not supported for this mode")
}
/**
* Return a [Comparator] that sorts [Playlist]s according to this [Mode].
*
* @param direction The direction to sort in.
* @return A [Comparator] that can be used to sort a [Genre] list according to this [Mode].
* or null to not sort at all.
*/
fun getPlaylistComparator(direction: Direction): Comparator<Playlist>? = null
fun sortPlaylists(playlists: MutableList<Playlist>, direction: Direction) {
throw NotImplementedError("Sorting playlists is not supported for this mode")
}
/**
* Sort by the item's name.
*
* @see Music.name
*/
data object ByName : Mode {
override val intCode: Int
get() = IntegerTable.SORT_BY_NAME
override val intCode = IntegerTable.SORT_BY_NAME
override val stringRes = R.string.lbl_name
override val stringRes: Int
get() = R.string.lbl_name
override fun getSongComparator(direction: Direction) =
compareByDynamic(direction, BasicComparator.SONG)
override fun getAlbumComparator(direction: Direction) =
compareByDynamic(direction, BasicComparator.ALBUM)
override fun getArtistComparator(direction: Direction) =
compareByDynamic(direction, BasicComparator.ARTIST)
override fun getGenreComparator(direction: Direction) =
compareByDynamic(direction, BasicComparator.GENRE)
override fun getPlaylistComparator(direction: Direction) =
compareByDynamic(direction, BasicComparator.PLAYLIST)
override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> songs.sortBy { it.name }
Direction.DESCENDING -> songs.sortByDescending { it.name }
}
}
override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> albums.sortBy { it.name }
Direction.DESCENDING -> albums.sortByDescending { it.name }
}
}
override fun sortArtists(artists: MutableList<Artist>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> artists.sortBy { it.name }
Direction.DESCENDING -> artists.sortByDescending { it.name }
}
}
override fun sortGenres(genres: MutableList<Genre>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> genres.sortBy { it.name }
Direction.DESCENDING -> genres.sortByDescending { it.name }
}
}
override fun sortPlaylists(playlists: MutableList<Playlist>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> playlists.sortBy { it.name }
Direction.DESCENDING -> playlists.sortByDescending { it.name }
}
}
}
/**
* Sort by the [Album] of an item. Only available for [Song]s.
*
* @see Album.name
*/
data object ByAlbum : Mode {
override val intCode: Int
get() = IntegerTable.SORT_BY_ALBUM
override val intCode = IntegerTable.SORT_BY_ALBUM
override val stringRes = R.string.lbl_album
override val stringRes: Int
get() = R.string.lbl_album
override fun getSongComparator(direction: Direction): Comparator<Song> =
MultiComparator(
compareByDynamic(direction, BasicComparator.ALBUM) { it.album },
compareBy(NullableComparator.DISC) { it.disc },
compareBy(NullableComparator.INT) { it.track },
compareBy(BasicComparator.SONG))
override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
songs.sortBy { it.track }
songs.sortBy { it.disc }
when (direction) {
Direction.ASCENDING -> songs.sortBy { it.album.name }
Direction.DESCENDING -> songs.sortByDescending { it.album.name }
}
}
}
/**
* Sort by the [Artist] name of an item. Only available for [Song] and [Album].
*
* @see Artist.name
*/
data object ByArtist : Mode {
override val intCode: Int
get() = IntegerTable.SORT_BY_ARTIST
override val intCode = IntegerTable.SORT_BY_ARTIST
override val stringRes = R.string.lbl_artist
override val stringRes: Int
get() = R.string.lbl_artist
override fun getSongComparator(direction: Direction): Comparator<Song> =
MultiComparator(
compareByDynamic(direction, ListComparator.ARTISTS) { it.artists },
compareByDescending(NullableComparator.DATE_RANGE) { it.album.dates },
compareByDescending(BasicComparator.ALBUM) { it.album },
compareBy(NullableComparator.DISC) { it.disc },
compareBy(NullableComparator.INT) { it.track },
compareBy(BasicComparator.SONG))
override fun getAlbumComparator(direction: Direction): Comparator<Album> =
MultiComparator(
compareByDynamic(direction, ListComparator.ARTISTS) { it.artists },
compareByDescending(NullableComparator.DATE_RANGE) { it.dates },
compareBy(BasicComparator.ALBUM))
override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
songs.sortByDescending { it.album.dates }
songs.sortByDescending { it.album.name }
songs.sortBy { it.disc }
songs.sortBy { it.track }
when (direction) {
Direction.ASCENDING -> songs.sortBy { it.artists.firstOrNull()?.name }
Direction.DESCENDING ->
songs.sortByDescending { it.artists.firstOrNull()?.name }
}
}
override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
albums.sortByDescending { it.dates }
when (direction) {
Direction.ASCENDING -> albums.sortBy { it.artists.firstOrNull()?.name }
Direction.DESCENDING ->
albums.sortByDescending { it.artists.firstOrNull()?.name }
}
}
}
/**
* Sort by the [Date] of an item. Only available for [Song] and [Album].
*
* @see Song.date
* @see Album.dates
*/
data object ByDate : Mode {
override val intCode: Int
get() = IntegerTable.SORT_BY_YEAR
override val intCode = IntegerTable.SORT_BY_YEAR
override val stringRes = R.string.lbl_date
override val stringRes: Int
get() = R.string.lbl_date
override fun getSongComparator(direction: Direction): Comparator<Song> =
MultiComparator(
compareByDynamic(direction, NullableComparator.DATE_RANGE) { it.album.dates },
compareByDescending(BasicComparator.ALBUM) { it.album },
compareBy(NullableComparator.DISC) { it.disc },
compareBy(NullableComparator.INT) { it.track },
compareBy(BasicComparator.SONG))
override fun getAlbumComparator(direction: Direction): Comparator<Album> =
MultiComparator(
compareByDynamic(direction, NullableComparator.DATE_RANGE) { it.dates },
compareBy(BasicComparator.ALBUM))
override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
songs.sortByDescending { it.album.name }
songs.sortBy { it.disc }
songs.sortBy { it.track }
when (direction) {
Direction.ASCENDING -> songs.sortBy { it.album.dates }
Direction.DESCENDING -> songs.sortByDescending { it.album.dates }
}
}
override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> albums.sortBy { it.dates }
Direction.DESCENDING -> albums.sortByDescending { it.dates }
}
}
}
/** Sort by the duration of an item. */
data object ByDuration : Mode {
override val intCode: Int
get() = IntegerTable.SORT_BY_DURATION
override val intCode = IntegerTable.SORT_BY_DURATION
override val stringRes = R.string.lbl_duration
override val stringRes: Int
get() = R.string.lbl_duration
override fun getSongComparator(direction: Direction): Comparator<Song> =
MultiComparator(
compareByDynamic(direction) { it.durationMs }, compareBy(BasicComparator.SONG))
override fun getAlbumComparator(direction: Direction): Comparator<Album> =
MultiComparator(
compareByDynamic(direction) { it.durationMs }, compareBy(BasicComparator.ALBUM))
override fun getArtistComparator(direction: Direction): Comparator<Artist> =
MultiComparator(
compareByDynamic(direction, NullableComparator.LONG) { it.durationMs },
compareBy(BasicComparator.ARTIST))
override fun getGenreComparator(direction: Direction): Comparator<Genre> =
MultiComparator(
compareByDynamic(direction) { it.durationMs }, compareBy(BasicComparator.GENRE))
override fun getPlaylistComparator(direction: Direction): Comparator<Playlist> =
MultiComparator(
compareByDynamic(direction) { it.durationMs },
compareBy(BasicComparator.PLAYLIST))
override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> songs.sortBy { it.durationMs }
Direction.DESCENDING -> songs.sortByDescending { it.durationMs }
}
}
override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> albums.sortBy { it.durationMs }
Direction.DESCENDING -> albums.sortByDescending { it.durationMs }
}
}
override fun sortArtists(artists: MutableList<Artist>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> artists.sortBy { it.durationMs }
Direction.DESCENDING -> artists.sortByDescending { it.durationMs }
}
}
override fun sortGenres(genres: MutableList<Genre>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> genres.sortBy { it.durationMs }
Direction.DESCENDING -> genres.sortByDescending { it.durationMs }
}
}
override fun sortPlaylists(playlists: MutableList<Playlist>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> playlists.sortBy { it.durationMs }
Direction.DESCENDING -> playlists.sortByDescending { it.durationMs }
}
}
}
/** Sort by the amount of songs an item contains. Only available for MusicParents. */
data object ByCount : Mode {
override val intCode: Int
get() = IntegerTable.SORT_BY_COUNT
override val intCode = IntegerTable.SORT_BY_COUNT
override val stringRes = R.string.lbl_song_count
override val stringRes: Int
get() = R.string.lbl_song_count
override fun getAlbumComparator(direction: Direction): Comparator<Album> =
MultiComparator(
compareByDynamic(direction) { it.songs.size }, compareBy(BasicComparator.ALBUM))
override fun getArtistComparator(direction: Direction): Comparator<Artist> =
MultiComparator(
compareByDynamic(direction, NullableComparator.INT) { it.songs.size },
compareBy(BasicComparator.ARTIST))
override fun getGenreComparator(direction: Direction): Comparator<Genre> =
MultiComparator(
compareByDynamic(direction) { it.songs.size }, compareBy(BasicComparator.GENRE))
override fun getPlaylistComparator(direction: Direction): Comparator<Playlist> =
MultiComparator(
compareByDynamic(direction) { it.songs.size },
compareBy(BasicComparator.PLAYLIST))
override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> albums.sortBy { it.songs.size }
Direction.DESCENDING -> albums.sortByDescending { it.songs.size }
}
}
override fun sortArtists(artists: MutableList<Artist>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> artists.sortBy { it.songs.size }
Direction.DESCENDING -> artists.sortByDescending { it.songs.size }
}
}
override fun sortGenres(genres: MutableList<Genre>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> genres.sortBy { it.songs.size }
Direction.DESCENDING -> genres.sortByDescending { it.songs.size }
}
}
override fun sortPlaylists(playlists: MutableList<Playlist>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> playlists.sortBy { it.songs.size }
Direction.DESCENDING -> playlists.sortByDescending { it.songs.size }
}
}
}
/**
* Sort by the disc number of an item. Only available for [Song]s.
*
* @see Song.disc
*/
data object ByDisc : Mode {
override val intCode: Int
get() = IntegerTable.SORT_BY_DISC
override val intCode = IntegerTable.SORT_BY_DISC
override val stringRes = R.string.lbl_disc
override val stringRes: Int
get() = R.string.lbl_disc
override fun getSongComparator(direction: Direction): Comparator<Song> =
MultiComparator(
compareByDynamic(direction, NullableComparator.DISC) { it.disc },
compareBy(NullableComparator.INT) { it.track },
compareBy(BasicComparator.SONG))
override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
songs.sortBy { it.track }
songs.sortBy { it.name }
when (direction) {
Direction.ASCENDING -> songs.sortBy { it.disc }
Direction.DESCENDING -> songs.sortByDescending { it.disc }
}
}
}
/**
* Sort by the track number of an item. Only available for [Song]s.
*
* @see Song.track
*/
data object ByTrack : Mode {
override val intCode: Int
get() = IntegerTable.SORT_BY_TRACK
override val intCode = IntegerTable.SORT_BY_TRACK
override val stringRes = R.string.lbl_track
override val stringRes: Int
get() = R.string.lbl_track
override fun getSongComparator(direction: Direction): Comparator<Song> =
MultiComparator(
compareBy(NullableComparator.DISC) { it.disc },
compareByDynamic(direction, NullableComparator.INT) { it.track },
compareBy(BasicComparator.SONG))
override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
songs.sortBy { it.name }
songs.sortBy { it.disc }
when (direction) {
Direction.ASCENDING -> songs.sortBy { it.track }
Direction.DESCENDING -> songs.sortByDescending { it.track }
}
}
}
/**
* Sort by the date an item was added. Only supported by [Song]s and [Album]s.
*
* @see Song.dateAdded
* @see Album.dates
*/
data object ByDateAdded : Mode {
override val intCode: Int
get() = IntegerTable.SORT_BY_DATE_ADDED
override val intCode = IntegerTable.SORT_BY_DATE_ADDED
override val stringRes = R.string.lbl_date_added
override val stringRes: Int
get() = R.string.lbl_date_added
override fun sortSongs(songs: MutableList<Song>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> songs.sortBy { it.dateAdded }
Direction.DESCENDING -> songs.sortByDescending { it.dateAdded }
}
}
override fun getSongComparator(direction: Direction): Comparator<Song> =
MultiComparator(
compareByDynamic(direction) { it.dateAdded }, compareBy(BasicComparator.SONG))
override fun getAlbumComparator(direction: Direction): Comparator<Album> =
MultiComparator(
compareByDynamic(direction) { album -> album.dateAdded },
compareBy(BasicComparator.ALBUM))
override fun sortAlbums(albums: MutableList<Album>, direction: Direction) {
when (direction) {
Direction.ASCENDING -> albums.sortBy { it.dateAdded }
Direction.DESCENDING -> albums.sortByDescending { it.dateAdded }
}
}
}
companion object {
/**
* Convert a [Mode] integer representation into an instance.
*
* @param intCode An integer representation of a [Mode]
* @return The corresponding [Mode], or null if the [Mode] is invalid.
* @see intCode
*/
fun fromIntCode(intCode: Int) =
fun fromIntCode(intCode: Int): Mode? =
when (intCode) {
ByName.intCode -> ByName
ByArtist.intCode -> ByArtist
@ -463,166 +399,3 @@ data class Sort(val mode: Mode, val direction: Direction) {
}
}
}
/**
* Utility function to create a [Comparator] in a dynamic way determined by [direction].
*
* @param direction The [Sort.Direction] to sort in.
* @see compareBy
* @see compareByDescending
*/
private inline fun <T : Music, K : Comparable<K>> compareByDynamic(
direction: Sort.Direction,
crossinline selector: (T) -> K
) =
when (direction) {
Sort.Direction.ASCENDING -> compareBy(selector)
Sort.Direction.DESCENDING -> compareByDescending(selector)
}
/**
* Utility function to create a [Comparator] in a dynamic way determined by [direction]
*
* @param direction The [Sort.Direction] to sort in.
* @param comparator A [Comparator] to wrap.
* @return A new [Comparator] with the specified configuration.
* @see compareBy
* @see compareByDescending
*/
private fun <T : Music> compareByDynamic(
direction: Sort.Direction,
comparator: Comparator<in T>
): Comparator<T> = compareByDynamic(direction, comparator) { it }
/**
* Utility function to create a [Comparator] a dynamic way determined by [direction]
*
* @param direction The [Sort.Direction] to sort in.
* @param comparator A [Comparator] to wrap.
* @param selector Called to obtain a specific attribute to sort by.
* @return A new [Comparator] with the specified configuration.
* @see compareBy
* @see compareByDescending
*/
private inline fun <T : Music, K> compareByDynamic(
direction: Sort.Direction,
comparator: Comparator<in K>,
crossinline selector: (T) -> K
) =
when (direction) {
Sort.Direction.ASCENDING -> compareBy(comparator, selector)
Sort.Direction.DESCENDING -> compareByDescending(comparator, selector)
}
/**
* Utility function to create a [Comparator] that sorts in ascending order based on the given
* [Comparator], with a selector based on the item itself.
*
* @param comparator The [Comparator] to wrap.
* @return A new [Comparator] with the specified configuration.
* @see compareBy
*/
private fun <T : Music> compareBy(comparator: Comparator<T>): Comparator<T> =
compareBy(comparator) { it }
/**
* A [Comparator] that chains several other [Comparator]s together to form one comparison.
*
* @param comparators The [Comparator]s to chain. These will be iterated through in order during a
* comparison, with the first non-equal result becoming the result.
*/
private class MultiComparator<T>(vararg comparators: Comparator<T>) : Comparator<T> {
private val _comparators = comparators
override fun compare(a: T?, b: T?): Int {
for (comparator in _comparators) {
val result = comparator.compare(a, b)
if (result != 0) {
return result
}
}
return 0
}
}
/**
* Wraps a [Comparator], extending it to compare two lists.
*
* @param inner The [Comparator] to use.
*/
private class ListComparator<T>(private val inner: Comparator<T>) : Comparator<List<T>> {
override fun compare(a: List<T>, b: List<T>): Int {
for (i in 0 until max(a.size, b.size)) {
val ai = a.getOrNull(i)
val bi = b.getOrNull(i)
when {
ai != null && bi != null -> {
val result = inner.compare(ai, bi)
if (result != 0) {
return result
}
}
ai == null && bi != null -> return -1 // a < b
ai == null && bi == null -> return 0 // a = b
else -> return 1 // a < b
}
}
return 0
}
companion object {
/** A re-usable configured for [Artist]s.. */
val ARTISTS: Comparator<List<Artist>> = ListComparator(BasicComparator.ARTIST)
}
}
/**
* A [Comparator] that compares abstract [Music] values. Internally, this is similar to
* [NullableComparator], however comparing [Music.name] instead of [Comparable].
*
* @see NullableComparator
* @see Music.name
*/
private class BasicComparator<T : Music> private constructor() : Comparator<T> {
override fun compare(a: T, b: T) = a.name.compareTo(b.name)
companion object {
/** A re-usable instance configured for [Song]s. */
val SONG: Comparator<Song> = BasicComparator()
/** A re-usable instance configured for [Album]s. */
val ALBUM: Comparator<Album> = BasicComparator()
/** A re-usable instance configured for [Artist]s. */
val ARTIST: Comparator<Artist> = BasicComparator()
/** A re-usable instance configured for [Genre]s. */
val GENRE: Comparator<Genre> = BasicComparator()
/** A re-usable instance configured for [Playlist]s. */
val PLAYLIST: Comparator<Playlist> = BasicComparator()
}
}
/**
* A [Comparator] that compares two possibly null values. Values will be considered lesser if they
* are null, and greater if they are non-null.
*/
private class NullableComparator<T : Comparable<T>> private constructor() : Comparator<T?> {
override fun compare(a: T?, b: T?) =
when {
a != null && b != null -> a.compareTo(b)
a == null && b != null -> -1 // a < b
a == null && b == null -> 0 // a = b
else -> 1 // a < b
}
companion object {
/** A re-usable instance configured for [Int]s. */
val INT = NullableComparator<Int>()
/** A re-usable instance configured for [Long]s. */
val LONG = NullableComparator<Long>()
/** A re-usable instance configured for [Disc]s */
val DISC = NullableComparator<Disc>()
/** A re-usable instance configured for [Date.Range]s. */
val DATE_RANGE = NullableComparator<Date.Range>()
}
}