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
|
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)
|
@ -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
|
||||||
@ -31,3 +31,5 @@ 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<*>.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
|
||||||
|
@ -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) }))
|
||||||
}
|
}
|
@ -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()
|
||||||
}
|
}
|
||||||
|
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
|
@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())
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user