All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.jetbrains.datalore.vis.svg.SvgElement.kt Maven / Gradle / Ivy

There is a newer version: 3.2.0
Show newest version
/*
 * Copyright (c) 2019. JetBrains s.r.o.
 * Use of this source code is governed by the MIT license that can be found in the LICENSE file.
 */

package jetbrains.datalore.vis.svg

import jetbrains.datalore.base.event.Event
import jetbrains.datalore.base.listMap.ListMap
import jetbrains.datalore.base.observable.event.EventHandler
import jetbrains.datalore.base.observable.event.ListenerCaller
import jetbrains.datalore.base.observable.event.Listeners
import jetbrains.datalore.base.observable.property.Property
import jetbrains.datalore.base.observable.property.PropertyChangeEvent
import jetbrains.datalore.base.observable.property.ReadableProperty
import jetbrains.datalore.base.registration.Registration
import jetbrains.datalore.vis.svg.event.SvgAttributeEvent
import jetbrains.datalore.vis.svg.event.SvgEventHandler
import jetbrains.datalore.vis.svg.event.SvgEventSpec

abstract class SvgElement : SvgNode() {

    companion object {
        private val ID: SvgAttributeSpec =
            SvgAttributeSpec.createSpec("id")
    }

    abstract val elementName: String

    private val myAttributes = AttributeMap()
    private var myListeners: Listeners? = null
    private val myEventPeer = SvgEventPeer()

    val ownerSvgElement: SvgSvgElement?
        get() {
            var cur: SvgNode? = this
            while (cur != null && cur !is SvgSvgElement) {
                cur = cur.parent().get()
            }
            return if (cur != null) {
                cur as SvgSvgElement?
            } else null
        }

    val attributeKeys: Set>
        get() = myAttributes.keySet()

    fun id(): Property {
        return getAttribute(ID)
    }

    fun handlersSet(): ReadableProperty> {
        return myEventPeer.handlersSet()
    }

    fun  addEventHandler(spec: SvgEventSpec, handler: SvgEventHandler): Registration {
        return myEventPeer.addEventHandler(spec, handler)
    }

    fun  dispatch(spec: SvgEventSpec, event: EventT) {
        myEventPeer.dispatch(spec, event, this)

        if (parent().get() != null && !event.isConsumed && parent().get() is SvgElement) {
            (parent().get() as SvgElement).dispatch(spec, event)
        }
    }

    private fun getSpecByName(name: String): SvgAttributeSpec {
        return SvgAttributeSpec.createSpec(name)
    }

    fun  getAttribute(spec: SvgAttributeSpec): Property {
        return object : Property {
            override val propExpr: String
                get() = "$this.$spec"

            override fun get(): ValueT? {
                return myAttributes[spec]
            }

            override fun set(value: ValueT?) {
                myAttributes[spec] = value
            }

            override fun addHandler(handler: EventHandler>): Registration {
                return addListener(object : SvgElementListener {
                    override fun onAttrSet(event: SvgAttributeEvent<*>) {
                        if (spec !== event.attrSpec) {
                            return
                        }
                        @Suppress("UNCHECKED_CAST")
                        val oldValue = event.oldValue as ValueT?
                        @Suppress("UNCHECKED_CAST")
                        val newValue = event.newValue as ValueT?
                        handler.onEvent(PropertyChangeEvent(oldValue, newValue))

                    }
                })
            }
        }
    }

    fun getAttribute(name: String): Property {
        val spec = getSpecByName(name)
        return getAttribute(spec)
    }

    fun  setAttribute(spec: SvgAttributeSpec, value: ValueT) {
        getAttribute(spec).set(value)
    }

    // if attr is one of pre-defined typed attrs (like CX in ellipse), the behaviour of this method is undefined
    fun setAttribute(name: String, value: String?) {
        getAttribute(name).set(value)
    }

    private fun onAttributeChanged(event: SvgAttributeEvent<*>) {
        if (myListeners != null) {
            myListeners!!.fire(object : ListenerCaller {
                override fun call(l: SvgElementListener) {
                    l.onAttrSet(event)
                }
            })
        }

        if (isAttached()) {
            container().attributeChanged(this, event)
        }
    }

    fun addListener(l: SvgElementListener): Registration {
        if (myListeners == null) {
            myListeners = Listeners()
        }
        val reg = myListeners!!.add(l)
        return object : Registration() {
            override fun doRemove() {
                reg.remove()
                if (myListeners!!.isEmpty) {
                    myListeners = null
                }
            }
        }
    }

    override fun toString(): String {
        return "<$elementName ${myAttributes.toSvgString()}>"
    }

    private inner class AttributeMap {
        private var myAttrs: ListMap, Any>? = null

        val isEmpty: Boolean
            get() = myAttrs == null || myAttrs!!.isEmpty

        fun size(): Int {
            return if (myAttrs == null) 0 else myAttrs!!.size()
        }

        fun containsKey(key: SvgAttributeSpec<*>): Boolean {
            return myAttrs != null && myAttrs!!.containsKey(key)
        }

        operator fun  get(spec: SvgAttributeSpec): ValueT? {
            return if (myAttrs != null && myAttrs!!.containsKey(spec)) {
                @Suppress("UNCHECKED_CAST")
                myAttrs!![spec] as ValueT
            } else null
        }

        operator fun  set(spec: SvgAttributeSpec, value: ValueT?): ValueT? {
            if (myAttrs == null) {
                myAttrs = ListMap()
            }

            val oldValue = if (value == null) {
                @Suppress("UNCHECKED_CAST")
                val v = myAttrs!!.remove(spec) as ValueT
                v
            } else {
                @Suppress("UNCHECKED_CAST")
                val v = myAttrs!!.put(spec, value) as ValueT
                v
            }

            if (value != oldValue) {
                val event = SvgAttributeEvent(spec, oldValue, value)
                [email protected](event)
            }

            return oldValue
        }

        fun  remove(spec: SvgAttributeSpec): ValueT? {
            return set(spec, null)
        }

        fun keySet(): Set> {
            return if (myAttrs == null) {
                emptySet()
            } else {
                val keySet = myAttrs!!.keySet()
                keySet
            }
        }

        internal fun toSvgString(): String {
            val builder = StringBuilder()
            for (spec in keySet()) {
                builder.append(spec.name)
                        .append("=\"")
                        .append(get(spec))
                        .append("\" ")
            }
            return builder.toString()
        }

        override fun toString(): String {
            return toSvgString()
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy