Files
Suwayomi-Server/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/db/ScrollableResultSet.kt
T
2024-09-03 21:37:18 -04:00

1069 lines
23 KiB
Kotlin

package xyz.nulldev.androidcompat.db
import java.io.InputStream
import java.io.Reader
import java.math.BigDecimal
import java.net.URL
import java.sql.Array
import java.sql.Blob
import java.sql.Clob
import java.sql.Date
import java.sql.NClob
import java.sql.Ref
import java.sql.ResultSet
import java.sql.ResultSetMetaData
import java.sql.RowId
import java.sql.SQLXML
import java.sql.Time
import java.sql.Timestamp
import java.util.Calendar
@Suppress("UNCHECKED_CAST")
class ScrollableResultSet(
val parent: ResultSet,
) : ResultSet by parent {
private val cachedContent = mutableListOf<ResultSetEntry>()
private val columnCache = mutableMapOf<String, Int>()
private var lastReturnWasNull = false
private var cursor = 0
var resultSetLength = 0
val parentMetadata = parent.metaData
val columnCount = parentMetadata.columnCount
val columnLabels =
(1..columnCount)
.map {
parentMetadata.getColumnLabel(it)
}.toTypedArray()
init {
val columnCount = columnCount
// TODO
// Profiling reveals that this is a bottleneck (average ms for this call is: 48ms)
// How can we optimize this?
// We need to fill the cache as the set is loaded
// Fill cache
while (parent.next()) {
cachedContent +=
ResultSetEntry().apply {
for (i in 1..columnCount) {
data += parent.getObject(i)
}
}
resultSetLength++
}
}
private fun notImplemented(): Nothing = throw UnsupportedOperationException("This class currently does not support this operation!")
private fun cursorValid(): Boolean = isAfterLast || isBeforeFirst
private fun internalMove(row: Int) {
if (cursor < 0) {
cursor = 0
} else if (cursor > resultSetLength + 1) {
cursor = resultSetLength + 1
} else {
cursor = row
}
}
private fun obj(column: Int): Any? {
val obj = cachedContent[cursor - 1].data[column - 1]
lastReturnWasNull = obj == null
return obj
}
private fun obj(column: String?): Any? = obj(cachedFindColumn(column))
private fun cachedFindColumn(column: String?) =
columnCache.getOrPut(column!!, {
findColumn(column)
})
override fun getNClob(columnIndex: Int): NClob = obj(columnIndex) as NClob
override fun getNClob(columnLabel: String?): NClob = obj(columnLabel) as NClob
override fun updateNString(
columnIndex: Int,
nString: String?,
) {
notImplemented()
}
override fun updateNString(
columnLabel: String?,
nString: String?,
) {
notImplemented()
}
override fun updateBinaryStream(
columnIndex: Int,
x: InputStream?,
length: Int,
) {
notImplemented()
}
override fun updateBinaryStream(
columnLabel: String?,
x: InputStream?,
length: Int,
) {
notImplemented()
}
override fun updateBinaryStream(
columnIndex: Int,
x: InputStream?,
length: Long,
) {
notImplemented()
}
override fun updateBinaryStream(
columnLabel: String?,
x: InputStream?,
length: Long,
) {
notImplemented()
}
override fun updateBinaryStream(
columnIndex: Int,
x: InputStream?,
) {
notImplemented()
}
override fun updateBinaryStream(
columnLabel: String?,
x: InputStream?,
) {
notImplemented()
}
override fun updateTimestamp(
columnIndex: Int,
x: Timestamp?,
) {
notImplemented()
}
override fun updateTimestamp(
columnLabel: String?,
x: Timestamp?,
) {
notImplemented()
}
override fun updateNCharacterStream(
columnIndex: Int,
x: Reader?,
length: Long,
) {
notImplemented()
}
override fun updateNCharacterStream(
columnLabel: String?,
reader: Reader?,
length: Long,
) {
notImplemented()
}
override fun updateNCharacterStream(
columnIndex: Int,
x: Reader?,
) {
notImplemented()
}
override fun updateNCharacterStream(
columnLabel: String?,
reader: Reader?,
) {
notImplemented()
}
override fun updateInt(
columnIndex: Int,
x: Int,
) {
notImplemented()
}
override fun updateInt(
columnLabel: String?,
x: Int,
) {
notImplemented()
}
override fun moveToInsertRow() {
notImplemented()
}
override fun getDate(columnIndex: Int): Date {
// TODO Maybe?
notImplemented()
}
override fun getDate(columnLabel: String?): Date {
// TODO Maybe?
notImplemented()
}
override fun getDate(
columnIndex: Int,
cal: Calendar?,
): Date {
// TODO Maybe?
notImplemented()
}
override fun getDate(
columnLabel: String?,
cal: Calendar?,
): Date {
// TODO Maybe?
notImplemented()
}
override fun beforeFirst() {
// TODO Maybe?
notImplemented()
}
override fun updateFloat(
columnIndex: Int,
x: Float,
) {
notImplemented()
}
override fun updateFloat(
columnLabel: String?,
x: Float,
) {
notImplemented()
}
override fun getBoolean(columnIndex: Int): Boolean = obj(columnIndex) as Boolean
override fun getBoolean(columnLabel: String?): Boolean = obj(columnLabel) as Boolean
override fun isFirst(): Boolean = cursor - 1 < resultSetLength
override fun getBigDecimal(
columnIndex: Int,
scale: Int,
): BigDecimal {
// TODO Maybe?
notImplemented()
}
override fun getBigDecimal(
columnLabel: String?,
scale: Int,
): BigDecimal {
// TODO Maybe?
notImplemented()
}
override fun getBigDecimal(columnIndex: Int): BigDecimal = obj(columnIndex) as BigDecimal
override fun getBigDecimal(columnLabel: String?): BigDecimal = obj(columnLabel) as BigDecimal
override fun updateBytes(
columnIndex: Int,
x: ByteArray?,
) {
notImplemented()
}
override fun updateBytes(
columnLabel: String?,
x: ByteArray?,
) {
notImplemented()
}
override fun isLast(): Boolean = cursor == resultSetLength
override fun insertRow() {
notImplemented()
}
override fun getTime(columnIndex: Int): Time {
// TODO Maybe?
notImplemented()
}
override fun getTime(columnLabel: String?): Time {
// TODO Maybe?
notImplemented()
}
override fun getTime(
columnIndex: Int,
cal: Calendar?,
): Time {
// TODO Maybe?
notImplemented()
}
override fun getTime(
columnLabel: String?,
cal: Calendar?,
): Time {
// TODO Maybe?
notImplemented()
}
override fun rowDeleted() = false
override fun last(): Boolean {
internalMove(resultSetLength)
return cursorValid()
}
override fun isAfterLast(): Boolean = cursor > resultSetLength
override fun relative(rows: Int): Boolean {
internalMove(cursor + rows)
return cursorValid()
}
override fun absolute(row: Int): Boolean {
if (row > 0) {
internalMove(row)
} else {
last()
for (i in 1..row) {
previous()
}
}
return cursorValid()
}
override fun getSQLXML(columnIndex: Int): SQLXML? {
// TODO Maybe?
notImplemented()
}
override fun getSQLXML(columnLabel: String?): SQLXML? {
// TODO Maybe?
notImplemented()
}
override fun <T : Any?> unwrap(iface: Class<T>?): T {
if (thisIsWrapperFor(iface)) {
return this as T
} else {
return parent.unwrap(iface)
}
}
override fun next(): Boolean {
internalMove(cursor + 1)
return cursorValid()
}
override fun getFloat(columnIndex: Int): Float = obj(columnIndex) as Float
override fun getFloat(columnLabel: String?): Float = obj(columnLabel) as Float
override fun wasNull() = lastReturnWasNull
override fun getRow(): Int = cursor
override fun first(): Boolean {
internalMove(1)
return cursorValid()
}
override fun updateAsciiStream(
columnIndex: Int,
x: InputStream?,
length: Int,
) {
notImplemented()
}
override fun updateAsciiStream(
columnLabel: String?,
x: InputStream?,
length: Int,
) {
notImplemented()
}
override fun updateAsciiStream(
columnIndex: Int,
x: InputStream?,
length: Long,
) {
notImplemented()
}
override fun updateAsciiStream(
columnLabel: String?,
x: InputStream?,
length: Long,
) {
notImplemented()
}
override fun updateAsciiStream(
columnIndex: Int,
x: InputStream?,
) {
notImplemented()
}
override fun updateAsciiStream(
columnLabel: String?,
x: InputStream?,
) {
notImplemented()
}
override fun getURL(columnIndex: Int): URL = obj(columnIndex) as URL
override fun getURL(columnLabel: String?): URL = obj(columnLabel) as URL
override fun updateShort(
columnIndex: Int,
x: Short,
) {
notImplemented()
}
override fun updateShort(
columnLabel: String?,
x: Short,
) {
notImplemented()
}
override fun getType() = ResultSet.TYPE_SCROLL_INSENSITIVE
override fun updateNClob(
columnIndex: Int,
nClob: NClob?,
) {
notImplemented()
}
override fun updateNClob(
columnLabel: String?,
nClob: NClob?,
) {
notImplemented()
}
override fun updateNClob(
columnIndex: Int,
reader: Reader?,
length: Long,
) {
notImplemented()
}
override fun updateNClob(
columnLabel: String?,
reader: Reader?,
length: Long,
) {
notImplemented()
}
override fun updateNClob(
columnIndex: Int,
reader: Reader?,
) {
notImplemented()
}
override fun updateNClob(
columnLabel: String?,
reader: Reader?,
) {
notImplemented()
}
override fun updateRef(
columnIndex: Int,
x: Ref?,
) {
notImplemented()
}
override fun updateRef(
columnLabel: String?,
x: Ref?,
) {
notImplemented()
}
override fun updateObject(
columnIndex: Int,
x: Any?,
scaleOrLength: Int,
) {
notImplemented()
}
override fun updateObject(
columnIndex: Int,
x: Any?,
) {
notImplemented()
}
override fun updateObject(
columnLabel: String?,
x: Any?,
scaleOrLength: Int,
) {
notImplemented()
}
override fun updateObject(
columnLabel: String?,
x: Any?,
) {
notImplemented()
}
override fun afterLast() {
internalMove(resultSetLength + 1)
}
override fun updateLong(
columnIndex: Int,
x: Long,
) {
notImplemented()
}
override fun updateLong(
columnLabel: String?,
x: Long,
) {
notImplemented()
}
override fun getBlob(columnIndex: Int): Blob {
// TODO Maybe?
notImplemented()
}
override fun getBlob(columnLabel: String?): Blob {
// TODO Maybe?
notImplemented()
}
override fun updateClob(
columnIndex: Int,
x: Clob?,
) {
notImplemented()
}
override fun updateClob(
columnLabel: String?,
x: Clob?,
) {
notImplemented()
}
override fun updateClob(
columnIndex: Int,
reader: Reader?,
length: Long,
) {
notImplemented()
}
override fun updateClob(
columnLabel: String?,
reader: Reader?,
length: Long,
) {
notImplemented()
}
override fun updateClob(
columnIndex: Int,
reader: Reader?,
) {
notImplemented()
}
override fun updateClob(
columnLabel: String?,
reader: Reader?,
) {
notImplemented()
}
override fun getByte(columnIndex: Int): Byte = obj(columnIndex) as Byte
override fun getByte(columnLabel: String?): Byte = obj(columnLabel) as Byte
override fun getString(columnIndex: Int): String? = obj(columnIndex) as String?
override fun getString(columnLabel: String?): String? = obj(columnLabel) as String?
override fun updateSQLXML(
columnIndex: Int,
xmlObject: SQLXML?,
) {
notImplemented()
}
override fun updateSQLXML(
columnLabel: String?,
xmlObject: SQLXML?,
) {
notImplemented()
}
override fun updateDate(
columnIndex: Int,
x: Date?,
) {
notImplemented()
}
override fun updateDate(
columnLabel: String?,
x: Date?,
) {
notImplemented()
}
override fun getObject(columnIndex: Int): Any? = obj(columnIndex)
override fun getObject(columnLabel: String?): Any? = obj(columnLabel)
override fun getObject(
columnIndex: Int,
map: MutableMap<String, Class<*>>?,
): Any {
// TODO Maybe?
notImplemented()
}
override fun getObject(
columnLabel: String?,
map: MutableMap<String, Class<*>>?,
): Any {
// TODO Maybe?
notImplemented()
}
override fun <T : Any?> getObject(
columnIndex: Int,
type: Class<T>?,
): T = obj(columnIndex) as T
override fun <T : Any?> getObject(
columnLabel: String?,
type: Class<T>?,
): T = obj(columnLabel) as T
override fun previous(): Boolean {
internalMove(cursor - 1)
return cursorValid()
}
override fun updateDouble(
columnIndex: Int,
x: Double,
) {
notImplemented()
}
override fun updateDouble(
columnLabel: String?,
x: Double,
) {
notImplemented()
}
private fun castToLong(obj: Any?): Long {
if (obj == null) {
return 0
} else if (obj is Long) {
return obj
} else if (obj is Number) {
return obj.toLong()
} else {
throw IllegalStateException("Object is not a long!")
}
}
override fun getLong(columnIndex: Int): Long = castToLong(obj(columnIndex))
override fun getLong(columnLabel: String?): Long = castToLong(obj(columnLabel))
override fun getClob(columnIndex: Int): Clob {
// TODO Maybe?
notImplemented()
}
override fun getClob(columnLabel: String?): Clob {
// TODO Maybe?
notImplemented()
}
override fun updateBlob(
columnIndex: Int,
x: Blob?,
) {
notImplemented()
}
override fun updateBlob(
columnLabel: String?,
x: Blob?,
) {
notImplemented()
}
override fun updateBlob(
columnIndex: Int,
inputStream: InputStream?,
length: Long,
) {
notImplemented()
}
override fun updateBlob(
columnLabel: String?,
inputStream: InputStream?,
length: Long,
) {
notImplemented()
}
override fun updateBlob(
columnIndex: Int,
inputStream: InputStream?,
) {
notImplemented()
}
override fun updateBlob(
columnLabel: String?,
inputStream: InputStream?,
) {
notImplemented()
}
override fun updateByte(
columnIndex: Int,
x: Byte,
) {
notImplemented()
}
override fun updateByte(
columnLabel: String?,
x: Byte,
) {
notImplemented()
}
override fun updateRow() {
notImplemented()
}
override fun deleteRow() {
notImplemented()
}
override fun getNString(columnIndex: Int): String = obj(columnIndex) as String
override fun getNString(columnLabel: String?): String = obj(columnLabel) as String
override fun getArray(columnIndex: Int): Array {
// TODO Maybe?
notImplemented()
}
override fun getArray(columnLabel: String?): Array {
// TODO Maybe?
notImplemented()
}
override fun cancelRowUpdates() {
notImplemented()
}
override fun updateString(
columnIndex: Int,
x: String?,
) {
notImplemented()
}
override fun updateString(
columnLabel: String?,
x: String?,
) {
notImplemented()
}
override fun setFetchDirection(direction: Int) {
notImplemented()
}
override fun getCharacterStream(columnIndex: Int): Reader = getNCharacterStream(columnIndex)
override fun getCharacterStream(columnLabel: String?): Reader = getNCharacterStream(columnLabel)
override fun isBeforeFirst(): Boolean = cursor - 1 < resultSetLength
override fun updateBoolean(
columnIndex: Int,
x: Boolean,
) {
notImplemented()
}
override fun updateBoolean(
columnLabel: String?,
x: Boolean,
) {
notImplemented()
}
override fun refreshRow() {
notImplemented()
}
override fun rowUpdated() = false
override fun updateBigDecimal(
columnIndex: Int,
x: BigDecimal?,
) {
notImplemented()
}
override fun updateBigDecimal(
columnLabel: String?,
x: BigDecimal?,
) {
notImplemented()
}
override fun getShort(columnIndex: Int): Short = obj(columnIndex) as Short
override fun getShort(columnLabel: String?): Short = obj(columnLabel) as Short
override fun getAsciiStream(columnIndex: Int): InputStream = getBinaryStream(columnIndex)
override fun getAsciiStream(columnLabel: String?): InputStream = getBinaryStream(columnLabel)
override fun updateTime(
columnIndex: Int,
x: Time?,
) {
notImplemented()
}
override fun updateTime(
columnLabel: String?,
x: Time?,
) {
notImplemented()
}
override fun getTimestamp(columnIndex: Int): Timestamp {
// TODO Maybe?
notImplemented()
}
override fun getTimestamp(columnLabel: String?): Timestamp {
// TODO Maybe?
notImplemented()
}
override fun getTimestamp(
columnIndex: Int,
cal: Calendar?,
): Timestamp {
// TODO Maybe?
notImplemented()
}
override fun getTimestamp(
columnLabel: String?,
cal: Calendar?,
): Timestamp {
// TODO Maybe?
notImplemented()
}
override fun getRef(columnIndex: Int): Ref {
// TODO Maybe?
notImplemented()
}
override fun getRef(columnLabel: String?): Ref {
// TODO Maybe?
notImplemented()
}
override fun getConcurrency() = ResultSet.CONCUR_READ_ONLY
override fun updateRowId(
columnIndex: Int,
x: RowId?,
) {
notImplemented()
}
override fun updateRowId(
columnLabel: String?,
x: RowId?,
) {
notImplemented()
}
override fun getNCharacterStream(columnIndex: Int): Reader = getBinaryStream(columnIndex).reader()
override fun getNCharacterStream(columnLabel: String?): Reader = getBinaryStream(columnLabel).reader()
override fun updateArray(
columnIndex: Int,
x: Array?,
) {
notImplemented()
}
override fun updateArray(
columnLabel: String?,
x: Array?,
) {
notImplemented()
}
override fun getBytes(columnIndex: Int): ByteArray = obj(columnIndex) as ByteArray
override fun getBytes(columnLabel: String?): ByteArray = obj(columnLabel) as ByteArray
override fun getDouble(columnIndex: Int): Double = obj(columnIndex) as Double
override fun getDouble(columnLabel: String?): Double = obj(columnLabel) as Double
override fun getUnicodeStream(columnIndex: Int): InputStream = getBinaryStream(columnIndex)
override fun getUnicodeStream(columnLabel: String?): InputStream = getBinaryStream(columnLabel)
override fun rowInserted() = false
private fun thisIsWrapperFor(iface: Class<*>?) = this.javaClass.isInstance(iface)
override fun isWrapperFor(iface: Class<*>?): Boolean = thisIsWrapperFor(iface) || parent.isWrapperFor(iface)
override fun getInt(columnIndex: Int): Int = obj(columnIndex) as Int
override fun getInt(columnLabel: String?): Int = obj(columnLabel) as Int
override fun updateNull(columnIndex: Int) {
notImplemented()
}
override fun updateNull(columnLabel: String?) {
notImplemented()
}
override fun getRowId(columnIndex: Int): RowId {
// TODO Maybe?
notImplemented()
}
override fun getRowId(columnLabel: String?): RowId {
// TODO Maybe?
notImplemented()
}
override fun getMetaData(): ResultSetMetaData =
object : ResultSetMetaData by parentMetadata {
override fun isReadOnly(column: Int) = true
override fun isWritable(column: Int) = false
override fun isDefinitelyWritable(column: Int) = false
override fun getColumnCount() = this@ScrollableResultSet.columnCount
override fun getColumnLabel(column: Int): String = columnLabels[column - 1]
}
override fun getBinaryStream(columnIndex: Int): InputStream = (obj(columnIndex) as ByteArray).inputStream()
override fun getBinaryStream(columnLabel: String?): InputStream = (obj(columnLabel) as ByteArray).inputStream()
override fun updateCharacterStream(
columnIndex: Int,
x: Reader?,
length: Int,
) {
notImplemented()
}
override fun updateCharacterStream(
columnLabel: String?,
reader: Reader?,
length: Int,
) {
notImplemented()
}
override fun updateCharacterStream(
columnIndex: Int,
x: Reader?,
length: Long,
) {
notImplemented()
}
override fun updateCharacterStream(
columnLabel: String?,
reader: Reader?,
length: Long,
) {
notImplemented()
}
override fun updateCharacterStream(
columnIndex: Int,
x: Reader?,
) {
notImplemented()
}
override fun updateCharacterStream(
columnLabel: String?,
reader: Reader?,
) {
notImplemented()
}
class ResultSetEntry {
val data = mutableListOf<Any?>()
}
}