diff --git a/properties/src/main/kotlin/net/aquadc/properties/internal/ManagedProperty.kt b/properties/src/main/kotlin/net/aquadc/properties/internal/ManagedProperty.kt index 9c27220d..2f524eac 100644 --- a/properties/src/main/kotlin/net/aquadc/properties/internal/ManagedProperty.kt +++ b/properties/src/main/kotlin/net/aquadc/properties/internal/ManagedProperty.kt @@ -1,16 +1,18 @@ package net.aquadc.properties.internal +import android.support.annotation.RestrictTo import net.aquadc.properties.MutableProperty import net.aquadc.properties.Property /** * A property whose value can be changed inside a transaction. */ -@PublishedApi internal class ManagedProperty( +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +class ManagedProperty( private val manager: Manager, private val token: TOKN, private val id: Long -) : `Notifier+1AtomicRef`(true, unset()), MutableProperty, (@ParameterName("newValue") T) -> Unit { +) : `Notifier+1AtomicRef`(true, unset()), MutableProperty { override var value: T get() { @@ -45,7 +47,7 @@ import net.aquadc.properties.Property val clean = if (ref === Unset) manager.getClean(token, id) else Unset // after mutating dirty state we won't be able to see the clean one, so preserve it - val success = manager.set(token, id, expect, update, this) + val success = manager.set(token, id, expect, update) // this changes 'dirty' state (and value returned by 'get'), // but we don't want to deliver it until it becomes clean @@ -54,7 +56,7 @@ import net.aquadc.properties.Property return success } - override fun invoke(newValue: T) { + fun commit(newValue: T) { if (newValue !== Unset) { // the transaction was committed @@ -82,12 +84,7 @@ interface Manager { /** * Set, if [expected] === [Unset]; CAS otherwise. - * @param onTransactionEnd will be invoked after transaction end; - * param newValue: new value, if transaction was committed, or [Unset], if it was rolled back * @return if write was successful; simple sets are always successful, even if current value is already equal to [update]. */ - fun set(token: TOKN, id: Long, expected: Any?, update: T, onTransactionEnd: (newValue: T) -> Unit): Boolean + fun set(token: TOKN, id: Long, expected: Any?, update: T): Boolean } - -inline fun newManagedProperty(manager: Manager, token: TOKN, id: Long): MutableProperty = - ManagedProperty(manager, token, id) diff --git a/properties/src/main/kotlin/net/aquadc/properties/internal/`Notifier+1AtomicRef`.kt b/properties/src/main/kotlin/net/aquadc/properties/internal/`Notifier+1AtomicRef`.kt index 84f37caf..be125415 100644 --- a/properties/src/main/kotlin/net/aquadc/properties/internal/`Notifier+1AtomicRef`.kt +++ b/properties/src/main/kotlin/net/aquadc/properties/internal/`Notifier+1AtomicRef`.kt @@ -1,12 +1,14 @@ package net.aquadc.properties.internal +import android.support.annotation.RestrictTo import java.util.concurrent.atomic.AtomicReferenceFieldUpdater /** * This is a [-Notifier] with extra [AtomicReferenceFieldUpdater]. * @param REF type of extra atomic ref */ -internal abstract class `Notifier+1AtomicRef` +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +abstract class `Notifier+1AtomicRef` internal constructor(concurrent: Boolean, initialRef: REF) : `-Notifier`(concurrent) { diff --git a/sql/src/main/kotlin/net/aquadc/properties/sql/jdbc-sqlite.kt b/sql/src/main/kotlin/net/aquadc/properties/sql/jdbc-sqlite.kt index cddd4919..9666a19c 100644 --- a/sql/src/main/kotlin/net/aquadc/properties/sql/jdbc-sqlite.kt +++ b/sql/src/main/kotlin/net/aquadc/properties/sql/jdbc-sqlite.kt @@ -4,9 +4,9 @@ package net.aquadc.properties.sql import net.aquadc.properties.MutableProperty import net.aquadc.properties.Property +import net.aquadc.properties.internal.ManagedProperty import net.aquadc.properties.internal.Manager import net.aquadc.properties.internal.Unset -import net.aquadc.properties.internal.newManagedProperty import net.aquadc.properties.propertyOf import java.sql.Connection import java.sql.PreparedStatement @@ -55,12 +55,20 @@ class JdbcSqliteSession(private val connection: Connection) : Session { } private fun onTransactionEnd(successful: Boolean) { - checkNotNull(transaction) + val transaction = transaction ?: throw AssertionError() try { if (successful) connection.commit() else connection.rollback() - transaction = null + this.transaction = null - // TODO notify + transaction.updated?.forEach { (col, pkToVal) -> + pkToVal.forEach { (pk, value) -> + @Suppress("UPPER_BOUND_VIOLATED") + fieldOfInternal(col as Col, pk).commit(value) + } + } + transaction.inserted?.forEach { (table, pk) -> + // todo + } } finally { lock.writeLock().unlock() } @@ -196,17 +204,19 @@ class JdbcSqliteSession(private val connection: Connection) : Session { .executeQuery() } + override fun , ID : IdBound, T> fieldOf(col: Col, id: ID): MutableProperty = + fieldOfInternal(col, id) // just erase type - override fun , ID : IdBound, T> fieldOf( - table: Table, col: Col, id: ID - ): MutableProperty { + fun , ID : IdBound, T> fieldOfInternal( + col: Col, id: ID + ): ManagedProperty> { val tableCols = - recordManager.records.getOrPut(col, ::ConcurrentHashMap) as ConcurrentHashMap> + recordManager.records.getOrPut(col, ::ConcurrentHashMap) as ConcurrentHashMap>> - val localId = localId(table, id) + val localId = localId(col.table as Table, id) return tableCols.getOrPut(localId) { - newManagedProperty(recordManager as Manager, T>, col, localId) + ManagedProperty(recordManager as Manager, T>, col, localId) } } @@ -232,7 +242,7 @@ class JdbcSqliteSession(private val connection: Connection) : Session { * col: records * record: fields */ - internal val records = ConcurrentHashMap, ConcurrentHashMap>>() + internal val records = ConcurrentHashMap, ConcurrentHashMap>>() private val statements = ThreadLocal>() @@ -260,7 +270,7 @@ class JdbcSqliteSession(private val connection: Connection) : Session { } @Suppress("UPPER_BOUND_VIOLATED") - override fun set(token: Col<*, *>, id: Long, expected: Any?, update: Any?, onTransactionEnd: (newValue: Any?) -> Unit): Boolean { + override fun set(token: Col<*, *>, id: Long, expected: Any?, update: Any?): Boolean { val transaction = transaction ?: throw IllegalStateException("This can be performed only within a transaction") getDirty(token, id).let { if (it === Unset) { diff --git a/sql/src/main/kotlin/net/aquadc/properties/sql/library.kt b/sql/src/main/kotlin/net/aquadc/properties/sql/library.kt index 172e85fe..03da218a 100644 --- a/sql/src/main/kotlin/net/aquadc/properties/sql/library.kt +++ b/sql/src/main/kotlin/net/aquadc/properties/sql/library.kt @@ -17,7 +17,7 @@ interface Session { fun , ID : IdBound> select(table: Table, condition: WhereCondition): Property> fun , ID : IdBound> count(table: Table, condition: WhereCondition): Property - fun , ID : IdBound, T> fieldOf(table: Table, col: Col, id: ID): MutableProperty + fun , ID : IdBound, T> fieldOf(col: Col, id: ID): MutableProperty } inline fun Session.transaction(block: (Transaction) -> Unit) { @@ -141,7 +141,7 @@ abstract class Record, ID : IdBound>( infix fun , ForeID : IdBound> Col.toOneNullable(foreignTable: Table): MutableProperty = - session.fieldOf(this@Record.table, this, primaryKey).bind( + session.fieldOf(this, primaryKey).bind( { id -> if (id == null) null else session.require(foreignTable, id) }, { it?.primaryKey } ) @@ -151,7 +151,7 @@ abstract class Record, ID : IdBound>( session.select(foreignTable, this eq primaryKey) operator fun Col.invoke(): MutableProperty = - session.fieldOf(this@Record.table, this, primaryKey) + session.fieldOf(this, primaryKey) }