Verified Commit dab0b0a1 authored by AtjonTV's avatar AtjonTV

FFDB: Implement FlatFile DataBase

* This is a very simple flat file database. It was inspired by the now deprecated flatfile backend of the "SaneEconomy" Spigot Plugin by AppleDash
* This FFDB will become the new Backend for FaradayDB's On-Disk storage; It needs some more work to become the replacement though.
parent de020554
......@@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- JsonKraken 2.0 dependency
- `objectOutputStream` extension for File
- `objectInputStream` extension for File
- FFDB (FlatFile DataBase) (`klib.ffdb.FFDB`)
- IncompatibleDatabaseException (`klib.exceptions.IncompatibleDatabaseException`)
### Changed
- Upgraded Kotlin from 1.3.61 to 1.3.70
### Deprecated
......
package klib.exceptions
/**
* Custom exception
*
* @param current
* @param required
*
* @since 5.0.0
* @author Thomas Obernosterer
*/
class IncompatibleDatabaseException(current: String, required: String) : Exception("Database with version $current cannot perform a version $required action!")
package klib.ffdb
import java.io.File
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import klib.annotations.Experimental
import klib.exceptions.IncompatibleDatabaseException
import klib.extensions.asFile
import klib.extensions.objectInputStream
import klib.extensions.objectOutputStream
/**
* FlatFile DataBase
*
* A simple Database format that stores objects data into one file
*
* @param storageFile The file to read/write from/to
* @param schemaVersion Version of the File (V2 supports ReadAll)
*
* @since 5.0.0 (Experimental)
* @author Thomas Obernosterer
*/
@Experimental
class FFDB(val storageFile: File, val schemaVersion: Int = Version.V2.version) {
val writeBuffer: MutableList<Any> = ArrayList()
companion object {
/**
* Open Database
*
* @param path Path to File
*
* @since 5.0.0
* @author Thomas Obernosterer
*/
fun open(path: String, version: Int = Version.V2.version): FFDB = FFDB(path.asFile(), version)
/**
* Open Database
*
* @param file File handle
*
* @since 5.0.0
* @author Thomas Obernosterer
*/
fun open(
file: File = File.createTempFile("klib.ffdb-database", ".ffdb"),
version: Int = Version.V2.version
): FFDB = FFDB(file, version)
}
/**
* Initialize the Database
*
* + Automatically reads all data into the buffer in case Database Version 2 is used
*
* @since 5.0.0
* @author Thomas Obernosterer
*/
init {
when (schemaVersion) {
Version.V2.version -> {
val data = readAllV2(storageFile.objectInputStream())
data.forEach {
if (it != null) {
writeBuffer.add(it)
}
}
}
}
}
/**
* Add Object to Write Buffer
*
* @since 5.0.0
* @author Thomas Obernosterer
*/
fun write(data: Any) {
writeBuffer.add(data)
}
/**
* Add Array to Write Buffer
*
* @since 5.0.0
* @author Thomas Obernosterer
*/
fun writeAll(data: Array<Any>) {
data.forEach {
writeBuffer.add(it)
}
}
/**
* Add List to Write Buffer
*
* @since 5.0.0
* @author Thomas Obernosterer
*/
fun writeAll(data: List<Any>) {
data.forEach {
writeBuffer.add(it)
}
}
fun remove(predicate: (Any?) -> Boolean) {
TODO("Not Implemented")
}
fun removeAt(index: Int): Any? {
TODO("Not Implemented")
}
/**
* Write objects to Database
*
* @since 5.0.0
* @author Thomas Obernosterer
*/
fun flush() {
when (schemaVersion) {
Version.V1.version -> writeV1(storageFile.objectOutputStream())
Version.V2.version -> writeV2(storageFile.objectOutputStream())
}
}
/**
* Clear the internal write buffer
*
* @since 5.0.0
* @author Thomas Obernosterer
*/
fun clearBuffer() {
writeBuffer.clear()
}
/**
* Read one Object from Database
*
* @since 5.0.0
* @author Thomas Obernosterer
*/
fun readOne(): Any? {
return when (schemaVersion) {
Version.V1.version -> readV1(storageFile.objectInputStream())
Version.V2.version -> readV2(storageFile.objectInputStream())
else -> null
}
}
/**
* Read all Object from Database
*
* :Requires Database V2
*
* @since 5.0.0
* @author Thomas Obernosterer
*/
fun readAll(): List<Any?> {
return when (schemaVersion) {
Version.V1.version -> throw IncompatibleDatabaseException(schemaVersion.toString(), "2")
Version.V2.version -> readAllV2(storageFile.objectInputStream())
else -> ArrayList()
}
}
/**
* Database Version
*
* @param version Version Number as Int
*/
enum class Version(val version: Int) {
V1(1),
V2(2);
}
// ////// Version 1 ////////
private fun writeV1(stream: ObjectOutputStream) = with(stream) {
writeInt(Version.V1.version)
writeBuffer.forEach {
writeObject(it)
}
writeBuffer.clear()
}
private fun readV1(stream: ObjectInputStream): Any = with(stream) {
val ver = readInt()
if (ver == Version.V1.version) {
return readObject()
}
}
// ////// Version 2 ////////
private fun writeV2(stream: ObjectOutputStream) = with(stream) {
writeInt(Version.V2.version)
writeInt(writeBuffer.size)
writeBuffer.forEach {
writeObject(it)
}
writeBuffer.clear()
}
private fun readV2(stream: ObjectInputStream): Any = with(stream) {
val ver = readInt()
// Buffer Size, Ignore this for reading just one
readInt()
if (ver == Version.V2.version) {
return readObject()
}
}
private fun readAllV2(stream: ObjectInputStream): List<Any?> {
val ver = stream.readInt()
var size = stream.readInt()
if (ver == Version.V2.version) {
val objects: MutableList<Any?> = ArrayList()
while (size > 0) {
objects.add(stream.readObject())
size--
}
return objects
}
return ArrayList()
}
}
package devtests
import klib.annotations.Experimental
import klib.ffdb.FFDB
@OptIn(Experimental::class)
fun main() {
val db = FFDB.open("/stmp/testing.ffdb", FFDB.Version.V2.version)
val user = User(1, "Thomas", "Obernosterer", "thomas.obernosterer@atvg-studios.com")
db.write(user)
db.flush()
val data = db.readAll()
data.forEach {
if (it is User) {
println(it.first_name)
}
}
}
package devtests
import java.io.Serializable
import klib.extensions.toListOfType
import klib.kLibInf
import net.jemzart.jsonkraken.JsonArray
data class User(var id: Int, val first_name: String, val last_name: String, val email: String)
data class User(var id: Int, val first_name: String, val last_name: String, val email: String) : Serializable
fun main() {
val jsonData = "[{\n" +
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment