diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index fe63bb6..53bf319 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 696dbe6..c236054 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,5 +1,5 @@
plugins {
- kotlin("jvm") version "1.9.23"
+ kotlin("jvm") version "2.0.0-RC2"
id("maven-publish")
id("signing")
}
diff --git a/src/main/kotlin/nl/astraeus/persistence/Datastore.kt b/src/main/kotlin/nl/astraeus/persistence/Datastore.kt
index 6f3459b..53edd1e 100644
--- a/src/main/kotlin/nl/astraeus/persistence/Datastore.kt
+++ b/src/main/kotlin/nl/astraeus/persistence/Datastore.kt
@@ -44,7 +44,7 @@ class Datastore(
private val fileManager = FileManager(directory)
private val transactionFormatter = DecimalFormat("#")
private var nextTransactionNumber = 1L
- private val data: MutableMap, TypeData> = ConcurrentHashMap()
+ private var data: MutableMap, TypeData> = ConcurrentHashMap()
private val indexes: MutableMap, MutableMap> = ConcurrentHashMap()
init {
@@ -66,6 +66,18 @@ class Datastore(
}
}
+ return data[javaClass]!!.nextId.get()
+ }
+
+ fun getNextIdAndIncrement(javaClass: Class): Long {
+ if (data[javaClass] == null) {
+ synchronized(this) {
+ if (data[javaClass] == null) {
+ data[javaClass] = TypeData()
+ }
+ }
+ }
+
return data[javaClass]!!.nextId.getAndIncrement()
}
@@ -255,8 +267,7 @@ class Datastore(
val versionNumber = ois.readInt()
check(versionNumber == 1) { "Unsupported version number: $versionNumber" }
nextTransactionNumber = ois.readLong() + 1
- data.clear()
- data.putAll(ois.readObject() as MutableMap, TypeData>)
+ data = ois.readObject() as MutableMap, TypeData>
val foundIndexes = mutableMapOf, MutableList>()
val numberOfClassesWithIndex = ois.readInt()
diff --git a/src/main/kotlin/nl/astraeus/persistence/Reference.kt b/src/main/kotlin/nl/astraeus/persistence/Reference.kt
deleted file mode 100644
index 4d0c144..0000000
--- a/src/main/kotlin/nl/astraeus/persistence/Reference.kt
+++ /dev/null
@@ -1,133 +0,0 @@
-package nl.astraeus.nl.astraeus.persistence
-
-import java.io.Serializable
-import kotlin.reflect.KProperty
-
-class Reference(
- val cls: Class,
-) : Serializable {
- companion object {
- private const val serialVersionUID: Long = 1L
- }
-
- var id: Long = 0
-
- operator fun getValue(thisRef: H, property: KProperty<*>): S {
- return currentTransaction()?.find(cls.kotlin, id) ?: throw IllegalStateException("Reference not found")
- }
-
- operator fun setValue(thisRef: H, property: KProperty<*>, value: S) {
- id = value.id
- // todo: only store if not already stored?
- currentTransaction()?.store(value)
- }
-}
-
-class ListReference(
- val cls: Class,
-) : Serializable {
- companion object {
- private const val serialVersionUID: Long = 1L
- }
-
- var ids: ReferenceList = ReferenceList(cls)
-
- operator fun getValue(thisRef: H, property: KProperty<*>): ReferenceList {
- return ids
- }
-
- operator fun setValue(thisRef: H, property: KProperty<*>, value: List) {
- this.ids.clear()
- this.ids.addAll(value)
- }
-}
-
-class ReferenceList(
- val cls: Class,
-) : MutableList {
- val ids = ArrayList()
-
- private fun checkElementIsPersisted(element: T) {
- if (currentTransaction()?.find(cls.kotlin, element.id) == null) {
- currentTransaction()?.store(element)
- }
- }
-
- override val size: Int = ids.size
- override fun clear() = ids.clear()
-
- override fun addAll(elements: Collection): Boolean {
- TODO("Not yet implemented")
- }
-
- override fun addAll(index: Int, elements: Collection): Boolean {
- TODO("Not yet implemented")
- }
-
- override fun add(index: Int, element: T) {
- ids.add(index, element.id)
- }
-
- override fun add(element: T): Boolean {
- return ids.add(element.id)
- }
-
- override fun get(index: Int): T = currentTransaction()?.find(cls.kotlin, ids[index]) ?: throw IllegalStateException("Reference not found")
-
- override fun isEmpty(): Boolean = ids.isEmpty()
-
- override fun iterator(): MutableIterator {
- TODO("Not yet implemented")
- }
-
- override fun listIterator(): MutableListIterator {
- TODO("Not yet implemented")
- }
-
- override fun listIterator(index: Int): MutableListIterator {
- TODO("Not yet implemented")
- }
-
- override fun removeAt(index: Int): T {
- val id = ids.removeAt(index)
-
- return currentTransaction()?.find(cls.kotlin, id) ?: throw IllegalStateException("Reference not found")
- }
-
- override fun set(index: Int, element: T): T {
- TODO("Not yet implemented")
- }
-
- override fun retainAll(elements: Collection): Boolean {
- TODO("Not yet implemented")
- }
-
- override fun removeAll(elements: Collection): Boolean {
- TODO("Not yet implemented")
- }
-
- override fun remove(element: T): Boolean {
- TODO("Not yet implemented")
- }
-
- override fun subList(fromIndex: Int, toIndex: Int): MutableList {
- TODO("Not yet implemented")
- }
-
- override fun lastIndexOf(element: T): Int {
- TODO("Not yet implemented")
- }
-
- override fun indexOf(element: T): Int {
- TODO("Not yet implemented")
- }
-
- override fun containsAll(elements: Collection): Boolean {
- TODO("Not yet implemented")
- }
-
- override fun contains(element: T): Boolean {
- TODO("Not yet implemented")
- }
-
-}
\ No newline at end of file
diff --git a/src/main/kotlin/nl/astraeus/persistence/Transaction.kt b/src/main/kotlin/nl/astraeus/persistence/Transaction.kt
index 113301a..3a9c388 100644
--- a/src/main/kotlin/nl/astraeus/persistence/Transaction.kt
+++ b/src/main/kotlin/nl/astraeus/persistence/Transaction.kt
@@ -5,6 +5,7 @@ import kotlin.reflect.KClass
inline fun Query.count(): Int = this.count(T::class)
inline fun Query.find(id: Long): T? = this.find(T::class, id)
+inline fun Query.all(): List = this.all(T::class)
inline fun Query.search(noinline search: (T) -> Boolean): List =
this.search(T::class, search)
inline fun Query.findByIndex(
@@ -42,6 +43,9 @@ open class Query(
open fun find(clazz: KClass, id: Long): T? = persistent.datastore.find(clazz, id)
+ open fun all(clazz: Class): List = all(clazz.kotlin)
+ open fun all(clazz: KClass): List = search(clazz) { _ -> true }
+
open fun search(clazz: KClass, search: (T) -> Boolean): List = persistent.datastore.search(clazz, search)
fun findByIndex(
@@ -99,7 +103,7 @@ class Transaction(
fun store(obj: Persistable) {
if (obj.id == 0L) {
- obj.id = persistent.datastore.getNextId(obj.javaClass)
+ obj.id = persistent.datastore.getNextIdAndIncrement(obj.javaClass)
} else if (obj.id > persistent.datastore.getNextId(obj.javaClass)) {
persistent.datastore.setMaxId(obj.javaClass, obj.id + 1)
}
diff --git a/src/main/kotlin/nl/astraeus/persistence/reference/Reference.kt b/src/main/kotlin/nl/astraeus/persistence/reference/Reference.kt
new file mode 100644
index 0000000..02648d1
--- /dev/null
+++ b/src/main/kotlin/nl/astraeus/persistence/reference/Reference.kt
@@ -0,0 +1,28 @@
+package nl.astraeus.nl.astraeus.persistence.reference
+
+import nl.astraeus.nl.astraeus.persistence.Persistable
+import nl.astraeus.nl.astraeus.persistence.currentTransaction
+import kotlin.reflect.KProperty
+
+class Reference(
+ val cls: Class,
+ val setter: (Long?) -> Unit,
+ val getter: () -> Long?,
+) {
+
+ operator fun getValue(thisRef: Persistable, property: KProperty<*>): S? {
+ return currentTransaction()?.find(cls.kotlin, (getter() ?: 0L))
+ }
+
+ operator fun setValue(thisRef: Persistable, property: KProperty<*>, value: S?) {
+ if (value != null) {
+ // todo: only store if not already stored?
+ currentTransaction()?.store(value)
+ }
+ setter(value?.id)
+ }
+
+ companion object {
+ private const val serialVersionUID: Long = 1L
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/nl/astraeus/persistence/reference/ReferenceCollection.kt b/src/main/kotlin/nl/astraeus/persistence/reference/ReferenceCollection.kt
new file mode 100644
index 0000000..c804cfb
--- /dev/null
+++ b/src/main/kotlin/nl/astraeus/persistence/reference/ReferenceCollection.kt
@@ -0,0 +1,82 @@
+package nl.astraeus.nl.astraeus.persistence.reference
+
+import nl.astraeus.nl.astraeus.persistence.Persistable
+import nl.astraeus.nl.astraeus.persistence.currentTransaction
+import java.io.Serializable
+
+inline fun referenceCollection(
+ ids: MutableCollection = mutableListOf()
+) = ReferenceCollection(T::class.java, ids)
+
+open class ReferenceCollection(
+ val cls: Class,
+ val ids: MutableCollection = mutableListOf()
+) : MutableCollection, Serializable {
+
+ protected fun checkElementIsPersisted(element: T) {
+ if (currentTransaction()?.find(cls.kotlin, element.id) == null) {
+ currentTransaction()?.store(element)
+ }
+ }
+
+ override val size: Int
+ get() {
+ return ids.size
+ }
+
+ override fun clear() {
+ ids.clear()
+ }
+
+ override fun isEmpty(): Boolean = ids.isEmpty()
+
+ override fun iterator(): MutableIterator {
+ return object : MutableIterator {
+ private var idsIterator = ids.iterator()
+
+ override fun hasNext(): Boolean = idsIterator.hasNext()
+
+ override fun next(): T = idsIterator.next().let {
+ currentTransaction()?.find(cls.kotlin, it)
+ } ?: throw IllegalStateException("Reference not found")
+
+ override fun remove() = idsIterator.remove()
+ }
+ }
+
+ override fun retainAll(elements: Collection): Boolean {
+ return ids.retainAll(elements.map { it.id }.toSet())
+ }
+
+ override fun removeAll(elements: Collection): Boolean {
+ return ids.removeAll(elements.map { it.id }.toSet())
+ }
+
+ override fun remove(element: T): Boolean {
+ return ids.remove(element.id)
+ }
+
+ override fun containsAll(elements: Collection): Boolean {
+ return ids.containsAll(elements.map { it.id })
+ }
+
+ override fun contains(element: T): Boolean {
+ return ids.contains(element.id)
+ }
+
+ override fun addAll(elements: Collection): Boolean {
+ for (element in elements) {
+ checkElementIsPersisted(element)
+ }
+ return ids.addAll(elements.map { it.id })
+ }
+
+ override fun add(element: T): Boolean {
+ checkElementIsPersisted(element)
+ return ids.add(element.id)
+ }
+
+ companion object {
+ private const val serialVersionUID: Long = 1L
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/nl/astraeus/persistence/reference/ReferenceList.kt b/src/main/kotlin/nl/astraeus/persistence/reference/ReferenceList.kt
new file mode 100644
index 0000000..efc483f
--- /dev/null
+++ b/src/main/kotlin/nl/astraeus/persistence/reference/ReferenceList.kt
@@ -0,0 +1,66 @@
+package nl.astraeus.nl.astraeus.persistence.reference
+
+import nl.astraeus.nl.astraeus.persistence.Persistable
+import nl.astraeus.nl.astraeus.persistence.currentTransaction
+import java.io.Serializable
+
+inline fun referenceList(
+ ids: MutableList = mutableListOf()
+) = ReferenceList(T::class.java, ids)
+
+class ReferenceList(
+ cls: Class,
+ val idsList: MutableList = mutableListOf()
+) : ReferenceCollection(cls, idsList), MutableList, Serializable {
+
+ override fun add(index: Int, element: T) {
+ checkElementIsPersisted(element)
+ idsList.add(index, element.id)
+ }
+
+ override fun addAll(index: Int, elements: Collection): Boolean {
+ for(element in elements) {
+ checkElementIsPersisted(element)
+ }
+ return idsList.addAll(index, elements.map { it.id })
+ }
+
+ override fun get(index: Int): T = currentTransaction()?.find(cls.kotlin, idsList[index]) ?: throw IllegalStateException("Reference not found")
+ override fun indexOf(element: T): Int {
+ return idsList.indexOf(element.id)
+ }
+
+ override fun isEmpty(): Boolean = ids.isEmpty()
+ override fun lastIndexOf(element: T): Int {
+ return idsList.lastIndexOf(element.id)
+ }
+
+ override fun listIterator(): MutableListIterator {
+ return ReferenceListIterator(cls, idsList)
+ }
+
+ override fun listIterator(index: Int): MutableListIterator {
+ return ReferenceListIterator(cls, idsList, index)
+ }
+
+ override fun removeAt(index: Int): T {
+ val id = idsList.removeAt(index)
+
+ return currentTransaction()?.find(cls.kotlin, id) ?: throw IllegalStateException("Reference not found")
+ }
+
+ override fun subList(fromIndex: Int, toIndex: Int): MutableList {
+ return ReferenceList(cls, idsList.subList(fromIndex, toIndex))
+ }
+
+ override fun set(index: Int, element: T): T {
+ checkElementIsPersisted(element)
+ idsList[index] = element.id
+ return element
+ }
+
+ companion object {
+ private const val serialVersionUID: Long = 1L
+ }
+
+}
diff --git a/src/main/kotlin/nl/astraeus/persistence/reference/ReferenceListIterator.kt b/src/main/kotlin/nl/astraeus/persistence/reference/ReferenceListIterator.kt
new file mode 100644
index 0000000..7da58f4
--- /dev/null
+++ b/src/main/kotlin/nl/astraeus/persistence/reference/ReferenceListIterator.kt
@@ -0,0 +1,47 @@
+package nl.astraeus.nl.astraeus.persistence.reference
+
+import nl.astraeus.nl.astraeus.persistence.Persistable
+import nl.astraeus.nl.astraeus.persistence.currentTransaction
+
+class ReferenceListIterator(
+ private val cls: Class,
+ idsList: MutableList,
+ index: Int = 0
+) : MutableListIterator {
+ private val idsIterator = idsList.listIterator(index)
+
+ private fun checkElementIsPersisted(element: T) {
+ if (currentTransaction()?.find(cls.kotlin, element.id) == null) {
+ currentTransaction()?.store(element)
+ }
+ }
+
+ override fun add(element: T) {
+ checkElementIsPersisted(element)
+ idsIterator.add(element.id)
+ }
+
+ override fun hasNext(): Boolean = idsIterator.hasNext()
+ override fun hasPrevious(): Boolean = idsIterator.hasPrevious()
+
+ override fun next(): T = idsIterator.next().let {
+ currentTransaction()?.find(cls.kotlin, it)
+ } ?: throw IllegalStateException("Reference not found")
+
+ override fun nextIndex(): Int = idsIterator.nextIndex()
+
+ override fun previous(): T = idsIterator.previous().let {
+ currentTransaction()?.find(cls.kotlin, it)
+ } ?: throw IllegalStateException("Reference not found")
+
+ override fun previousIndex(): Int = idsIterator.previousIndex()
+
+ override fun remove() {
+ idsIterator.remove()
+ }
+
+ override fun set(element: T) {
+ checkElementIsPersisted(element)
+ idsIterator.set(element.id)
+ }
+}
\ No newline at end of file
diff --git a/src/test/kotlin/nl/astraeus/persistence/TestPersistence.kt b/src/test/kotlin/nl/astraeus/persistence/TestPersistence.kt
index d931b7c..3f356a6 100644
--- a/src/test/kotlin/nl/astraeus/persistence/TestPersistence.kt
+++ b/src/test/kotlin/nl/astraeus/persistence/TestPersistence.kt
@@ -2,11 +2,12 @@ package nl.astraeus.persistence
import nl.astraeus.nl.astraeus.persistence.Persistable
import nl.astraeus.nl.astraeus.persistence.Persistent
-import nl.astraeus.nl.astraeus.persistence.Reference
import nl.astraeus.nl.astraeus.persistence.TransactionLog
+import nl.astraeus.nl.astraeus.persistence.currentTransaction
import nl.astraeus.nl.astraeus.persistence.find
import nl.astraeus.nl.astraeus.persistence.findByIndex
import nl.astraeus.nl.astraeus.persistence.index
+import nl.astraeus.nl.astraeus.persistence.reference.ReferenceCollection
import org.junit.jupiter.api.Assertions.assertNotNull
import org.junit.jupiter.api.Assertions.assertNull
import java.io.File
@@ -19,7 +20,11 @@ class TestPersistence {
override var version: Long = 0,
val name: String
) : Persistable, Cloneable {
- //var persons: MutableList by ListReference(Person::class.java)
+ val personIds: MutableList = ArrayList()
+ val persons: MutableCollection
+ get() {
+ return ReferenceCollection(Person::class.java, personIds)
+ }
companion object {
private const val serialVersionUID: Long = 1L
@@ -35,15 +40,23 @@ class TestPersistence {
override var version: Long = 0,
val name: String,
val age: Int,
+ private var companyId: Long? = null
) : Persistable, Cloneable {
- var company: Company by Reference(Company::class.java)
+ var company: Company?
+ get() = currentTransaction()?.find(Company::class, companyId ?: 0L)
+ set(value) {
+ if (value != null) {
+ currentTransaction()?.store(value)
+ }
+ companyId = value?.id
+ }
companion object {
private const val serialVersionUID: Long = 1L
}
override fun toString(): String {
- return "Person(id=$id, version=$version, name='$name', age=$age)"
+ return "Person(id=$id, version=$version, name='$name', age=$age, company=$companyId)"
}
}
diff --git a/src/test/kotlin/nl/astraeus/persistence/TestReferences.kt b/src/test/kotlin/nl/astraeus/persistence/TestReferences.kt
new file mode 100644
index 0000000..98a607c
--- /dev/null
+++ b/src/test/kotlin/nl/astraeus/persistence/TestReferences.kt
@@ -0,0 +1,92 @@
+package nl.astraeus.persistence
+
+import nl.astraeus.nl.astraeus.persistence.Persistable
+import nl.astraeus.nl.astraeus.persistence.Persistent
+import nl.astraeus.nl.astraeus.persistence.TransactionLog
+import nl.astraeus.nl.astraeus.persistence.currentTransaction
+import nl.astraeus.nl.astraeus.persistence.reference.referenceCollection
+import java.io.File
+import kotlin.test.Test
+
+class TestReferences {
+
+ class Company(
+ override var id: Long = 0,
+ override var version: Long = 0,
+ val name: String
+ ) : Persistable, Cloneable {
+ val persons: MutableCollection = referenceCollection()
+
+ companion object {
+ private const val serialVersionUID: Long = 1L
+ }
+
+ override fun toString(): String {
+ return "Company(id=$id, version=$version, name='$name', persons=${persons.size})"
+ }
+ }
+
+ class Person(
+ override var id: Long = 0,
+ override var version: Long = 0,
+ val name: String,
+ val age: Int,
+ private var companyId: Long? = null
+ ) : Persistable, Cloneable {
+ var company: Company?
+ get() = currentTransaction()?.find(Company::class, companyId ?: 0L)
+ set(value) {
+ if (value != null) {
+ currentTransaction()?.store(value)
+ }
+ companyId = value?.id
+ }
+
+ companion object {
+ private const val serialVersionUID: Long = 1L
+ }
+
+ override fun toString(): String {
+ return "Person(id=$id, version=$version, name='$name', age=$age, company=$companyId)"
+ }
+ }
+
+ @Test
+ fun showTransactions() {
+ val log = TransactionLog(File("data", "test-references"))
+
+ log.showTransactions()
+ }
+
+ @Test
+ fun testSerializeDeSerializeReferenceList() {
+ val pst = Persistent(
+ directory = File("data", "test-references"),
+ )
+
+ pst.transaction {
+ val company = find(Company::class, 1L) ?: Company(
+ id = 1L,
+ name = "ACME"
+ )
+
+ if (company.persons.isEmpty()) {
+ val person = Person(
+ id = 0L,
+ name = "John Doe",
+ age = 25
+ )
+ person.company = company
+ store(person)
+ company.persons.add(person)
+ }
+
+ store(company)
+
+ for (person in company.persons) {
+ println("Person: $person")
+ }
+ }
+ }
+
+}
diff --git a/src/test/kotlin/nl/astraeus/persistence/TestThreaded.kt b/src/test/kotlin/nl/astraeus/persistence/TestThreaded.kt
index 0ab5929..e8d1460 100644
--- a/src/test/kotlin/nl/astraeus/persistence/TestThreaded.kt
+++ b/src/test/kotlin/nl/astraeus/persistence/TestThreaded.kt
@@ -2,7 +2,7 @@ package nl.astraeus.persistence
import nl.astraeus.nl.astraeus.persistence.Persistable
import nl.astraeus.nl.astraeus.persistence.Persistent
-import nl.astraeus.nl.astraeus.persistence.Reference
+import nl.astraeus.nl.astraeus.persistence.reference.Reference
import nl.astraeus.nl.astraeus.persistence.TransactionLog
import nl.astraeus.nl.astraeus.persistence.count
import nl.astraeus.nl.astraeus.persistence.index
@@ -36,8 +36,13 @@ class TestThreaded {
override var version: Long = 0,
val name: String,
val age: Int,
+ var companyId: Long? = null
) : Persistable, Cloneable {
- var company: Company by Reference(Company::class.java)
+ var company: Company? by Reference(
+ Company::class.java,
+ { id -> companyId = id },
+ { companyId }
+ )
companion object {
private const val serialVersionUID: Long = 1L