You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
126 lines
4.2 KiB
Kotlin
126 lines
4.2 KiB
Kotlin
package me.eater.threedom.dom
|
|
|
|
import me.eater.threedom.dom.event.DOMTreeUpdate
|
|
import me.eater.threedom.dom.event.NodeClassListUpdate
|
|
import me.eater.threedom.dom.event.NodeIDUpdate
|
|
import me.eater.threedom.event.Event
|
|
import me.eater.threedom.generated.EventNames
|
|
import kotlin.reflect.KClass
|
|
|
|
class Document : IDocument {
|
|
val root: PlainNode = PlainNode(this)
|
|
private val eventTree = EventTree {
|
|
on<DOMTreeUpdate.Remove> { (event) ->
|
|
removeNodeFromSearch(event.child)
|
|
}
|
|
|
|
on<DOMTreeUpdate.Insert> { (event) ->
|
|
addNodeToSearch(event.child)
|
|
}
|
|
|
|
on<NodeIDUpdate> { (event) ->
|
|
event.old?.let {
|
|
byId.remove(it, event.node.nodeId)
|
|
}
|
|
|
|
event.new?.let {
|
|
byId.putIfAbsent(it, event.node.nodeId)
|
|
}
|
|
}
|
|
|
|
on<NodeClassListUpdate.Added> { (event) ->
|
|
for (className in event.classNames) {
|
|
byClass.getOrPut(className, ::mutableSetOf).add(event.node.nodeId)
|
|
}
|
|
}
|
|
|
|
on<NodeClassListUpdate.Removed> { (event) ->
|
|
for (className in event.classNames) {
|
|
val set = byClass[className] ?: continue
|
|
|
|
set.remove(event.node.nodeId)
|
|
|
|
if (set.size == 0) {
|
|
byClass.remove(className)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private val allNodes: MutableMap<Long, INode<*>> = mutableMapOf()
|
|
private val byId: MutableMap<String, Long> = mutableMapOf()
|
|
private val byClass: MutableMap<String, MutableSet<Long>> = mutableMapOf()
|
|
|
|
override fun <T> addEventListener(eventName: String, refNode: INode<*>, block: (Event<T>) -> Unit) {
|
|
@Suppress("UNCHECKED_CAST")
|
|
eventTree.addEventListener(eventName, refNode, block as (Event<*>) -> Unit)
|
|
}
|
|
|
|
fun <T> addEventListener(eventName: String, block: (Event<T>) -> Unit) = addEventListener(eventName, root, block)
|
|
|
|
fun <T> addTopLevelEventListener(eventName: String, block: (Event<T>) -> Unit) =
|
|
@Suppress("UNCHECKED_CAST")
|
|
eventTree.addTopLevelEventListener(eventName, block as (Event<*>) -> Unit)
|
|
|
|
override fun trigger(eventName: String, targetNode: INode<*>, event: Event<*>) =
|
|
eventTree.trigger(eventName, targetNode, event)
|
|
|
|
|
|
override fun addNode(newNode: INode<*>) = root.addNode(newNode)
|
|
override fun removeNode(refNode: INode<*>) = root.removeNode(refNode)
|
|
|
|
override fun deleteNode(refNode: INode<*>) {
|
|
refNode.detachFromDocument()
|
|
eventTree.removeNode(refNode)
|
|
}
|
|
|
|
override fun removeAll() = root.removeAll()
|
|
|
|
override fun replaceNode(newNode: INode<*>, refNode: INode<*>): Boolean = root.replaceNode(newNode, refNode)
|
|
|
|
override fun hasChild(refNode: INode<*>): Boolean = root.hasChild(refNode)
|
|
|
|
override fun sequence(): Sequence<INode<*>> = root.sequence()
|
|
override fun iterator(): Iterator<INode<*>> = root.iterator()
|
|
|
|
override fun getNodeById(id: String): INode<*>? = byId[id]?.let(allNodes::get)
|
|
|
|
override fun getNodesByClassName(className: String): Sequence<INode<*>> =
|
|
byClass[className]?.asSequence()?.mapNotNull(allNodes::get) ?: emptySequence()
|
|
|
|
|
|
private fun addNodeToSearch(node: INode<*>) {
|
|
allNodes[node.nodeId] = node
|
|
|
|
node.id?.let { byId.putIfAbsent(it, node.nodeId) }
|
|
|
|
for (className in node.classList) {
|
|
byClass.getOrPut(className, ::mutableSetOf).add(node.nodeId)
|
|
}
|
|
}
|
|
|
|
private fun removeNodeFromSearch(node: INode<*>) {
|
|
allNodes.remove(node.nodeId)
|
|
|
|
node.id?.let { byId.remove(it, node.nodeId) }
|
|
|
|
for (className in node.classList) {
|
|
val set = byClass.get(className) ?: continue
|
|
set.remove(node.nodeId)
|
|
if (set.size == 0) {
|
|
byClass.remove(className)
|
|
}
|
|
}
|
|
}
|
|
|
|
inline fun <reified T> on(topLevel: Boolean = false, noinline block: (Event<T>) -> Unit) = if (topLevel)
|
|
addTopLevelEventListener(EventNames.getEventName<T>(), block)
|
|
else
|
|
addEventListener(EventNames.getEventName<T>(), block)
|
|
|
|
override fun <T : INode<T>> createNode(nodeType: KClass<T>): T =
|
|
nodeType.java.getConstructor(IDocument::class.java).newInstance(this)
|
|
}
|
|
|
|
|