Skip to content

Commit

Permalink
Added a way to persist properties' values, #3
Browse files Browse the repository at this point in the history
  • Loading branch information
Miha-x64 committed Feb 8, 2018
1 parent 4357b8c commit 01130b2
Show file tree
Hide file tree
Showing 8 changed files with 558 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package net.aquadc.properties.persistence

import java.io.*

/**
* Captures properties' value into a [ByteArray] eagerly.
*/
class ByteArrayPropertiesMemento : PropertiesMemento, Externalizable {

constructor(props: PersistableProperties) {
val os = ByteArrayOutputStream()
props.saveOrRestore(PropertyOutput(DataOutputStream(os)))
bytes = os.toByteArray()
}

constructor() {
bytes = EMPTY
}

private var bytes: ByteArray

override fun writeExternal(out: ObjectOutput) {
val bytes = bytes
out.writeInt(bytes.size)
out.write(bytes)
}

override fun readExternal(oi: ObjectInput) {
val ba = ByteArray(oi.readInt())
oi.read(ba)
bytes = ba
}

override fun restoreTo(target: PersistableProperties) {
target.saveOrRestore(PropertyInput(DataInputStream(ByteArrayInputStream(bytes))))
}

fun copyValue(): ByteArray =
bytes.clone()

companion object {
private const val serialVersionUid: Long = 1
private val EMPTY = ByteArray(0)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package net.aquadc.properties.persistence

/**
* Describes a state-class which can be saved and restored.
*/
interface PersistableProperties {

/**
* Gives properties to (de)serializer.
* Should consist of [x] method calls.
*/
fun saveOrRestore(d: PropertyIo)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package net.aquadc.properties.persistence

/**
* Describes an object which holds data from properties.
*/
interface PropertiesMemento {

/**
* Pushes encapsulated data into [target].
*/
fun restoreTo(target: PersistableProperties)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package net.aquadc.properties.persistence

import net.aquadc.properties.MutableProperty
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import java.util.concurrent.atomic.AtomicReference

/**
* [InvocationHandler] for [PropertyIo].
* Reads properties' values into buffers;
* writes buffered values into properties.
*/
class PropertyBuffer private constructor() : InvocationHandler {

private val values = ArrayList<Any?>()
private var produceNext = -1

override fun invoke(proxy: Any?, method: Method, args: Array<out Any>): Any? {
@Suppress("UNCHECKED_CAST") // won't explode if used properly
val prop = args[0] as MutableProperty<Any?>
if (produceNext == -1) { // consume
values.add(prop.value)
} else { // produce
val idx = produceNext++
prop.value = values[idx]
values[idx] = null
}
return null
}

fun produceThen() {
check(produceNext == -1)
produceNext = 0
}

fun consumeThen() {
check(produceNext == values.size)
values.clear()
produceNext = -1
}

fun assertClean() {
check(produceNext == -1)
check(values.isEmpty())
}

companion object {
private val ref = AtomicReference<Pair<PropertyIo, PropertyBuffer>?>(null)
fun get(): Pair<PropertyIo, PropertyBuffer> {
ref.getAndSet(null)?.let { return it }
val buf = PropertyBuffer()
val proxy = Proxy.newProxyInstance(PropertyIo::class.java.classLoader, arrayOf(PropertyIo::class.java), buf) as PropertyIo
return proxy to buf
}
fun recycle(buffer: Pair<PropertyIo, PropertyBuffer>) {
check(Proxy.getInvocationHandler(buffer.first) === buffer.second)
buffer.second.assertClean()
ref.set(buffer)
}
inline fun <R> borrow(block: (PropertyIo, PropertyBuffer) -> R): R {
val pdPb = get()
val (pd, pb) = pdPb
val ret = block(pd, pb)
recycle(pdPb)
return ret
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package net.aquadc.properties.persistence

import net.aquadc.properties.MutableProperty
import java.io.DataInput
import java.util.*

/**
* Reads data from [input] into properties.
*/
class PropertyInput(
private val input: DataInput
) : PropertyIo {

override fun bool(prop: MutableProperty<Boolean>) {
prop.value = input.readBoolean()
}
override fun byte(prop: MutableProperty<Byte>) {
prop.value = input.readByte()
}
override fun short(prop: MutableProperty<Short>) {
prop.value = input.readShort()
}
override fun char(prop: MutableProperty<Char>) {
prop.value = input.readChar()
}
override fun int(prop: MutableProperty<Int>) {
prop.value = input.readInt()
}
override fun long(prop: MutableProperty<Long>) {
prop.value = input.readLong()
}
override fun float(prop: MutableProperty<Float>) {
prop.value = input.readFloat()
}
override fun double(prop: MutableProperty<Double>) {
prop.value = input.readDouble()
}

override fun bytes(prop: MutableProperty<ByteArray>) {
prop.value = ByteArray(input.readInt()).also(input::readFully)
}
override fun shorts(prop: MutableProperty<ShortArray>) {
prop.value = ShortArray(input.readInt()) { input.readShort() }
}
override fun chars(prop: MutableProperty<CharArray>) {
prop.value = CharArray(input.readInt()) { input.readChar() }
}
override fun ints(prop: MutableProperty<IntArray>) {
prop.value = IntArray(input.readInt()) { input.readInt() }
}
override fun longs(prop: MutableProperty<LongArray>) {
prop.value = LongArray(input.readInt()) { input.readLong() }
}
override fun floats(prop: MutableProperty<FloatArray>) {
prop.value = FloatArray(input.readInt()) { input.readFloat() }
}
override fun doubles(prop: MutableProperty<DoubleArray>) {
prop.value = DoubleArray(input.readInt()) { input.readDouble() }
}

override fun string(prop: MutableProperty<String>) {
prop.value = input.readUTF()
}
override fun stringArr(prop: MutableProperty<Array<String>>) {
prop.value = Array(input.readInt()) { input.readUTF() }
}
override fun stringList(prop: MutableProperty<List<String>>) {
prop.value = List(input.readInt()) { input.readUTF() }
}

override fun <E : Enum<E>> enum(prop: MutableProperty<E>, type: Class<E>) {
prop.value = java.lang.Enum.valueOf(type, input.readUTF())
}
override fun <E : Enum<E>> enumSet(prop: MutableProperty<Set<E>>, type: Class<E>) {
prop.value = EnumSet.noneOf(type).also { set ->
repeat(input.readInt()) {
set.add(java.lang.Enum.valueOf(type, input.readUTF()))
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package net.aquadc.properties.persistence

import net.aquadc.properties.MutableProperty
import java.util.*


interface PropertyIo {
fun bool(prop: MutableProperty<Boolean>)
fun byte(prop: MutableProperty<Byte>)
fun short(prop: MutableProperty<Short>)
fun char(prop: MutableProperty<Char>)
fun int(prop: MutableProperty<Int>)
fun long(prop: MutableProperty<Long>)
fun float(prop: MutableProperty<Float>)
fun double(prop: MutableProperty<Double>)

fun bytes(prop: MutableProperty<ByteArray>)
fun shorts(prop: MutableProperty<ShortArray>)
fun chars(prop: MutableProperty<CharArray>)
fun ints(prop: MutableProperty<IntArray>)
fun longs(prop: MutableProperty<LongArray>)
fun floats(prop: MutableProperty<FloatArray>)
fun doubles(prop: MutableProperty<DoubleArray>)

fun string(prop: MutableProperty<String>)
fun stringArr(prop: MutableProperty<Array<String>>)
fun stringList(prop: MutableProperty<List<String>>)

fun <E : Enum<E>> enum(prop: MutableProperty<E>, type: Class<E>)
fun <E : Enum<E>> enumSet(prop: MutableProperty<Set<E>>, type: Class<E>)
}


@JvmName("bool") inline infix fun PropertyIo.x(prop: MutableProperty<Boolean>) = bool(prop)
@JvmName("byte") inline infix fun PropertyIo.x(prop: MutableProperty<Byte>) = byte(prop)
@JvmName("short") inline infix fun PropertyIo.x(prop: MutableProperty<Short>) = short(prop)
@JvmName("char") inline infix fun PropertyIo.x(prop: MutableProperty<Char>) = char(prop)
@JvmName("int") inline infix fun PropertyIo.x(prop: MutableProperty<Int>) = int(prop)
@JvmName("long") inline infix fun PropertyIo.x(prop: MutableProperty<Long>) = long(prop)
@JvmName("float") inline infix fun PropertyIo.x(prop: MutableProperty<Float>) = float(prop)
@JvmName("double") inline infix fun PropertyIo.x(prop: MutableProperty<Double>) = double(prop)

@JvmName("bytes") inline infix fun PropertyIo.x(prop: MutableProperty<ByteArray>) = bytes(prop)
@JvmName("shorts") inline infix fun PropertyIo.x(prop: MutableProperty<ShortArray>) = shorts(prop)
@JvmName("chars") inline infix fun PropertyIo.x(prop: MutableProperty<CharArray>) = chars(prop)
@JvmName("ints") inline infix fun PropertyIo.x(prop: MutableProperty<IntArray>) = ints(prop)
@JvmName("longs") inline infix fun PropertyIo.x(prop: MutableProperty<LongArray>) = longs(prop)
@JvmName("floats") inline infix fun PropertyIo.x(prop: MutableProperty<FloatArray>) = floats(prop)
@JvmName("doubles") inline infix fun PropertyIo.x(prop: MutableProperty<DoubleArray>) = doubles(prop)

@JvmName("string") inline infix fun PropertyIo.x(prop: MutableProperty<String>) = string(prop)
@JvmName("stringArr") inline infix fun PropertyIo.x(prop: MutableProperty<Array<String>>) = stringArr(prop)
@JvmName("stringList") inline infix fun PropertyIo.x(prop: MutableProperty<List<String>>) = stringList(prop)

@JvmName("enum") inline infix fun <reified E : Enum<E>> PropertyIo.x(prop: MutableProperty<E>) =
enum(prop, E::class.java)
@JvmName("enumSet") inline infix fun <reified E : Enum<E>> PropertyIo.x(prop: MutableProperty<Set<E>>) =
enumSet(prop, E::class.java)

@Suppress("UNCHECKED_CAST") // we'll read it as Set and write as EnumSet
@JvmName("realEnumSet") inline infix fun <reified E : Enum<E>> PropertyIo.x(prop: MutableProperty<EnumSet<E>>) =
enumSet(prop as MutableProperty<Set<E>>, E::class.java)
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package net.aquadc.properties.persistence

import net.aquadc.properties.MutableProperty
import java.io.DataOutput

/**
* Writes data from properties into [output].
*/
class PropertyOutput(
private val output: DataOutput
) : PropertyIo {

override fun bool(prop: MutableProperty<Boolean>) {
output.writeBoolean(prop.value)
}
override fun byte(prop: MutableProperty<Byte>) {
output.writeByte(prop.value.toInt())
}
override fun short(prop: MutableProperty<Short>) {
output.writeShort(prop.value.toInt())
}
override fun char(prop: MutableProperty<Char>) {
output.writeChar(prop.value.toInt())
}
override fun int(prop: MutableProperty<Int>) {
output.writeInt(prop.value)
}
override fun long(prop: MutableProperty<Long>) {
output.writeLong(prop.value)
}
override fun float(prop: MutableProperty<Float>) {
output.writeFloat(prop.value)
}
override fun double(prop: MutableProperty<Double>) {
output.writeDouble(prop.value)
}

override fun bytes(prop: MutableProperty<ByteArray>) {
val value = prop.value
output.writeInt(value.size)
output.write(value)
}
override fun shorts(prop: MutableProperty<ShortArray>) {
val value = prop.value
output.writeInt(value.size)
value.forEach { output.writeShort(it.toInt()) }
}
override fun chars(prop: MutableProperty<CharArray>) {
val value = prop.value
output.writeInt(value.size)
value.forEach { output.writeChar(it.toInt()) }
}
override fun ints(prop: MutableProperty<IntArray>) {
val value = prop.value
output.writeInt(value.size)
value.forEach(output::writeInt)
}
override fun longs(prop: MutableProperty<LongArray>) {
val value = prop.value
output.writeInt(value.size)
value.forEach(output::writeLong)
}
override fun floats(prop: MutableProperty<FloatArray>) {
val value = prop.value
output.writeInt(value.size)
value.forEach(output::writeFloat)
}
override fun doubles(prop: MutableProperty<DoubleArray>) {
val value = prop.value
output.writeInt(value.size)
value.forEach(output::writeDouble)
}

override fun string(prop: MutableProperty<String>) {
output.writeUTF(prop.value)
}
override fun stringArr(prop: MutableProperty<Array<String>>) {
val value = prop.value
output.writeInt(value.size)
value.forEach(output::writeUTF)
}
override fun stringList(prop: MutableProperty<List<String>>) {
val value = prop.value
output.writeInt(value.size)
for (i in 0 until value.size) output.writeUTF(value[i])
}

override fun <E : Enum<E>> enum(prop: MutableProperty<E>, type: Class<E>) {
output.writeUTF(prop.value.name)
}
override fun <E : Enum<E>> enumSet(prop: MutableProperty<Set<E>>, type: Class<E>) {
val value = prop.value
output.writeInt(value.size)
value.forEach { output.writeUTF(it.name) }
}

}

0 comments on commit 01130b2

Please sign in to comment.