Last active
December 28, 2022 06:27
-
-
Save ToniNgethe/4d2af6374f030c0a0920bacc6c587aa0 to your computer and use it in GitHub Desktop.
A simple key-value plain database for your demo kotlin based projects. Uses .json file for storing the data
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
data class DataBaseVersion( | |
val version: String, | |
val dataModified: String | |
) | |
interface JsonParser { | |
fun <T> parseToString(data: T): String | |
fun <T> parseToObject(string: String, type: Type): T | |
fun <T> parseToObject(string: String, classOfT: Class<T>): T | |
} | |
class JsonParserImpl : JsonParser { | |
private val gson = Gson() | |
override fun <T> parseToString(data: T): String = gson.toJson(data) | |
override fun <T> parseToObject(string: String, type: Type): T { | |
return gson.fromJson(string, type) | |
} | |
override fun <T> parseToObject(string: String, classOfT: Class<T>): T { | |
return gson.fromJson(string, classOfT) | |
} | |
} | |
class Database : JsonParser by JsonParserImpl() { | |
private var dataBaseName = "database" | |
lateinit var databaseFile: File | |
private set | |
class DataBaseBuilder() { | |
private val database = Database() | |
fun setDbName(name: String): DataBaseBuilder { | |
database.dataBaseName = name | |
return this | |
} | |
fun buildDb(): Database { | |
database.syncDatabase() | |
return database | |
} | |
} | |
// check if file exists | |
// 1. if not, create it | |
// 2. if available continue | |
private fun syncDatabase() { | |
try { | |
databaseFile = File("$dataBaseName.json") | |
if (databaseFile.exists()) { | |
println("Database already exists") | |
return | |
} | |
val createFile = databaseFile.createNewFile() | |
if (createFile) { | |
val databaseVersion = DataBaseVersion(version = "1.0.0", dataModified = "${Date().time}") | |
writeToFile("database", parseToString(databaseVersion)) | |
println("Database sync was a success") | |
} else | |
throw Exception("Unable to create database file") | |
} catch (e: Exception) { | |
println("Unable to sync database: ${e.toString()}") | |
} | |
} | |
// convert data of type T to json string | |
// then store in terms of string | |
inline fun <reified T> writeData(key: String, data: T) { | |
try { | |
if (databaseFile == null) { | |
throw Exception("Database file is not yet set up") | |
} | |
writeToFile(key, parseToString(data)) | |
println("Data saved") | |
} catch (e: Exception) { | |
e.printStackTrace() | |
println("Unable to write data") | |
} | |
} | |
inline fun <reified T> getDataUsingKey(key: String): T? { | |
return try { | |
val allData = readAllData() | |
val data = allData[key] | |
if (data != null) { | |
parseToObject(data, T::class.java) | |
} else | |
null | |
} catch (e: Exception) { | |
null | |
} | |
} | |
inline fun <reified T> getListOUsingKey(key: String): ArrayList<T>? { | |
return try { | |
val allData = readAllData() | |
val data = allData[key] | |
if (data != null) { | |
val type = TypeToken.getParameterized(ArrayList::class.java, T::class.java).type | |
Gson().fromJson<ArrayList<T>>(data, type) | |
} else | |
null | |
} catch (e: Exception) { | |
null | |
} | |
} | |
@Synchronized | |
fun writeToFile(key: String, dataToWrite: String) { | |
var fileWriter: OutputStreamWriter? = null | |
var bufferWriter: BufferedWriter? = null | |
try { | |
var previousData = readAllData() | |
previousData[key] = dataToWrite | |
fileWriter = databaseFile.writer() | |
bufferWriter = BufferedWriter(fileWriter) | |
bufferWriter.write(parseToString(previousData)) | |
bufferWriter.flush() | |
} catch (e: Exception) { | |
e.printStackTrace() | |
} finally { | |
try { | |
fileWriter?.close() | |
bufferWriter?.close() | |
} catch (e: Exception) { | |
e.printStackTrace() | |
} | |
} | |
} | |
@Synchronized | |
fun readAllData(): HashMap<String, String> { | |
var bufferedReader: BufferedReader? = null | |
return try { | |
bufferedReader = databaseFile.bufferedReader() | |
val data = bufferedReader.readText() | |
if (data.isEmpty() || data.isBlank()) { | |
HashMap<String, String>() | |
} else { | |
val type = object : TypeToken<HashMap<String, String>>() {}.type | |
parseToObject(data, type) | |
} | |
} catch (e: Exception) { | |
e.printStackTrace() | |
HashMap<String, String>() | |
} finally { | |
bufferedReader?.close() | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DatabaseTest { | |
private lateinit var database: Database | |
@BeforeEach | |
fun setUp() { | |
database = Database.DataBaseBuilder() | |
.setDbName("test") | |
.buildDb() | |
} | |
@AfterEach | |
fun tearDown() { | |
database.databaseFile.delete() | |
} | |
@Test | |
fun `should pass given the database file was created`() { | |
assertNotNull(database.databaseFile) | |
} | |
private val mockData = JsonObject().apply { | |
addProperty("name", "Florence") | |
addProperty("age", "90") | |
} | |
@Test | |
fun `should pass given single data is inserted successfully`() { | |
database.writeData<JsonObject>("user", mockData) | |
val data = database.getDataUsingKey<JsonObject>("user") | |
assertNotNull(data) | |
assertEquals(data?.get("name")?.asString, "Florence") | |
} | |
@Test | |
fun `should return null given no data is available by that key`() { | |
val data = database.getDataUsingKey<JsonObject>("user_1") | |
assertNull(data) | |
} | |
@Test | |
fun `should return list of objects given its available`() { | |
val mockDataList = listOf<JsonObject>(mockData) | |
database.writeData("user_1", mockDataList) | |
val dataList = database.getListOUsingKey<JsonObject>("user_1") | |
assertNotNull( dataList ) | |
assertEquals( dataList?.size, 1 ) | |
assertContains( dataList!!, mockData ) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
data class Person( | |
val name: String, val age: Int | |
) | |
fun main(args: Array<String>) { | |
val database = Database.DataBaseBuilder() | |
.setDbName("bobo_res") | |
.buildDb() | |
database.writeData("person", listOf( Person(name = "toni", age = 22) )) | |
database.writeData("user", Person(name = "toni", age = 22)) | |
val user = database.getDataUsingKey<Person>("user") | |
println( user?.name ) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment