Files
Suwayomi-Server/AndroidCompat/src/main/java/xyz/nulldev/androidcompat/db/ScrollableResultSet.kt
T
2021-09-25 04:34:02 +03:30

852 lines
21 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 {
return 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? {
return obj(cachedFindColumn(column))
}
private fun cachedFindColumn(column: String?) =
columnCache.getOrPut(column!!, {
findColumn(column)
})
override fun getNClob(columnIndex: Int): NClob {
return obj(columnIndex) as NClob
}
override fun getNClob(columnLabel: String?): NClob {
return 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 {
return obj(columnIndex) as Boolean
}
override fun getBoolean(columnLabel: String?): Boolean {
return obj(columnLabel) as Boolean
}
override fun isFirst(): Boolean {
return 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 {
return obj(columnIndex) as BigDecimal
}
override fun getBigDecimal(columnLabel: String?): BigDecimal {
return obj(columnLabel) as BigDecimal
}
override fun updateBytes(columnIndex: Int, x: ByteArray?) {
notImplemented()
}
override fun updateBytes(columnLabel: String?, x: ByteArray?) {
notImplemented()
}
override fun isLast(): Boolean {
return 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 {
return 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 {
return obj(columnIndex) as Float
}
override fun getFloat(columnLabel: String?): Float {
return obj(columnLabel) as Float
}
override fun wasNull() = lastReturnWasNull
override fun getRow(): Int {
return 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 {
return obj(columnIndex) as URL
}
override fun getURL(columnLabel: String?): URL {
return 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 {
return obj(columnIndex) as Byte
}
override fun getByte(columnLabel: String?): Byte {
return obj(columnLabel) as Byte
}
override fun getString(columnIndex: Int): String? {
return obj(columnIndex) as String?
}
override fun getString(columnLabel: String?): String? {
return 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? {
return obj(columnIndex)
}
override fun getObject(columnLabel: String?): Any? {
return 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 {
return obj(columnIndex) as T
}
override fun <T : Any?> getObject(columnLabel: String?, type: Class<T>?): T {
return 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 {
return castToLong(obj(columnIndex))
}
override fun getLong(columnLabel: String?): Long {
return 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 {
return obj(columnIndex) as String
}
override fun getNString(columnLabel: String?): String {
return 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 {
return getNCharacterStream(columnIndex)
}
override fun getCharacterStream(columnLabel: String?): Reader {
return getNCharacterStream(columnLabel)
}
override fun isBeforeFirst(): Boolean {
return 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 {
return obj(columnIndex) as Short
}
override fun getShort(columnLabel: String?): Short {
return obj(columnLabel) as Short
}
override fun getAsciiStream(columnIndex: Int): InputStream {
return getBinaryStream(columnIndex)
}
override fun getAsciiStream(columnLabel: String?): InputStream {
return 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 {
return getBinaryStream(columnIndex).reader()
}
override fun getNCharacterStream(columnLabel: String?): Reader {
return 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 {
return obj(columnIndex) as ByteArray
}
override fun getBytes(columnLabel: String?): ByteArray {
return obj(columnLabel) as ByteArray
}
override fun getDouble(columnIndex: Int): Double {
return obj(columnIndex) as Double
}
override fun getDouble(columnLabel: String?): Double {
return obj(columnLabel) as Double
}
override fun getUnicodeStream(columnIndex: Int): InputStream {
return getBinaryStream(columnIndex)
}
override fun getUnicodeStream(columnLabel: String?): InputStream {
return getBinaryStream(columnLabel)
}
override fun rowInserted() = false
private fun thisIsWrapperFor(iface: Class<*>?) = this.javaClass.isInstance(iface)
override fun isWrapperFor(iface: Class<*>?): Boolean {
return thisIsWrapperFor(iface) || parent.isWrapperFor(iface)
}
override fun getInt(columnIndex: Int): Int {
return obj(columnIndex) as Int
}
override fun getInt(columnLabel: String?): Int {
return 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 {
return 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 {
return columnLabels[column - 1]
}
}
}
override fun getBinaryStream(columnIndex: Int): InputStream {
return (obj(columnIndex) as ByteArray).inputStream()
}
override fun getBinaryStream(columnLabel: String?): InputStream {
return (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?>()
}
}