Fix tests with new prototype location

This commit is contained in:
Alexander Nozik 2021-07-12 10:01:21 +03:00
parent 42e2530f6f
commit 073d374a9e
9 changed files with 96 additions and 130 deletions

View File

@ -10,7 +10,7 @@ import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertNotNull import kotlin.test.assertNotNull
class GDMLVisualTest { class GDMLVisionTest {
// @Test // @Test
// fun testCubesStyles(){ // fun testCubesStyles(){

View File

@ -28,11 +28,10 @@ internal data class PropertyListener(
*/ */
@Serializable @Serializable
@SerialName("vision") @SerialName("vision")
public open class VisionBase : Vision { public open class VisionBase(
override @Transient var parent: VisionGroup? = null,
protected var properties: Config? = null protected var properties: Config? = null
) : Vision {
@Transient
override var parent: VisionGroup? = null
@Synchronized @Synchronized
protected fun getOrCreateProperties(): Config { protected fun getOrCreateProperties(): Config {

View File

@ -0,0 +1,23 @@
package space.kscience.visionforge.gdml
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.plus
import space.kscience.visionforge.VisionGroup
private fun VisionGroup.countChildren(namePrefix: Name, cache: MutableMap<Name, Int> = hashMapOf()): Int {
var counter = 0
children.forEach { (token, child) ->
if (child is VisionGroup) {
counter += child.countChildren(namePrefix + token, cache)
} else {
counter++
}
}
cache[namePrefix] = counter
return counter
}
public fun VisionGroup.processLayers() {
}

View File

@ -26,7 +26,7 @@ class TestCubes {
val smallBoxPrototype = vision.getPrototype("solids.smallBox".toName()) as? Box val smallBoxPrototype = vision.getPrototype("solids.smallBox".toName()) as? Box
assertNotNull(smallBoxPrototype) assertNotNull(smallBoxPrototype)
assertEquals(30.0, smallBoxPrototype.xSize.toDouble()) assertEquals(30.0, smallBoxPrototype.xSize.toDouble())
val smallBoxVision = vision["composite-111.smallBox"]?.prototype as? Box val smallBoxVision = vision["composite-111.smallBox"]?.unref as? Box
assertNotNull(smallBoxVision) assertNotNull(smallBoxVision)
assertEquals(30.0, smallBoxVision.xSize.toDouble()) assertEquals(30.0, smallBoxVision.xSize.toDouble())
} }

View File

@ -5,7 +5,6 @@ import kotlinx.coroutines.withContext
import nl.adaptivity.xmlutil.StAXReader import nl.adaptivity.xmlutil.StAXReader
import space.kscience.gdml.Gdml import space.kscience.gdml.Gdml
import space.kscience.gdml.decodeFromReader import space.kscience.gdml.decodeFromReader
import space.kscience.visionforge.solid.prototype
import space.kscience.visionforge.visitor.countDistinct import space.kscience.visionforge.visitor.countDistinct
import space.kscience.visionforge.visitor.flowStatistics import space.kscience.visionforge.visitor.flowStatistics
import java.io.File import java.io.File
@ -23,7 +22,7 @@ suspend fun main() {
vision.flowStatistics<KClass<*>>{ _, child -> vision.flowStatistics<KClass<*>>{ _, child ->
child.prototype::class child::class
}.countDistinct().forEach { (depth, size) -> }.countDistinct().forEach { (depth, size) ->
println("$depth\t$size") println("$depth\t$size")
} }

View File

@ -34,6 +34,7 @@ import kotlin.reflect.KProperty
* Interface for 3-dimensional [Vision] * Interface for 3-dimensional [Vision]
*/ */
public interface Solid : Vision { public interface Solid : Vision {
override val descriptor: NodeDescriptor get() = Companion.descriptor override val descriptor: NodeDescriptor get() = Companion.descriptor
public companion object { public companion object {

View File

@ -12,11 +12,7 @@ import space.kscience.visionforge.VisionChange
@Serializable @Serializable
@SerialName("solid") @SerialName("solid")
public open class SolidBase( public open class SolidBase : VisionBase(), Solid {
// override var position: Point3D? = null,
// override var rotation: Point3D? = null,
// override var scale: Point3D? = null,
) : VisionBase(), Solid {
override val descriptor: NodeDescriptor get() = Solid.descriptor override val descriptor: NodeDescriptor get() = Solid.descriptor
override fun update(change: VisionChange) { override fun update(change: VisionChange) {

View File

@ -1,14 +1,7 @@
package space.kscience.visionforge.solid package space.kscience.visionforge.solid
import kotlinx.serialization.KSerializer
import kotlinx.serialization.PolymorphicSerializer
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import space.kscience.dataforge.meta.MetaItem
import space.kscience.dataforge.meta.descriptors.NodeDescriptor import space.kscience.dataforge.meta.descriptors.NodeDescriptor
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.NameToken
@ -27,37 +20,35 @@ public interface PrototypeHolder {
*/ */
@Serializable @Serializable
@SerialName("group.solid") @SerialName("group.solid")
public class SolidGroup( public class SolidGroup : VisionGroupBase(), Solid, PrototypeHolder {
@Serializable(PrototypeSerializer::class) internal var prototypes: MutableVisionGroup? = null,
) : VisionGroupBase(), Solid, PrototypeHolder {
init { override val children: Map<NameToken, Vision> get() = super.childrenInternal.filter { it.key != PROTOTYPES_TOKEN }
prototypes?.parent = this
public var prototypes: MutableVisionGroup?
get() = childrenInternal[PROTOTYPES_TOKEN] as? MutableVisionGroup
set(value) {
set(PROTOTYPES_TOKEN, value)
} }
override val descriptor: NodeDescriptor get() = Solid.descriptor override val descriptor: NodeDescriptor get() = Solid.descriptor
/** /**
* Ger a prototype redirecting the request to the parent if prototype is not found * Get a prototype redirecting the request to the parent if prototype is not found.
* If prototype is a ref, then it is unfolded until
*/ */
override fun getPrototype(name: Name): Solid? = override fun getPrototype(name: Name): Solid? =
(prototypes?.get(name) as? Solid) ?: (parent as? PrototypeHolder)?.getPrototype(name) prototypes?.get(name)?.unref ?: (parent as? PrototypeHolder)?.getPrototype(name)
/** /**
* Create or edit prototype node as a group * Create or edit prototype node as a group
*/ */
override fun prototypes(builder: VisionContainerBuilder<Solid>.() -> Unit): Unit { override fun prototypes(builder: VisionContainerBuilder<Solid>.() -> Unit): Unit {
(prototypes ?: Prototypes().also { (prototypes ?: SolidGroup().also {
prototypes = it prototypes = it
it.parent = this
}).run(builder) }).run(builder)
} }
// /**
// * TODO add special static group to hold statics without propagation
// */
// override fun addStatic(child: VisualObject) = setChild(NameToken("@static(${child.hashCode()})"), child)
override fun createGroup(): SolidGroup = SolidGroup() override fun createGroup(): SolidGroup = SolidGroup()
override fun update(change: VisionChange) { override fun update(change: VisionChange) {
@ -87,50 +78,3 @@ public fun VisionContainerBuilder<Vision>.group(
@VisionBuilder @VisionBuilder
public fun VisionContainerBuilder<Vision>.group(name: String, action: SolidGroup.() -> Unit = {}): SolidGroup = public fun VisionContainerBuilder<Vision>.group(name: String, action: SolidGroup.() -> Unit = {}): SolidGroup =
SolidGroup().apply(action).also { set(name, it) } SolidGroup().apply(action).also { set(name, it) }
/**
* A special class which works as a holder for prototypes
*/
internal class Prototypes(
children: MutableMap<NameToken, Vision> = hashMapOf(),
) : VisionGroupBase(children), PrototypeHolder {
override fun getProperty(
name: Name,
inherit: Boolean,
includeStyles: Boolean,
includeDefaults: Boolean,
): MetaItem? = null
override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) {
error("Can't set property of a prototypes container")
}
override val descriptor: NodeDescriptor? = null
override fun prototypes(builder: VisionContainerBuilder<Solid>.() -> Unit) {
apply(builder)
}
override fun getPrototype(name: Name): Solid? = get(name) as? Solid
}
internal class PrototypeSerializer : KSerializer<MutableVisionGroup> {
private val mapSerializer: KSerializer<Map<NameToken, Vision>> =
MapSerializer(
NameToken.serializer(),
PolymorphicSerializer(Vision::class)
)
override val descriptor: SerialDescriptor get() = mapSerializer.descriptor
override fun deserialize(decoder: Decoder): MutableVisionGroup {
val map = mapSerializer.deserialize(decoder)
return Prototypes(map.toMutableMap())
}
override fun serialize(encoder: Encoder, value: MutableVisionGroup) {
mapSerializer.serialize(encoder, value.children)
}
}

View File

@ -11,10 +11,27 @@ import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.* import space.kscience.dataforge.names.*
import space.kscience.visionforge.* import space.kscience.visionforge.*
public interface SolidReference : Solid {
public interface SolidReference : VisionGroup {
/**
* The prototype for this reference. Always returns a "real" prototype, not a reference
*/
public val prototype: Solid public val prototype: Solid
} }
/**
* Get a vision prototype if it is a [SolidReference] or vision itself if it is not.
* Unref is recursive, so it always returns a non-reference.
*/
public val Vision.unref: Solid
get() = when (this) {
is SolidReference -> prototype.unref
is Solid -> this
else -> error("This Vision is neither Solid nor SolidReference")
}
private fun SolidReference.getRefProperty( private fun SolidReference.getRefProperty(
name: Name, name: Name,
inherit: Boolean, inherit: Boolean,
@ -31,6 +48,12 @@ private fun SolidReference.getRefProperty(
} }
}.merge() }.merge()
private fun childToken(childName: Name): NameToken =
NameToken(SolidReferenceGroup.REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString())
private fun childPropertyName(childName: Name, propertyName: Name): Name =
childToken(childName) + propertyName
/** /**
* A reference [Solid] to reuse a template object * A reference [Solid] to reuse a template object
*/ */
@ -38,48 +61,24 @@ private fun SolidReference.getRefProperty(
@SerialName("solid.ref") @SerialName("solid.ref")
public class SolidReferenceGroup( public class SolidReferenceGroup(
public val refName: Name, public val refName: Name,
) : SolidBase(), SolidReference, VisionGroup { ) : VisionBase(), SolidReference, VisionGroup, Solid {
/** /**
* Recursively search for defined template in the parent * Recursively search for defined template in the parent
*/ */
override val prototype: Solid override val prototype: Solid by lazy {
get() {
if (parent == null) error("No parent is present for SolidReferenceGroup") if (parent == null) error("No parent is present for SolidReferenceGroup")
if (parent !is SolidGroup) error("Reference parent is not a group") if (parent !is PrototypeHolder) error("Parent does not hold prototypes")
return (parent as? SolidGroup)?.getPrototype(refName) (parent as? PrototypeHolder)?.getPrototype(refName) ?: error("Prototype with name $refName not found")
?: error("Prototype with name $refName not found")
} }
override val children: Map<NameToken, Vision> override val children: Map<NameToken, Vision>
get() = (prototype as? VisionGroup)?.children get() = (prototype as? VisionGroup)?.children
?.filter { !it.key.toString().startsWith("@") } ?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN }
?.mapValues { ?.mapValues {
ReferenceChild(it.key.asName()) ReferenceChild(this, it.key.asName())
} ?: emptyMap() } ?: emptyMap()
private fun childToken(childName: Name): NameToken =
NameToken(REFERENCE_CHILD_PROPERTY_PREFIX, childName.toString())
private fun childPropertyName(childName: Name, propertyName: Name): Name =
childToken(childName) + propertyName
private fun getChildProperty(childName: Name, propertyName: Name): MetaItem? {
return getOwnProperty(childPropertyName(childName, propertyName))
}
private fun setChildProperty(childName: Name, propertyName: Name, item: MetaItem?, notify: Boolean) {
setProperty(childPropertyName(childName, propertyName), item, notify)
}
private fun prototypeFor(name: Name): Solid {
return if (name.isEmpty()) prototype else {
val proto = (prototype as? SolidGroup)?.get(name)
?: error("Prototype with name $name not found in SolidReferenceGroup $refName")
proto as? Solid ?: error("Prototype with name $name is ${proto::class} but expected Solid")
}
}
override fun getProperty( override fun getProperty(
name: Name, name: Name,
inherit: Boolean, inherit: Boolean,
@ -94,20 +93,31 @@ public class SolidReferenceGroup(
* A ProxyChild is created temporarily only to interact with properties, it does not store any values * A ProxyChild is created temporarily only to interact with properties, it does not store any values
* (properties are stored in external cache) and created and destroyed on-demand). * (properties are stored in external cache) and created and destroyed on-demand).
*/ */
private inner class ReferenceChild(private val childName: Name) : SolidReference, VisionGroup { private class ReferenceChild(
override val prototype: Solid get() = prototypeFor(childName) val owner: SolidReferenceGroup,
private val childName: Name
) : SolidReference, VisionGroup {
override val prototype: Solid by lazy {
if (childName.isEmpty()) owner.prototype else {
val proto = (owner.prototype as? VisionGroup)?.get(childName)
?: error("Prototype with name $childName not found in SolidReferenceGroup ${owner.refName}")
proto.unref as? Solid ?: error("Prototype with name $childName is ${proto::class} but expected Solid")
}
}
override val children: Map<NameToken, Vision> override val children: Map<NameToken, Vision>
get() = (prototype as? VisionGroup)?.children get() = (prototype as? VisionGroup)?.children
?.filter { !it.key.toString().startsWith("@") } ?.filter { it.key != SolidGroup.PROTOTYPES_TOKEN }
?.mapValues { (key, _) -> ?.mapValues { (key, _) ->
ReferenceChild(childName + key.asName()) ReferenceChild(owner, childName + key.asName())
} ?: emptyMap() } ?: emptyMap()
override fun getOwnProperty(name: Name): MetaItem? = getChildProperty(childName, name) override fun getOwnProperty(name: Name): MetaItem? =
owner.getOwnProperty(childPropertyName(childName, name))
override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) { override fun setProperty(name: Name, item: MetaItem?, notify: Boolean) {
setChildProperty(childName, name, item, notify) owner.setProperty(childPropertyName(childName, name), item, notify)
} }
override fun getProperty( override fun getProperty(
@ -124,7 +134,7 @@ public class SolidReferenceGroup(
override var parent: VisionGroup? override var parent: VisionGroup?
get() { get() {
val parentName = childName.cutLast() val parentName = childName.cutLast()
return if (parentName.isEmpty()) this@SolidReferenceGroup else ReferenceChild(parentName) return if (parentName.isEmpty()) owner else ReferenceChild(owner, parentName)
} }
set(_) { set(_) {
error("Setting a parent for a reference child is not possible") error("Setting a parent for a reference child is not possible")
@ -132,7 +142,7 @@ public class SolidReferenceGroup(
@DFExperimental @DFExperimental
override val propertyChanges: Flow<Name> override val propertyChanges: Flow<Name>
get() = this@SolidReferenceGroup.propertyChanges.mapNotNull { name -> get() = owner.propertyChanges.mapNotNull { name ->
if (name.startsWith(childToken(childName))) { if (name.startsWith(childToken(childName))) {
name.cutFirst() name.cutFirst()
} else { } else {
@ -141,7 +151,7 @@ public class SolidReferenceGroup(
} }
override fun invalidateProperty(propertyName: Name) { override fun invalidateProperty(propertyName: Name) {
this@SolidReferenceGroup.invalidateProperty(childPropertyName(childName, propertyName)) owner.invalidateProperty(childPropertyName(childName, propertyName))
} }
override fun update(change: VisionChange) { override fun update(change: VisionChange) {
@ -159,12 +169,6 @@ public class SolidReferenceGroup(
} }
} }
/**
* Get a vision prototype if it is a [SolidReferenceGroup] or vision itself if it is not
*/
public val Vision.prototype: Vision
get() = if (this is SolidReference) prototype.prototype else this
/** /**
* Create ref for existing prototype * Create ref for existing prototype
*/ */