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

org.fernice.flare.style.ruletree.RuleTree.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.ruletree

import org.fernice.flare.style.Importance
import org.fernice.flare.style.Origin
import org.fernice.flare.style.StyleSourceAndCascadePriority
import org.fernice.flare.style.source.StyleSource
import org.fernice.flare.style.stylesheet.LayerOrder
import org.fernice.std.Recycler
import java.lang.ref.WeakReference
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicReference

enum class CascadeLevel {

    UserAgentNormal,

    UserNormal,

    AuthorNormal,

    AuthorImportant,

    UserImportant,

    UserAgentImportant;

    val origin: Origin
        get() = when (this) {
            UserAgentNormal -> Origin.UserAgent
            UserNormal -> Origin.User
            AuthorNormal -> Origin.Author
            AuthorImportant -> Origin.Author
            UserImportant -> Origin.User
            UserAgentImportant -> Origin.UserAgent
        }

    val importance: Importance
        get() = if (isImportant) Importance.Important else Importance.Normal

    val isImportant: Boolean
        get() {
            return when (this) {
                AuthorImportant,
                UserImportant,
                UserAgentImportant,
                -> true

                else -> false
            }
        }

    fun unimportant(): CascadeLevel {
        return when (this) {
            UserAgentNormal, UserAgentImportant -> UserAgentNormal
            UserNormal, UserImportant -> UserNormal
            AuthorNormal, AuthorImportant -> AuthorNormal
        }
    }

    fun important(): CascadeLevel {
        return when (this) {
            UserAgentNormal, UserAgentImportant -> UserAgentImportant
            UserNormal, UserImportant -> UserImportant
            AuthorNormal, AuthorImportant -> AuthorImportant
        }
    }

    companion object {
        fun of(origin: Origin, importance: Importance): CascadeLevel {
            return when (origin) {
                Origin.UserAgent -> if (importance == Importance.Important) UserAgentImportant else UserAgentNormal
                Origin.User -> if (importance == Importance.Important) UserImportant else UserNormal
                Origin.Author -> if (importance == Importance.Important) AuthorImportant else AuthorNormal
            }
        }
    }
}

data class CascadePriority(
    val level: CascadeLevel,
    val layerOrder: LayerOrder,
) : Comparable {

    fun unimportant(): CascadePriority = CascadePriority(level.unimportant(), layerOrder)
    fun important(): CascadePriority = CascadePriority(level.important(), layerOrder)

    override fun compareTo(other: CascadePriority): Int {
        val c = level.compareTo(other.level)
        if (c != 0) return c
        return layerOrder.compareTo(other.layerOrder)
    }

    companion object {
        fun of(level: CascadeLevel, layerOrder: LayerOrder): CascadePriority {
            return CascadePriority(level, layerOrder)
        }
    }
}

private class ImportantDeclarations {
    val userAgent: MutableList = arrayListOf()
    val user: MutableList = arrayListOf()
    val author: MutableList = arrayListOf()
}

private val ImportantDeclarationsRecycler = Recycler(
    factory = { ImportantDeclarations() },
    reset = {
        it.userAgent.clear()
        it.user.clear()
        it.author.clear()
    },
)

private const val DORMANT_CYCLES = 1000

class RuleTree {
    val root: RuleNode = RuleNode.root()

    fun computedRuleNode(iterator: Iterator): RuleNode {
        var current = root
        var lastPriority = current.priority
        var seenImportant = false

        val importantDeclarations = ImportantDeclarationsRecycler.acquire()

        for (element in iterator) {
            val (source, priority) = element
            assert(!priority.level.isImportant) { "important level are only allowed internally" }
            assert(priority >= lastPriority) { "illegal order: $priority < $lastPriority" }

            val hasImportant = source.declarations.hasImportant()
            if (hasImportant) {
                seenImportant = true

                when (priority.level) {
                    CascadeLevel.AuthorNormal -> importantDeclarations.author.add(element)
                    CascadeLevel.UserNormal -> importantDeclarations.user.add(element)
                    CascadeLevel.UserAgentNormal -> importantDeclarations.userAgent.add(element)
                    else -> {}
                }
            }

            current = current.ensureChild(root, source, priority)
            lastPriority = priority
        }

        if (!seenImportant) {
            ImportantDeclarationsRecycler.release(importantDeclarations)
            return current
        }

        for ((source, priority) in importantDeclarations.author) {
            current = current.ensureChild(root, source, priority.important())
        }
        for ((source, priority) in importantDeclarations.user) {
            current = current.ensureChild(root, source, priority.important())
        }
        for ((source, priority) in importantDeclarations.userAgent) {
            current = current.ensureChild(root, source, priority.important())
        }

        ImportantDeclarationsRecycler.release(importantDeclarations)

        return current
    }

    private val cycles = AtomicInteger(DORMANT_CYCLES)

    fun gc() {
        val cycle = cycles.updateAndGet { if (it == 0) DORMANT_CYCLES else (it - 1) }
        if (cycle == DORMANT_CYCLES || root.isGcRequested) {
            performGc()
        }
    }

    private fun performGc() {
        root.performGc()
    }

    fun clear() {
        root.clear()
    }
}

private val RULE_NODE_NUMBER_RANGE = AtomicInteger()

class RuleNode private constructor(
    val root: RuleNode?,
    val parent: RuleNode?,
    val source: WeakReference?,
    val priority: CascadePriority,
) {

    private val firstChildReference = AtomicReference()
    private val nextSiblingReference = AtomicReference()
    private val previousSiblingReference = AtomicReference()

    internal val number = RULE_NODE_NUMBER_RANGE.getAndIncrement()

    fun ensureChild(
        root: RuleNode?,
        source: StyleSource,
        priority: CascadePriority,
    ): RuleNode {
        // Find the last child to append the new node after it
        var lastChild: RuleNode? = firstChildReference.get()
        while (lastChild != null) {
            val sourceReference = lastChild.source
            if (sourceReference != null && sourceReference.get() == null) {
                gc()
            }

            // Check whether we've already inserted the rule to allow
            // for reusing of existing nodes and minimizing the tree
            if (lastChild.priority == priority && sourceReference?.get() == source) {
                return lastChild
            }

            lastChild = lastChild.nextSiblingReference.get() ?: break
        }

        // We haven't found a preexisting node, create new one
        val node = RuleNode(root, this, WeakReference(source), priority)

        while (true) {
            // Retrieve the references of the sibling or first child,
            // if there are no children yet
            val siblingReference = when (lastChild) {
                null -> firstChildReference
                else -> lastChild.nextSiblingReference
            }

            // Try to append the node to the last sibling. Synchronize
            // only for the writing, reading might still have been dirty.
            synchronized(this) {
                if (siblingReference.compareAndSet(null, node)) {
                    lastChild?.previousSiblingReference?.set(node)

                    return node
                }
            }

            // Some other thread appended some node to the last sibling
            val next = siblingReference.get()

            // Check the appended node on whether we can reuse it
            if (next?.priority == priority && next.source?.get() == source) {
                return next
            }

            // try again with the new last child
            lastChild = next ?: lastChild
        }
    }

    private val gcRequested = AtomicBoolean(false)
    val isGcRequested: Boolean
        get() = gcRequested.get()

    fun gc() {
        if (!gcRequested.getAndSet(true)) {
            parent?.gc()
        }
    }

    internal fun performGc() {
        var child = firstChildReference.get()

        while (child != null) {
            // contents of this node have unrecoverably vacated, remove the node
            val declarationsReference = child.source
            if (declarationsReference != null && declarationsReference.get() == null) {
                do {
                    // consistency is guarded by the forward reference, find who's referring
                    // to the node
                    val previousSibling = child.previousSiblingReference.get()
                    val nextSiblingReference = when (previousSibling) {
                        null -> firstChildReference
                        else -> previousSibling.nextSiblingReference
                    }

                    val target = nextSiblingReference.get()

                    // someone else might already have removed this child
                    if (target !== child) break

                    val successful = synchronized(this) {
                        val nextSibling = target.nextSiblingReference.get()
                        if (nextSiblingReference.compareAndSet(target, nextSibling)) {
                            nextSibling?.previousSiblingReference?.set(previousSibling)
                            true
                        } else {
                            false
                        }
                    }
                } while (!successful)
            }

            child.performGc()

            child = child.nextSiblingReference.get()
        }
    }

    fun clear() {
        firstChildReference.set(null)
        previousSiblingReference.set(null)
        nextSiblingReference.set(null)
    }

    val firstChild: RuleNode? get() = firstChildReference.get()
    val previousSibling: RuleNode? get() = previousSiblingReference.get()
    val nextSibling: RuleNode? get() = nextSiblingReference.get()

    fun selfAndAncestors(): SelfAndAncestors {
        return SelfAndAncestors(this)
    }

    fun childrenIterator(): Iterator {
        return RuleNodeChildrenIterator(firstChild)
    }

    override fun toString(): String {
        return "RuleNode[$number](source: ${source?.get()} level: ${priority.level} layerOrder: ${priority.layerOrder})"
    }

    companion object {
        fun root(): RuleNode {
            return RuleNode(
                root = null,
                parent = null,
                source = null,
                priority = CascadePriority.of(CascadeLevel.UserAgentNormal, LayerOrder.Root),
            )
        }
    }
}

class SelfAndAncestors(private val current: RuleNode) : Sequence {

    override fun iterator(): Iterator {
        return SelfAndAncestorsIterator(current)
    }

    class SelfAndAncestorsIterator(private var current: RuleNode?) : Iterator {

        override fun hasNext(): Boolean = current != null

        override fun next(): RuleNode {
            val next = current ?: throw NoSuchElementException()
            current = next.parent
            return next
        }
    }
}

class RuleNodeChildrenIterator(private var current: RuleNode?) : Iterator {

    override fun hasNext(): Boolean = current != null

    override fun next(): RuleNode {
        val actual = current ?: throw NoSuchElementException()
        current = actual.nextSibling
        return actual
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy