mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-05 08:38:56 +01:00
Add 8muses
This commit is contained in:
@@ -20,6 +20,7 @@ val HENTAI_CAFE_SOURCE_ID = delegatedSourceId<HentaiCafe>()
|
||||
val PURURIN_SOURCE_ID = delegatedSourceId<Pururin>()
|
||||
const val TSUMINO_SOURCE_ID = LEWD_SOURCE_SERIES + 9
|
||||
const val HITOMI_SOURCE_ID = LEWD_SOURCE_SERIES + 10
|
||||
const val EIGHTMUSES_SOURCE_ID = LEWD_SOURCE_SERIES + 11
|
||||
const val MERGED_SOURCE_ID = LEWD_SOURCE_SERIES + 69
|
||||
|
||||
private val DELEGATED_LEWD_SOURCES = listOf(
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package exh.metadata.metadata
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.SManga
|
||||
import exh.metadata.metadata.base.RaisedSearchMetadata
|
||||
import exh.plusAssign
|
||||
|
||||
class EightMusesSearchMetadata : RaisedSearchMetadata() {
|
||||
var path: List<String> = emptyList()
|
||||
|
||||
var title by titleDelegate(TITLE_TYPE_MAIN)
|
||||
|
||||
var thumbnailUrl: String? = null
|
||||
|
||||
override fun copyTo(manga: SManga) {
|
||||
manga.url = path.joinToString("/", prefix = "/")
|
||||
|
||||
title?.let {
|
||||
manga.title = it
|
||||
}
|
||||
|
||||
thumbnailUrl?.let {
|
||||
manga.thumbnail_url = it
|
||||
}
|
||||
|
||||
manga.artist = tags.ofNamespace(ARTIST_NAMESPACE).joinToString { it.name }
|
||||
|
||||
manga.genre = tagsToGenreString()
|
||||
|
||||
val titleDesc = StringBuilder()
|
||||
title?.let { titleDesc += "Title: $it\n" }
|
||||
|
||||
val tagsDesc = tagsToDescription()
|
||||
|
||||
manga.description = listOf(titleDesc.toString(), tagsDesc.toString())
|
||||
.filter(String::isNotBlank)
|
||||
.joinToString(separator = "\n")
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TITLE_TYPE_MAIN = 0
|
||||
|
||||
const val TAG_TYPE_DEFAULT = 0
|
||||
|
||||
const val BASE_URL = "https://www.8muses.com"
|
||||
|
||||
const val TAGS_NAMESPACE = "tags"
|
||||
const val ARTIST_NAMESPACE = "artist"
|
||||
}
|
||||
}
|
||||
24
app/src/main/java/exh/util/CachedField.kt
Normal file
24
app/src/main/java/exh/util/CachedField.kt
Normal file
@@ -0,0 +1,24 @@
|
||||
package exh.util
|
||||
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
|
||||
class CachedField<T>(private val expiresAfterMs: Long) {
|
||||
@Volatile
|
||||
private var initTime: Long = -1
|
||||
|
||||
@Volatile
|
||||
private var content: T? = null
|
||||
|
||||
private val mutex = Mutex()
|
||||
|
||||
suspend fun obtain(producer: suspend () -> T): T {
|
||||
return mutex.withLock {
|
||||
if(initTime < 0 || System.currentTimeMillis() - initTime > expiresAfterMs) {
|
||||
content = producer()
|
||||
}
|
||||
|
||||
content as T
|
||||
}
|
||||
}
|
||||
}
|
||||
174
app/src/main/java/exh/util/FakeMutables.kt
Normal file
174
app/src/main/java/exh/util/FakeMutables.kt
Normal file
@@ -0,0 +1,174 @@
|
||||
package exh.util
|
||||
|
||||
// Zero-allocation-overhead mutable collection shims
|
||||
|
||||
private inline class CollectionShim<E>(private val coll: Collection<E>) : FakeMutableCollection<E> {
|
||||
override val size: Int get() = coll.size
|
||||
|
||||
override fun contains(element: E) = coll.contains(element)
|
||||
|
||||
override fun containsAll(elements: Collection<E>) = coll.containsAll(elements)
|
||||
|
||||
override fun isEmpty() = coll.isEmpty()
|
||||
|
||||
override fun fakeIterator() = coll.iterator()
|
||||
}
|
||||
|
||||
interface FakeMutableCollection<E> : MutableCollection<E>, FakeMutableIterable<E> {
|
||||
override fun add(element: E): Boolean {
|
||||
throw UnsupportedOperationException("This collection is immutable!")
|
||||
}
|
||||
|
||||
override fun addAll(elements: Collection<E>): Boolean {
|
||||
throw UnsupportedOperationException("This collection is immutable!")
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
throw UnsupportedOperationException("This collection is immutable!")
|
||||
}
|
||||
|
||||
override fun remove(element: E): Boolean {
|
||||
throw UnsupportedOperationException("This collection is immutable!")
|
||||
}
|
||||
|
||||
override fun removeAll(elements: Collection<E>): Boolean {
|
||||
throw UnsupportedOperationException("This collection is immutable!")
|
||||
}
|
||||
|
||||
override fun retainAll(elements: Collection<E>): Boolean {
|
||||
throw UnsupportedOperationException("This collection is immutable!")
|
||||
}
|
||||
|
||||
override fun iterator(): MutableIterator<E> = super.iterator()
|
||||
|
||||
companion object {
|
||||
fun <E> fromCollection(coll: Collection<E>): FakeMutableCollection<E> = CollectionShim(coll)
|
||||
}
|
||||
}
|
||||
|
||||
private inline class SetShim<E>(private val set: Set<E>) : FakeMutableSet<E> {
|
||||
override val size: Int get() = set.size
|
||||
|
||||
override fun contains(element: E) = set.contains(element)
|
||||
|
||||
override fun containsAll(elements: Collection<E>) = set.containsAll(elements)
|
||||
|
||||
override fun isEmpty() = set.isEmpty()
|
||||
|
||||
override fun fakeIterator() = set.iterator()
|
||||
}
|
||||
|
||||
interface FakeMutableSet<E> : MutableSet<E>, FakeMutableCollection<E> {
|
||||
/**
|
||||
* Adds the specified element to the set.
|
||||
*
|
||||
* @return `true` if the element has been added, `false` if the element is already contained in the set.
|
||||
*/
|
||||
override fun add(element: E): Boolean = super.add(element)
|
||||
|
||||
override fun addAll(elements: Collection<E>): Boolean = super.addAll(elements)
|
||||
|
||||
override fun clear() = super.clear()
|
||||
|
||||
override fun remove(element: E): Boolean = super.remove(element)
|
||||
|
||||
override fun removeAll(elements: Collection<E>): Boolean = super.removeAll(elements)
|
||||
|
||||
override fun retainAll(elements: Collection<E>): Boolean = super.retainAll(elements)
|
||||
|
||||
override fun iterator(): MutableIterator<E> = super.iterator()
|
||||
|
||||
companion object {
|
||||
fun <E> fromSet(set: Set<E>): FakeMutableSet<E> = SetShim(set)
|
||||
}
|
||||
}
|
||||
|
||||
private inline class IterableShim<E>(private val iterable: Iterable<E>) : FakeMutableIterable<E> {
|
||||
override fun fakeIterator() = iterable.iterator()
|
||||
}
|
||||
|
||||
interface FakeMutableIterable<E> : MutableIterable<E> {
|
||||
/**
|
||||
* Returns an iterator over the elements of this sequence that supports removing elements during iteration.
|
||||
*/
|
||||
override fun iterator(): MutableIterator<E> = FakeMutableIterator.fromIterator(fakeIterator())
|
||||
|
||||
fun fakeIterator(): Iterator<E>
|
||||
|
||||
companion object {
|
||||
fun <E> fromIterable(iterable: Iterable<E>): FakeMutableIterable<E> = IterableShim(iterable)
|
||||
}
|
||||
}
|
||||
|
||||
private inline class IteratorShim<E>(private val iterator: Iterator<E>) : FakeMutableIterator<E> {
|
||||
/**
|
||||
* Returns `true` if the iteration has more elements.
|
||||
*/
|
||||
override fun hasNext() = iterator.hasNext()
|
||||
|
||||
/**
|
||||
* Returns the next element in the iteration.
|
||||
*/
|
||||
override fun next() = iterator.next()
|
||||
}
|
||||
|
||||
|
||||
interface FakeMutableIterator<E> : MutableIterator<E> {
|
||||
/**
|
||||
* Removes from the underlying collection the last element returned by this iterator.
|
||||
*/
|
||||
override fun remove() {
|
||||
throw UnsupportedOperationException("This set is immutable!")
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun <E> fromIterator(iterator: Iterator<E>) : FakeMutableIterator<E> = IteratorShim(iterator)
|
||||
}
|
||||
}
|
||||
|
||||
private inline class EntryShim<K, V>(private val entry: Map.Entry<K, V>) : FakeMutableEntry<K, V> {
|
||||
/**
|
||||
* Returns the key of this key/value pair.
|
||||
*/
|
||||
override val key: K
|
||||
get() = entry.key
|
||||
/**
|
||||
* Returns the value of this key/value pair.
|
||||
*/
|
||||
override val value: V
|
||||
get() = entry.value
|
||||
}
|
||||
|
||||
private inline class PairShim<K, V>(private val pair: Pair<K, V>) : FakeMutableEntry<K, V> {
|
||||
/**
|
||||
* Returns the key of this key/value pair.
|
||||
*/
|
||||
override val key: K get() = pair.first
|
||||
/**
|
||||
* Returns the value of this key/value pair.
|
||||
*/
|
||||
override val value: V get() = pair.second
|
||||
}
|
||||
|
||||
interface FakeMutableEntry<K, V> : MutableMap.MutableEntry<K, V> {
|
||||
override fun setValue(newValue: V): V {
|
||||
throw UnsupportedOperationException("This entry is immutable!")
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun <K, V> fromEntry(entry: Map.Entry<K, V>): FakeMutableEntry<K, V> = EntryShim(entry)
|
||||
|
||||
fun <K, V> fromPair(pair: Pair<K, V>): FakeMutableEntry<K, V> = PairShim(pair)
|
||||
|
||||
fun <K, V> fromPair(key: K, value: V) = object : FakeMutableEntry<K, V> {
|
||||
/**
|
||||
* Returns the key of this key/value pair.
|
||||
*/
|
||||
override val key: K = key
|
||||
/**
|
||||
* Returns the value of this key/value pair.
|
||||
*/
|
||||
override val value: V = value
|
||||
}
|
||||
}
|
||||
}
|
||||
345
app/src/main/java/exh/util/NakedTrie.kt
Normal file
345
app/src/main/java/exh/util/NakedTrie.kt
Normal file
@@ -0,0 +1,345 @@
|
||||
package exh.util
|
||||
|
||||
import android.util.SparseArray
|
||||
import java.util.*
|
||||
|
||||
class NakedTrieNode<T>(val key: Int, var parent: NakedTrieNode<T>?) {
|
||||
val children = SparseArray<NakedTrieNode<T>>(1)
|
||||
var hasData: Boolean = false
|
||||
var data: T? = null
|
||||
|
||||
// Walks in ascending order
|
||||
// Consumer should return true to continue walking, false to stop walking
|
||||
inline fun walk(prefix: String, consumer: (String, T) -> Boolean, leavesOnly: Boolean) {
|
||||
// Special case root
|
||||
if(hasData && (!leavesOnly || children.size() <= 0)) {
|
||||
if(!consumer(prefix, data as T)) return
|
||||
}
|
||||
|
||||
val stack = LinkedList<Pair<String, NakedTrieNode<T>>>()
|
||||
SparseArrayValueCollection(children, true).forEach {
|
||||
stack += prefix + it.key.toChar() to it
|
||||
}
|
||||
while(!stack.isEmpty()) {
|
||||
val (key, bottom) = stack.removeLast()
|
||||
SparseArrayValueCollection(bottom.children, true).forEach {
|
||||
stack += key + it.key.toChar() to it
|
||||
}
|
||||
if(bottom.hasData && (!leavesOnly || bottom.children.size() <= 0)) {
|
||||
if(!consumer(key, bottom.data as T)) return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getAsNode(key: String): NakedTrieNode<T>? {
|
||||
var current = this
|
||||
for(c in key) {
|
||||
current = current.children.get(c.toInt()) ?: return null
|
||||
if(!current.hasData) return null
|
||||
}
|
||||
return current
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast, memory efficient and flexible trie implementation with implementation details exposed
|
||||
*/
|
||||
class NakedTrie<T> : MutableMap<String, T> {
|
||||
/**
|
||||
* Returns the number of key/value pairs in the map.
|
||||
*/
|
||||
override var size: Int = 0
|
||||
private set
|
||||
|
||||
/**
|
||||
* Returns `true` if the map is empty (contains no elements), `false` otherwise.
|
||||
*/
|
||||
override fun isEmpty() = size <= 0
|
||||
|
||||
/**
|
||||
* Removes all elements from this map.
|
||||
*/
|
||||
override fun clear() {
|
||||
root.children.clear()
|
||||
root.hasData = false
|
||||
root.data = null
|
||||
size = 0
|
||||
}
|
||||
|
||||
val root = NakedTrieNode<T>(-1, null)
|
||||
private var version: Long = 0
|
||||
|
||||
override fun put(key: String, value: T): T? {
|
||||
// Traverse to node location in tree, making parent nodes if required
|
||||
var current = root
|
||||
for(c in key) {
|
||||
val castedC = c.toInt()
|
||||
var node = current.children.get(castedC)
|
||||
if(node == null) {
|
||||
node = NakedTrieNode(castedC, current)
|
||||
current.children.put(castedC, node)
|
||||
}
|
||||
current = node
|
||||
}
|
||||
|
||||
// Add data to node or replace existing data
|
||||
val previous = if(current.hasData) {
|
||||
current.data
|
||||
} else {
|
||||
current.hasData = true
|
||||
size++
|
||||
null
|
||||
}
|
||||
current.data = value
|
||||
|
||||
version++
|
||||
|
||||
return previous
|
||||
}
|
||||
|
||||
override fun get(key: String): T? {
|
||||
val current = getAsNode(key) ?: return null
|
||||
return if(current.hasData) current.data else null
|
||||
}
|
||||
|
||||
fun getAsNode(key: String): NakedTrieNode<T>? {
|
||||
return root.getAsNode(key)
|
||||
}
|
||||
|
||||
override fun containsKey(key: String): Boolean {
|
||||
var current = root
|
||||
for(c in key) {
|
||||
current = current.children.get(c.toInt()) ?: return false
|
||||
if(!current.hasData) return false
|
||||
}
|
||||
return current.hasData
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified key and its corresponding value from this map.
|
||||
*
|
||||
* @return the previous value associated with the key, or `null` if the key was not present in the map.
|
||||
*/
|
||||
override fun remove(key: String): T? {
|
||||
// Traverse node tree while keeping track of the nodes we have visited
|
||||
val nodeStack = LinkedList<NakedTrieNode<T>>()
|
||||
for(c in key) {
|
||||
val bottomOfStack = nodeStack.last
|
||||
val current = bottomOfStack.children.get(c.toInt()) ?: return null
|
||||
if(!current.hasData) return null
|
||||
nodeStack.add(bottomOfStack)
|
||||
}
|
||||
|
||||
// Mark node as having no data
|
||||
val bottomOfStack = nodeStack.last
|
||||
bottomOfStack.hasData = false
|
||||
val oldData = bottomOfStack.data
|
||||
bottomOfStack.data = null // Clear data field for GC
|
||||
|
||||
// Remove nodes that we visited that are useless
|
||||
for(curBottom in nodeStack.descendingIterator()) {
|
||||
val parent = curBottom.parent ?: break
|
||||
if(!curBottom.hasData && curBottom.children.size() <= 0) {
|
||||
// No data or child nodes, this node is useless, discard
|
||||
parent.children.remove(curBottom.key)
|
||||
} else break
|
||||
}
|
||||
|
||||
version++
|
||||
size--
|
||||
|
||||
return oldData
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this map with key/value pairs from the specified map [from].
|
||||
*/
|
||||
override fun putAll(from: Map<out String, T>) {
|
||||
// No way to optimize this so yeah...
|
||||
from.forEach { (s, u) ->
|
||||
put(s, u)
|
||||
}
|
||||
}
|
||||
|
||||
// Walks in ascending order
|
||||
// Consumer should return true to continue walking, false to stop walking
|
||||
inline fun walk(consumer: (String, T) -> Boolean) {
|
||||
walk(consumer, false)
|
||||
}
|
||||
|
||||
// Walks in ascending order
|
||||
// Consumer should return true to continue walking, false to stop walking
|
||||
inline fun walk(consumer: (String, T) -> Boolean, leavesOnly: Boolean) {
|
||||
root.walk("", consumer, leavesOnly)
|
||||
}
|
||||
|
||||
fun getOrPut(key: String, producer: () -> T): T {
|
||||
// Traverse to node location in tree, making parent nodes if required
|
||||
var current = root
|
||||
for(c in key) {
|
||||
val castedC = c.toInt()
|
||||
var node = current.children.get(castedC)
|
||||
if(node == null) {
|
||||
node = NakedTrieNode(castedC, current)
|
||||
current.children.put(castedC, node)
|
||||
}
|
||||
current = node
|
||||
}
|
||||
|
||||
// Add data to node or replace existing data
|
||||
if(!current.hasData) {
|
||||
current.hasData = true
|
||||
current.data = producer()
|
||||
size++
|
||||
version++
|
||||
}
|
||||
|
||||
return current.data as T
|
||||
}
|
||||
|
||||
// Includes root
|
||||
fun subMap(prefix: String, leavesOnly: Boolean = false): Map<String, T> {
|
||||
val node = getAsNode(prefix) ?: return emptyMap()
|
||||
|
||||
return object : Map<String, T> {
|
||||
/**
|
||||
* Returns a read-only [Set] of all key/value pairs in this map.
|
||||
*/
|
||||
override val entries: Set<Map.Entry<String, T>>
|
||||
get() {
|
||||
val out = mutableSetOf<Map.Entry<String, T>>()
|
||||
node.walk("", { k, v ->
|
||||
out.add(AbstractMap.SimpleImmutableEntry(k, v))
|
||||
true
|
||||
}, leavesOnly)
|
||||
return out
|
||||
}
|
||||
/**
|
||||
* Returns a read-only [Set] of all keys in this map.
|
||||
*/
|
||||
override val keys: Set<String>
|
||||
get() {
|
||||
val out = mutableSetOf<String>()
|
||||
node.walk("", { k, _ ->
|
||||
out.add(k)
|
||||
true
|
||||
}, leavesOnly)
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of key/value pairs in the map.
|
||||
*/
|
||||
override val size: Int get() {
|
||||
var s = 0
|
||||
node.walk("", { _, _ -> s++; true }, leavesOnly)
|
||||
return s
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a read-only [Collection] of all values in this map. Note that this collection may contain duplicate values.
|
||||
*/
|
||||
override val values: Collection<T>
|
||||
get() {
|
||||
val out = mutableSetOf<T>()
|
||||
node.walk("", { _, v ->
|
||||
out.add(v)
|
||||
true
|
||||
}, leavesOnly)
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the map contains the specified [key].
|
||||
*/
|
||||
override fun containsKey(key: String): Boolean {
|
||||
if(!key.startsWith(prefix)) return false
|
||||
|
||||
val childNode = node.getAsNode(key.removePrefix(prefix)) ?: return false
|
||||
return childNode.hasData && (!leavesOnly || childNode.children.size() <= 0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the map maps one or more keys to the specified [value].
|
||||
*/
|
||||
override fun containsValue(value: T): Boolean {
|
||||
node.walk("", { _, v ->
|
||||
if(v == value) return true
|
||||
true
|
||||
}, leavesOnly)
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value corresponding to the given [key], or `null` if such a key is not present in the map.
|
||||
*/
|
||||
override fun get(key: String): T? {
|
||||
if(!key.startsWith(prefix)) return null
|
||||
|
||||
val childNode = node.getAsNode(key.removePrefix(prefix)) ?: return null
|
||||
if(!childNode.hasData || (leavesOnly && childNode.children.size() > 0)) return null
|
||||
return childNode.data
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the map is empty (contains no elements), `false` otherwise.
|
||||
*/
|
||||
override fun isEmpty(): Boolean {
|
||||
if(node.children.size() <= 0 && !root.hasData) return true
|
||||
if(!leavesOnly) return false
|
||||
node.walk("", { _, _ -> return false }, leavesOnly)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Slow methods below
|
||||
|
||||
/**
|
||||
* Returns `true` if the map maps one or more keys to the specified [value].
|
||||
*/
|
||||
override fun containsValue(value: T): Boolean {
|
||||
walk { _, t ->
|
||||
if(t == value) {
|
||||
return true
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [MutableSet] of all key/value pairs in this map.
|
||||
*/
|
||||
override val entries: MutableSet<MutableMap.MutableEntry<String, T>>
|
||||
get() = FakeMutableSet.fromSet(mutableSetOf<MutableMap.MutableEntry<String, T>>().apply {
|
||||
walk { k, v ->
|
||||
this += FakeMutableEntry.fromPair(k, v)
|
||||
true
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Returns a [MutableSet] of all keys in this map.
|
||||
*/
|
||||
override val keys: MutableSet<String>
|
||||
get() = FakeMutableSet.fromSet(mutableSetOf<String>().apply {
|
||||
walk { k, _ ->
|
||||
this += k
|
||||
true
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Returns a [MutableCollection] of all values in this map. Note that this collection may contain duplicate values.
|
||||
*/
|
||||
override val values: MutableCollection<T>
|
||||
get() = FakeMutableCollection.fromCollection(mutableListOf<T>().apply {
|
||||
walk { _, v ->
|
||||
this += v
|
||||
true
|
||||
}
|
||||
})
|
||||
}
|
||||
73
app/src/main/java/exh/util/SparseArrayCollection.kt
Normal file
73
app/src/main/java/exh/util/SparseArrayCollection.kt
Normal file
@@ -0,0 +1,73 @@
|
||||
package exh.util
|
||||
|
||||
import android.util.SparseArray
|
||||
import java.util.AbstractMap
|
||||
|
||||
class SparseArrayKeyCollection(val sparseArray: SparseArray<out Any?>, var reverse: Boolean = false): AbstractCollection<Int>() {
|
||||
override val size get() = sparseArray.size()
|
||||
|
||||
override fun iterator() = object : Iterator<Int> {
|
||||
private var index: Int = 0
|
||||
|
||||
/**
|
||||
* Returns `true` if the iteration has more elements.
|
||||
*/
|
||||
override fun hasNext() = index < sparseArray.size()
|
||||
|
||||
/**
|
||||
* Returns the next element in the iteration.
|
||||
*/
|
||||
override fun next(): Int {
|
||||
var idx = index++
|
||||
if(reverse) idx = sparseArray.size() - 1 - idx
|
||||
return sparseArray.keyAt(idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SparseArrayValueCollection<E>(val sparseArray: SparseArray<E>, var reverse: Boolean = false): AbstractCollection<E>() {
|
||||
override val size get() = sparseArray.size()
|
||||
|
||||
override fun iterator() = object : Iterator<E> {
|
||||
private var index: Int = 0
|
||||
|
||||
/**
|
||||
* Returns `true` if the iteration has more elements.
|
||||
*/
|
||||
override fun hasNext() = index < sparseArray.size()
|
||||
|
||||
/**
|
||||
* Returns the next element in the iteration.
|
||||
*/
|
||||
override fun next(): E {
|
||||
var idx = index++
|
||||
if(reverse) idx = sparseArray.size() - 1 - idx
|
||||
return sparseArray.valueAt(idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SparseArrayCollection<E>(val sparseArray: SparseArray<E>, var reverse: Boolean = false): AbstractCollection<Map.Entry<Int, E>>() {
|
||||
override val size get() = sparseArray.size()
|
||||
|
||||
override fun iterator() = object : Iterator<Map.Entry<Int, E>> {
|
||||
private var index: Int = 0
|
||||
|
||||
/**
|
||||
* Returns `true` if the iteration has more elements.
|
||||
*/
|
||||
override fun hasNext() = index < sparseArray.size()
|
||||
|
||||
/**
|
||||
* Returns the next element in the iteration.
|
||||
*/
|
||||
override fun next(): Map.Entry<Int, E> {
|
||||
var idx = index++
|
||||
if(reverse) idx = sparseArray.size() - 1 - idx
|
||||
return AbstractMap.SimpleImmutableEntry(
|
||||
sparseArray.keyAt(idx),
|
||||
sparseArray.valueAt(idx)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user