Optimized generics added jvm module for tests
This commit is contained in:
parent
4ca81518d5
commit
81660f3ca7
27
dataforge-meta-jvm/build.gradle
Normal file
27
dataforge-meta-jvm/build.gradle
Normal 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"
|
23
src/main/kotlin/hep/dataforge/meta/Configuration.kt
Normal file
23
src/main/kotlin/hep/dataforge/meta/Configuration.kt
Normal 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()
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package hep.dataforge.meta
|
||||
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.NameToken
|
||||
import hep.dataforge.names.toName
|
||||
|
||||
/**
|
||||
@ -10,13 +9,13 @@ import hep.dataforge.names.toName
|
||||
* * a single node
|
||||
* * a list of nodes
|
||||
*/
|
||||
sealed class MetaItem<M : Meta<M>> {
|
||||
class ValueItem<M : Meta<M>>(val value: Value) : MetaItem<M>()
|
||||
class SingleNodeItem<M : Meta<M>>(val node: M) : MetaItem<M>()
|
||||
class MultiNodeItem<M : Meta<M>>(val nodes: List<M>) : MetaItem<M>()
|
||||
sealed class MetaItem<M : Meta> {
|
||||
class ValueItem<M : Meta>(val value: Value) : MetaItem<M>()
|
||||
class SingleNodeItem<M : Meta>(val node: 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()) {
|
||||
first()
|
||||
} else {
|
||||
@ -31,8 +30,26 @@ operator fun <M : Meta<M>> List<M>.get(query: String): M? {
|
||||
* * [MetaItem.SingleNodeItem] single node
|
||||
* * [MetaItem.MultiNodeItem] multi-value node
|
||||
*/
|
||||
interface Meta<M : Meta<M>> {
|
||||
val items: Map<String, MetaItem<M>>
|
||||
interface Meta {
|
||||
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>? {
|
||||
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
|
||||
*/
|
||||
class SealedMeta(meta: Meta<*>) : Meta<SealedMeta> {
|
||||
class SealedMeta(meta: Meta) : MetaNode<SealedMeta>() {
|
||||
override val items: Map<String, MetaItem<SealedMeta>> = if (meta is SealedMeta) {
|
||||
meta.items
|
||||
} 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
|
||||
*/
|
||||
fun Meta<*>.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(this)
|
||||
fun Meta.seal(): SealedMeta = this as? SealedMeta ?: SealedMeta(this)
|
@ -3,8 +3,8 @@ package hep.dataforge.meta
|
||||
/**
|
||||
* DSL builder for meta
|
||||
*/
|
||||
class MetaBuilder : MutableMeta<MetaBuilder>() {
|
||||
override fun wrap(meta: Meta<*>): MetaBuilder = meta.builder()
|
||||
class MetaBuilder : MutableMetaNode<MetaBuilder>() {
|
||||
override fun wrap(meta: Meta): MetaBuilder = meta.builder()
|
||||
override fun empty(): MetaBuilder = MetaBuilder()
|
||||
|
||||
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
|
||||
*/
|
||||
fun Meta<*>.builder(): MetaBuilder {
|
||||
fun Meta.builder(): MetaBuilder {
|
||||
return MetaBuilder().also { builder ->
|
||||
items.mapValues { entry ->
|
||||
val item = entry.value
|
||||
@ -30,4 +30,6 @@ fun Meta<*>.builder(): MetaBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun buildMeta(builder: MetaBuilder.() -> Unit): Meta = MetaBuilder().apply(builder)
|
@ -13,7 +13,7 @@ val MetaItem<*>.double get() = number.toDouble()
|
||||
val MetaItem<*>.int get() = number.toInt()
|
||||
val MetaItem<*>.long get() = number.toLong()
|
||||
|
||||
val <M : Meta<M>> MetaItem<M>.node: M
|
||||
val <M : Meta> MetaItem<M>.node: M
|
||||
get() = when (this) {
|
||||
is MetaItem.ValueItem -> error("Trying to interpret value meta item as node item")
|
||||
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.
|
||||
* 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) {
|
||||
is MetaItem.ValueItem -> emptyList()//error("Trying to interpret value meta item as node item")
|
||||
is MetaItem.SingleNodeItem -> listOf(node)
|
||||
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) {
|
||||
is MetaItem.ValueItem -> -1
|
||||
is MetaItem.SingleNodeItem -> if (node == meta) 0 else -1
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
interface MutableMeta<M : MutableMeta<M>>: Meta{
|
||||
operator fun set(name: Name, item: MetaItem<M>)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>()
|
||||
|
||||
/**
|
||||
@ -57,14 +62,14 @@ abstract class MutableMeta<M : MutableMeta<M>> : Meta<M> {
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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) {
|
||||
0 -> error("Can't set meta item for empty name")
|
||||
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, 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, 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: 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, 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, 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) }))
|
||||
}
|
@ -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")
|
||||
}
|
||||
|
||||
override fun toString(): String = "$body[$query]"
|
||||
override fun toString(): String = if (hasQuery()) {
|
||||
"$body[$query]"
|
||||
} else {
|
||||
body
|
||||
}
|
||||
|
||||
fun hasQuery() = query.isNotEmpty()
|
||||
}
|
||||
|
25
src/test/kotlin/hep/dataforge/meta/MetaBuilderTest.kt
Normal file
25
src/test/kotlin/hep/dataforge/meta/MetaBuilderTest.kt
Normal 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)
|
||||
}
|
||||
|
||||
}
|
@ -7,6 +7,6 @@ class NameTest{
|
||||
@Test
|
||||
fun simpleName(){
|
||||
val name = "token1.token2.token3".toName()
|
||||
assertEquals("token2", name[2].toString())
|
||||
assertEquals("token2", name[1].toString())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user