HashMapStore.kt

  1. package com.hexagontk.store.hashmap

  2. import com.hexagontk.core.filterNotEmpty
  3. import com.hexagontk.store.Store
  4. import kotlin.reflect.KClass
  5. import kotlin.reflect.KProperty1

  6. class HashMapStore<T : Any, K : Any>(
  7.     override val type: KClass<T>,
  8.     override val key: KProperty1<T, K>,
  9.     override val name: String = type.java.simpleName,
  10.     private val store: HashMap<K, Map<String, Any>> = hashMapOf(),
  11.     override val encoder: (T) -> Map<String, *>,
  12.     override val decoder: (Map<String, *>) -> T,
  13. ) : Store<T, K> {

  14.     override fun insertOne(instance: T): K {
  15.         store[key.get(instance)] = map(instance)
  16.         return key.get(instance)
  17.     }

  18.     override fun insertMany(instances: List<T>): List<K> {
  19.         instances.forEach {
  20.             store[key.get(it)] = map(it)
  21.         }

  22.         return instances.map { key.get(it) }
  23.     }

  24.     override fun saveOne(instance: T): K? {
  25.         if (store.containsKey(key.get(instance))) {
  26.             store[key.get(instance)] = map(instance)
  27.             return null
  28.         }

  29.         store[key.get(instance)] = map(instance)
  30.         @Suppress("UNCHECKED_CAST")
  31.         return fromStore(key.get(instance)) as K
  32.     }

  33.     override fun saveMany(instances: List<T>): List<K?> {
  34.         return instances.map(::saveOne)
  35.     }

  36.     override fun replaceOne(instance: T): Boolean =
  37.         store.replace(key.get(instance), map(instance)) != null


  38.     override fun replaceMany(instances: List<T>): List<T> =
  39.         instances.mapNotNull { if (replaceOne(it)) it else null }


  40.     override fun updateOne(key: K, updates: Map<String, *>): Boolean {
  41.         if (!store.containsKey(key)) return false

  42.         val instance = store[key]!!.toMutableMap()

  43.         updates
  44.             .filterNotEmpty()
  45.             .forEach {
  46.                 instance[it.key] = toStore(it.value)
  47.             }

  48.         return store.replace(key, instance) != null
  49.     }

  50.     override fun updateMany(filter: Map<String, *>, updates: Map<String, *>): Long {
  51.         val filteredInstances = store.filter(filter)

  52.         return filteredInstances.map { updateOne(it, updates) }.count { it }.toLong()
  53.     }

  54.     override fun deleteOne(id: K): Boolean =
  55.         store.remove(id) != null

  56.     override fun deleteMany(filter: Map<String, *>): Long {
  57.         val filteredInstances = store.filter(filter)

  58.         return filteredInstances.map { deleteOne(it) }.count { it }.toLong()
  59.     }

  60.     override fun findOne(key: K): T? {
  61.         val result = store[key]
  62.         return result?.let { fromStore(result) }
  63.     }

  64.     override fun findOne(key: K, fields: List<String>): Map<String, *>? {
  65.         val instance = store[key]

  66.         return if (instance == null) null else fields.associateWith { instance[it] }
  67.     }

  68.     override fun findMany(
  69.         filter: Map<String, *>,
  70.         limit: Int?,
  71.         skip: Int?,
  72.         sort: Map<String, Boolean>
  73.     ): List<T> {
  74.         val filteredKeys = store.filter(filter)
  75.         val filteredInstances = filteredKeys.map { store[it]!! }

  76.         @Suppress("UNCHECKED_CAST")
  77.         return filteredInstances
  78.             .sort(sort)
  79.             .paginate(skip ?: 0, limit ?: filteredKeys.size)
  80.             .map { fromStore(it as Map<String, Any>) }
  81.     }

  82.     override fun findMany(
  83.         filter: Map<String, *>,
  84.         fields: List<String>,
  85.         limit: Int?,
  86.         skip: Int?,
  87.         sort: Map<String, Boolean>
  88.     ): List<Map<String, *>> {
  89.         val filteredInstances = store.filter(filter)
  90.         val result = filteredInstances.mapNotNull { findOne(it, fields) }

  91.         return result
  92.             .paginate(skip ?: 0, limit ?: result.size)
  93.             .sort(sort)
  94.     }

  95.     override fun count(filter: Map<String, *>): Long =
  96.         store.filter(filter).size.toLong()

  97.     override fun drop() =
  98.         store.clear()

  99.     private fun map(instance: T): Map<String, Any> = toStore(instance)

  100.     private fun HashMap<K, Map<String, Any>>.filter(filter: Map<String, *>): List<K> =
  101.         filter { it.value.containsValues(filter) }
  102.             .map { it.key }

  103.     private fun Map<String, Any>.containsValues(filter: Map<String, *>): Boolean =
  104.         filter.all {
  105.             when (val value = it.value) {
  106.                 is List<*> -> value.contains(this[it.key])
  107.                 else -> value == this[it.key]
  108.             }
  109.         }

  110.     private fun List<Map<String, *>>.paginate(skip: Int, limit: Int): List<Map<String, *>> =
  111.         let {
  112.             var endIndex = skip + limit
  113.             if (endIndex > this.size) endIndex = this.size

  114.             this.subList(skip, endIndex)
  115.         }

  116.     // TODO: Add sorting functionality (now only sorts by first field)
  117.     private fun List<Map<String, *>>.sort(sortFields: Map<String, Boolean>): List<Map<String, *>> =
  118.         if (sortFields.isEmpty())
  119.             this
  120.         else
  121.             sortedBy {
  122.                 val firstSortField = sortFields.entries.first()
  123.                 val sortingValue = it[firstSortField.key]
  124.                 @Suppress("UNCHECKED_CAST")
  125.                 if (sortingValue is Comparable<*>)
  126.                     sortingValue as? Comparable<Any>
  127.                 else
  128.                     error("Not comparable value")
  129.             }

  130.     private fun toStore(instance: T): Map<String, Any>  =
  131.         encoder(instance)
  132.             .filterNotEmpty()
  133.             .mapKeys { it.key.toString() }
  134.             .mapValues { it.value }

  135.     private fun toStore(value: Any): Any = value

  136.     private fun fromStore(map: Map<String, Any>): T =
  137.         decoder(map.filterNotEmpty())

  138.     private fun fromStore(value: Any): Any =
  139.         value
  140. }