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

org.fernice.flare.style.Bloom.kt Maven / Gradle / Ivy

/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package org.fernice.flare.style

import org.fernice.flare.dom.Element
import org.fernice.flare.selector.BloomFilter
import org.fernice.flare.selector.CountingBloomFilter
import java.util.LinkedList
import java.util.Stack

class StyleBloom(
    private val filter: BloomFilter = CountingBloomFilter(),
) {
    private val elements = LinkedList()
    private val pushedHashes = LinkedList()

    fun filter(): BloomFilter {
        return filter
    }

    private inline fun forEachHash(element: Element, function: (Int) -> Unit) {
        element.namespace?.let { namespace ->
            function(namespace.hashCode())
        }

        function(element.localName.hashCode())

        element.id?.let { id ->
            function(id.hashCode())
        }

        for (styleClass in element.classes) {
            function(styleClass.hashCode())
        }
    }

    fun push(element: Element) {
        var count = 0
        forEachHash(element) { hash ->
            filter.insertHash(hash)
            pushedHashes.push(hash)
            count++
        }

        elements.push(PushedElement(element, count))
    }

    fun pop(): Element? {
        val element = if (elements.isEmpty()) {
            return null
        } else {
            elements.pop()
        }

        for (i in 0 until element.hashes) {
            val hash = pushedHashes.pop()
            filter.removeHash(hash)
        }

        return element.element
    }

    fun clear() {
        elements.clear()
        filter.clear()
        pushedHashes.clear()
    }

    fun rebuild(element: Element) {
        val parentsReversed = mutableListOf()

        var current = element

        loop@
        while (true) {
            current = current.traversalParent ?: break

            parentsReversed.add(0, current)
        }

        for (parent in parentsReversed) {
            push(parent)
        }
    }

    fun rebuildUntil(element: Element, destination: Element) {
        val parentsReversed = mutableListOf()

        var current = element

        loop@
        while (true) {
            current = current.traversalParent ?: break

            parentsReversed.add(0, current)

            if (destination === current) break
        }

        for (parent in parentsReversed) {
            push(parent)
        }
    }

    fun currentParent(): Element? {
        return if (elements.isEmpty()) {
            null
        } else {
            elements.peek().element
        }
    }

    fun insertParent(element: Element) {
        if (elements.isEmpty()) {
            rebuild(element)
            return
        }

        val traversalParent = element.traversalParent ?: return clear()

        // Current parent is present as stack is not empty
        if (currentParent() === traversalParent) {
            return
        } else {
            // We have no use for that parent
            pop()
        }

        loop@
        while (true) {
            val currentParent = pop() ?: break@loop
            if (currentParent === traversalParent) {
                rebuildUntil(element, currentParent)
                return
            }
        }

        rebuild(element)
    }

    private class PushedElement(val element: Element, val hashes: Int)
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy