Optimized generics added jvm module for tests

This commit is contained in:
Alexander Nozik 2018-09-14 22:12:34 +03:00
parent 4ca81518d5
commit 81660f3ca7
9 changed files with 129 additions and 26 deletions

View File

@ -0,0 +1,27 @@
plugins {
id 'kotlin-platform-jvm'
}
group 'hep.dataforge'
version '0.1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
expectedBy rootProject
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
testCompile "junit:junit:4.12"
testCompile "org.jetbrains.kotlin:kotlin-test"
testCompile "org.jetbrains.kotlin:kotlin-test-junit"
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
sourceCompatibility = "1.8"

View File

@ -0,0 +1,23 @@
package hep.dataforge.meta
//TODO add validator to configuration
class Configuration: MutableMetaNode<Configuration>() {
/**
* Attach configuration node instead of creating one
*/
override fun wrap(meta: Meta): Configuration {
return meta as? Configuration ?: Configuration().also { builder ->
items.mapValues { entry ->
val item = entry.value
builder[entry.key] = when (item) {
is MetaItem.ValueItem -> MetaItem.ValueItem(item.value)
is MetaItem.SingleNodeItem -> MetaItem.SingleNodeItem(wrap(item.node))
is MetaItem.MultiNodeItem -> MetaItem.MultiNodeItem(item.nodes.map { wrap(it) })
}
}
}
}
override fun empty(): Configuration = Configuration()
}

View File

@ -1,7 +1,6 @@
package hep.dataforge.meta package hep.dataforge.meta
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.NameToken
import hep.dataforge.names.toName import hep.dataforge.names.toName
/** /**
@ -10,13 +9,13 @@ import hep.dataforge.names.toName
* * a single node * * a single node
* * a list of nodes * * a list of nodes
*/ */
sealed class MetaItem<M : Meta<M>> { sealed class MetaItem<M : Meta> {
class ValueItem<M : Meta<M>>(val value: Value) : MetaItem<M>() class ValueItem<M : Meta>(val value: Value) : MetaItem<M>()
class SingleNodeItem<M : Meta<M>>(val node: M) : MetaItem<M>() class SingleNodeItem<M : Meta>(val node: M) : MetaItem<M>()
class MultiNodeItem<M : Meta<M>>(val nodes: List<M>) : MetaItem<M>() class MultiNodeItem<M : Meta>(val nodes: List<M>) : MetaItem<M>()
} }
operator fun <M : Meta<M>> List<M>.get(query: String): M? { operator fun <M : Meta> List<M>.get(query: String): M? {
return if (query.isEmpty()) { return if (query.isEmpty()) {
first() first()
} else { } else {
@ -31,8 +30,26 @@ operator fun <M : Meta<M>> List<M>.get(query: String): M? {
* * [MetaItem.SingleNodeItem] single node * * [MetaItem.SingleNodeItem] single node
* * [MetaItem.MultiNodeItem] multi-value node * * [MetaItem.MultiNodeItem] multi-value node
*/ */
interface Meta<M : Meta<M>> { interface Meta {
val items: Map<String, MetaItem<M>> val items: Map<String, MetaItem<out Meta>>
}
operator fun Meta.get(name: Name): MetaItem<out Meta>? {
return when (name.length) {
0 -> error("Can't resolve element from empty name")
1 -> items[name.first()!!.body]
else -> name.first()!!.let{ token -> items[token.body]?.nodes?.get(token.query)}?.get(name.cutFirst())
}
}
//TODO create Java helper for meta operations
operator fun Meta.get(key: String): MetaItem<out Meta>? = get(key.toName())
/**
* A meta node that ensures that all of its descendants has at least the same type
*/
abstract class MetaNode<M: MetaNode<M>>: Meta{
abstract override val items: Map<String, MetaItem<M>>
operator fun get(name: Name): MetaItem<M>? { operator fun get(name: Name): MetaItem<M>? {
return when (name.length) { return when (name.length) {
@ -50,7 +67,7 @@ interface Meta<M : Meta<M>> {
* *
* If the argument is possibly mutable node, it is copied on creation * If the argument is possibly mutable node, it is copied on creation
*/ */
class SealedMeta(meta: Meta<*>) : Meta<SealedMeta> { class SealedMeta(meta: Meta) : MetaNode<SealedMeta>() {
override val items: Map<String, MetaItem<SealedMeta>> = if (meta is SealedMeta) { override val items: Map<String, MetaItem<SealedMeta>> = if (meta is SealedMeta) {
meta.items meta.items
} else { } else {
@ -68,4 +85,4 @@ class SealedMeta(meta: Meta<*>) : Meta<SealedMeta> {
/** /**
* Generate sealed node from [this]. If it is already sealed return it as is * Generate sealed node from [this]. If it is already sealed return it as is
*/ */
fun Meta<*>.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(this) fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(this)

View File

@ -3,8 +3,8 @@ package hep.dataforge.meta
/** /**
* DSL builder for meta * DSL builder for meta
*/ */
class MetaBuilder : MutableMeta<MetaBuilder>() { class MetaBuilder : MutableMetaNode<MetaBuilder>() {
override fun wrap(meta: Meta<*>): MetaBuilder = meta.builder() override fun wrap(meta: Meta): MetaBuilder = meta.builder()
override fun empty(): MetaBuilder = MetaBuilder() override fun empty(): MetaBuilder = MetaBuilder()
infix fun String.to(value: Any) { infix fun String.to(value: Any) {
@ -19,7 +19,7 @@ class MetaBuilder : MutableMeta<MetaBuilder>() {
/** /**
* For safety, builder always copies the initial meta even if it is builder itself * For safety, builder always copies the initial meta even if it is builder itself
*/ */
fun Meta<*>.builder(): MetaBuilder { fun Meta.builder(): MetaBuilder {
return MetaBuilder().also { builder -> return MetaBuilder().also { builder ->
items.mapValues { entry -> items.mapValues { entry ->
val item = entry.value val item = entry.value
@ -30,4 +30,6 @@ fun Meta<*>.builder(): MetaBuilder {
} }
} }
} }
} }
fun buildMeta(builder: MetaBuilder.() -> Unit): Meta = MetaBuilder().apply(builder)

View File

@ -13,7 +13,7 @@ val MetaItem<*>.double get() = number.toDouble()
val MetaItem<*>.int get() = number.toInt() val MetaItem<*>.int get() = number.toInt()
val MetaItem<*>.long get() = number.toLong() val MetaItem<*>.long get() = number.toLong()
val <M : Meta<M>> MetaItem<M>.node: M val <M : Meta> MetaItem<M>.node: M
get() = when (this) { get() = when (this) {
is MetaItem.ValueItem -> error("Trying to interpret value meta item as node item") is MetaItem.ValueItem -> error("Trying to interpret value meta item as node item")
is MetaItem.SingleNodeItem -> node is MetaItem.SingleNodeItem -> node
@ -24,14 +24,14 @@ val <M : Meta<M>> MetaItem<M>.node: M
* Utility method to access item content as list of nodes. * Utility method to access item content as list of nodes.
* Returns empty list if it is value item. * Returns empty list if it is value item.
*/ */
val <M : Meta<M>> MetaItem<M>.nodes: List<M> val <M : Meta> MetaItem<M>.nodes: List<M>
get() = when (this) { get() = when (this) {
is MetaItem.ValueItem -> emptyList()//error("Trying to interpret value meta item as node item") is MetaItem.ValueItem -> emptyList()//error("Trying to interpret value meta item as node item")
is MetaItem.SingleNodeItem -> listOf(node) is MetaItem.SingleNodeItem -> listOf(node)
is MetaItem.MultiNodeItem -> nodes is MetaItem.MultiNodeItem -> nodes
} }
fun <M : Meta<M>> MetaItem<M>.indexOf(meta: M): Int { fun <M : Meta> MetaItem<M>.indexOf(meta: M): Int {
return when (this) { return when (this) {
is MetaItem.ValueItem -> -1 is MetaItem.ValueItem -> -1
is MetaItem.SingleNodeItem -> if (node == meta) 0 else -1 is MetaItem.SingleNodeItem -> if (node == meta) 0 else -1

View File

@ -8,10 +8,15 @@ class MetaListener(val owner: Any? = null, val action: (name: Name, oldItem: Met
operator fun invoke(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) = action(name, oldItem, newItem) operator fun invoke(name: Name, oldItem: MetaItem<*>?, newItem: MetaItem<*>?) = action(name, oldItem, newItem)
} }
interface MutableMeta<M : MutableMeta<M>>: Meta{
operator fun set(name: Name, item: MetaItem<M>)
}
/** /**
* A mutable meta node with attachable change listener * A mutable meta node with attachable change listener
*/ */
abstract class MutableMeta<M : MutableMeta<M>> : Meta<M> { abstract class MutableMetaNode<M : MutableMetaNode<M>> : MetaNode<M>(), MutableMeta<M> {
private val listeners = HashSet<MetaListener>() private val listeners = HashSet<MetaListener>()
/** /**
@ -57,14 +62,14 @@ abstract class MutableMeta<M : MutableMeta<M>> : Meta<M> {
/** /**
* Transform given meta to node type of this meta tree * Transform given meta to node type of this meta tree
*/ */
protected abstract fun wrap(meta: Meta<*>): M protected abstract fun wrap(meta: Meta): M
/** /**
* Create empty node * Create empty node
*/ */
protected abstract fun empty(): M protected abstract fun empty(): M
operator fun set(name: Name, item: MetaItem<M>) { override operator fun set(name: Name, item: MetaItem<M>) {
when (name.length) { when (name.length) {
0 -> error("Can't set meta item for empty name") 0 -> error("Can't set meta item for empty name")
1 -> { 1 -> {
@ -83,11 +88,11 @@ abstract class MutableMeta<M : MutableMeta<M>> : Meta<M> {
} }
operator fun set(name: Name, value: Value) = set(name, MetaItem.ValueItem(value)) operator fun set(name: Name, value: Value) = set(name, MetaItem.ValueItem(value))
operator fun set(name: Name, meta: Meta<*>) = set(name, MetaItem.SingleNodeItem(wrap(meta))) operator fun set(name: Name, meta: Meta) = set(name, MetaItem.SingleNodeItem(wrap(meta)))
operator fun set(name: Name, metas: List<Meta<*>>) = set(name, MetaItem.MultiNodeItem(metas.map { wrap(it) })) operator fun set(name: Name, metas: List<Meta>) = set(name, MetaItem.MultiNodeItem(metas.map { wrap(it) }))
operator fun set(name: String, item: MetaItem<M>) = set(name.toName(), item) operator fun set(name: String, item: MetaItem<M>) = set(name.toName(), item)
operator fun set(name: String, value: Value) = set(name.toName(), MetaItem.ValueItem(value)) operator fun set(name: String, value: Value) = set(name.toName(), MetaItem.ValueItem(value))
operator fun set(name: String, meta: Meta<*>) = set(name.toName(), MetaItem.SingleNodeItem(wrap(meta))) operator fun set(name: String, meta: Meta) = set(name.toName(), MetaItem.SingleNodeItem(wrap(meta)))
operator fun set(name: String, metas: List<Meta<*>>) = set(name.toName(), MetaItem.MultiNodeItem(metas.map { wrap(it) })) operator fun set(name: String, metas: List<Meta>) = set(name.toName(), MetaItem.MultiNodeItem(metas.map { wrap(it) }))
} }

View File

@ -65,7 +65,11 @@ data class NameToken internal constructor(val body: String, val query: String) {
if (body.isEmpty()) error("Syntax error: Name token body is empty") if (body.isEmpty()) error("Syntax error: Name token body is empty")
} }
override fun toString(): String = "$body[$query]" override fun toString(): String = if (hasQuery()) {
"$body[$query]"
} else {
body
}
fun hasQuery() = query.isNotEmpty() fun hasQuery() = query.isNotEmpty()
} }

View File

@ -0,0 +1,25 @@
package hep.dataforge.meta
import kotlin.test.Test
import kotlin.test.assertEquals
class MetaBuilderTest{
@Test
fun testBuilder(){
val meta = buildMeta {
"a" to 22
"b" to listOf(1,2,3)
this["c"] = "myValue".asValue()
"node" to {
"e" to 12.2
"childNode" to {
"f" to true
}
}
}
assertEquals(12.2, meta["node.e"]?.double)
assertEquals(true, meta["node.childNode.f"]?.boolean)
}
}

View File

@ -7,6 +7,6 @@ class NameTest{
@Test @Test
fun simpleName(){ fun simpleName(){
val name = "token1.token2.token3".toName() val name = "token1.token2.token3".toName()
assertEquals("token2", name[2].toString()) assertEquals("token2", name[1].toString())
} }
} }