1+ package nl.elements.objectstore
2+
13import io.reactivex.Observable
24import io.reactivex.ObservableSource
35import io.reactivex.Observer
46import io.reactivex.subjects.PublishSubject
5- import nl.elements.objectstore.Converter
6- import nl.elements.objectstore.Transformer
77import java.io.ByteArrayOutputStream
88import java.io.InputStream
99import java.io.OutputStream
@@ -13,19 +13,13 @@ import java.io.OutputStream
1313 * Supplies a basic abstraction for CRUD operations.
1414 */
1515
16- interface ObjectStore : ObservableSource <ObjectStore .Event > {
16+ interface ObjectStore : ReadableObjectStore , ObservableSource <ObjectStore .Event > {
1717
1818 val converter: Converter
1919 val transformer: Transformer
2020
21- val keys: Set <String >
22-
23- operator fun <T : Any > get (key : String ): T
24-
2521 operator fun set (key : String , value : Any )
2622
27- operator fun contains (key : String ): Boolean
28-
2923 fun remove (key : String )
3024
3125 fun OutputStream.write (key : String , value : Any ) =
@@ -61,7 +55,110 @@ interface ObjectStore : ObservableSource<ObjectStore.Event> {
6155
6256fun ObjectStore.toObservable (): Observable <ObjectStore .Event > = Observable .defer { this }
6357
58+ fun ObjectStore.toReadableObjectStore (): ReadableObjectStore = this
59+
6460fun ObjectStore.writeToBytes (key : String , value : Any ): ByteArray =
6561 ByteArrayOutputStream ()
6662 .also { it.write(key, value) }
6763 .toByteArray()
64+
65+ // / Utils for reducing `ObjectStore` which prefixes each key with the given `String` and delimiter
66+
67+ private const val DEFAULT_DELIMITER = " :"
68+
69+ /* *
70+ * Reduces all `ObjectStore`s (in sequence) into one store. Each `ObjectStore` his keys get prefixed with the given `Pair.first` and delimiter.
71+ *
72+ * Although it is not forbidden, it is not advised to have the `Pair.first` contain the delimiter.
73+ */
74+
75+ fun Array <Pair <String , ObjectStore >>.reduceWithNamespace (delimiter : String = DEFAULT_DELIMITER ) =
76+ asSequence().reduceWithNamespace(delimiter)
77+
78+ /* *
79+ * Reduces all `ObjectStore`s (in sequence) into one store. Each `ObjectStore` his keys get prefixed with the given `Pair.first` and delimiter.
80+ *
81+ * Although it is not forbidden, it is not advised to have the `Pair.first` contain the delimiter.
82+ */
83+
84+ fun Map <String , ObjectStore >.reduceWithNamespace (delimiter : String = DEFAULT_DELIMITER ): ObjectStore =
85+ toList().asSequence().reduceWithNamespace(delimiter)
86+
87+ /* *
88+ * Reduces all `ObjectStore`s (in sequence) into one store. Each `ObjectStore` his keys get prefixed with the given `Pair.first` and delimiter.
89+ *
90+ * Although it is not forbidden, it is not advised to have the `Pair.first` contain the delimiter.
91+ */
92+
93+ fun Iterable <Pair <String , ObjectStore >>.reduceWithNamespace (delimiter : String = DEFAULT_DELIMITER ) =
94+ asSequence().reduceWithNamespace(delimiter)
95+
96+ /* *
97+ * Reduces all `ObjectStore`s (in sequence) into one store. Each `ObjectStore` his keys get prefixed with the given `Pair.first` and delimiter.
98+ *
99+ * Although it is not forbidden, it is not advised to have the `Pair.first` contain the delimiter.
100+ */
101+
102+ fun Sequence <Pair <String , ObjectStore >>.reduceWithNamespace (delimiter : String = DEFAULT_DELIMITER ) =
103+ fold(unknownNamespaceObjectStore()) { next, (prefix, store) ->
104+ store.withNamespace(" $prefix$delimiter " , next)
105+ }
106+
107+ private fun ObjectStore.withNamespace (namespace : String , next : ObjectStore ): ObjectStore =
108+ object : ObjectStore {
109+
110+ val store = this @withNamespace
111+
112+ override val converter: Converter = store.converter
113+ override val transformer: Transformer = store.transformer
114+ override val keys: Set <String >
115+ get() = store.keys.map { namespace + it }.toMutableSet().apply { addAll(next.keys) }
116+
117+ override fun <T : Any > get (key : String ): T =
118+ key.removeNamespace()?.let (store::get) ? : next[key]
119+
120+ override fun contains (key : String ): Boolean =
121+ key.removeNamespace()?.let (store::contains) ? : next.contains(key)
122+
123+ override fun set (key : String , value : Any ) =
124+ key.removeNamespace()?.let { store[it] = value } ? : next.set(key, value)
125+
126+ override fun remove (key : String ) = key.removeNamespace()?.let (store::remove) ? : next.remove(key)
127+
128+ override fun subscribe (observer : Observer <in ObjectStore .Event >) =
129+ store
130+ .toObservable()
131+ .map(::prependNamespace)
132+ .let { Observable .merge(it, next) }
133+ .subscribe(observer)
134+
135+ private fun String.removeNamespace (): String? = removePrefix(namespace).takeIf { it != this @removeNamespace }
136+
137+ private fun prependNamespace (event : ObjectStore .Event ): ObjectStore .Event =
138+ when (event) {
139+ is ObjectStore .Event .Updated -> ObjectStore .Event .Updated (namespace + event.key)
140+ is ObjectStore .Event .Removed -> ObjectStore .Event .Removed (namespace + event.key)
141+ }
142+
143+ }
144+
145+ private fun unknownNamespaceObjectStore (): ObjectStore = object : ObjectStore {
146+
147+ override val converter: Converter = Converter .DEFAULT
148+ override val transformer: Transformer = Transformer .DEFAULT
149+ override val keys: Set <String > = emptySet()
150+
151+ override fun set (key : String , value : Any ) = throw UnknownNamespaceException (key)
152+
153+ override fun remove (key : String ) = throw UnknownNamespaceException (key)
154+
155+ override fun <T : Any > get (key : String ): T = throw UnknownNamespaceException (key)
156+
157+ override fun contains (key : String ): Boolean = throw UnknownNamespaceException (key)
158+
159+ override fun subscribe (observer : Observer <in ObjectStore .Event >) {
160+ }
161+
162+ }
163+
164+ class UnknownNamespaceException internal constructor(key : String ) : Exception(key)
0 commit comments