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

io.github.graphglue.model.property.NodeSetPropertyDelegate.kt Maven / Gradle / Ivy

Go to download

A library to develop annotation-based code-first GraphQL servers using GraphQL Kotlin, Spring Boot and Neo4j - excluding Spring GraphQL server dependencies

There is a newer version: 7.0.5
Show newest version
package io.github.graphglue.model.property

import io.github.graphglue.connection.model.Connection
import io.github.graphglue.data.execution.NodeQueryResult
import io.github.graphglue.data.repositories.RelationshipDiff
import io.github.graphglue.definition.FieldDefinition
import io.github.graphglue.definition.NodeDefinition
import io.github.graphglue.definition.NodeDefinitionCollection
import io.github.graphglue.definition.RelationshipDefinition
import io.github.graphglue.model.Node
import java.util.*
import kotlin.reflect.KProperty1
import kotlin.reflect.KTypeProjection
import kotlin.reflect.full.createType

/**
 * Property for the many side of a relation
 * Is mapped to a [Connection] in GraphQL
 *
 * @param parent see [BaseNodePropertyDelegate.parent]
 * @param property see [BaseNodePropertyDelegate.property]
 */
class NodeSetPropertyDelegate(
    parent: Node, property: KProperty1<*, *>
) : BaseNodePropertyDelegate.NodeSetProperty>(parent, property) {

    /**
     * The [NodeSetProperty] returned to the user
     */
    private val nodeSetProperty = NodeSetProperty()

    /**
     * Newly added nodes, relations must be added to the database
     */
    private val addedNodes = HashSet()

    /**
     * Newly removed nodes, relations must be removed from the database
     */
    private val removedNodes = HashSet()

    /**
     * Current set of nodes
     */
    private var currentNodes: MutableSet? = null

    /**
     * `true` iff the current values are loaded from the database or set via constructor
     */
    private val isLoaded get() = currentNodes != null

    override fun registerQueryResult(
        fieldDefinition: FieldDefinition,
        queryResult: NodeQueryResult
    ) {
        if (!isLoaded && queryResult.options.isAllQuery) {
            super.registerQueryResult(fieldDefinition, queryResult)
            currentNodes = HashSet(queryResult.nodes)
        }
    }

    override fun getRelationshipDiff(): RelationshipDiff {
        return RelationshipDiff(addedNodes, removedNodes.filter { it.rawId != null })
    }

    override fun getRelatedNodesToSave(): Collection {
        return addedNodes
    }

    override fun getLoadedRelatedNodes(): Collection {
        return currentNodes ?: emptyList()
    }

    /**
     * Ensures that this [NodeSetProperty] is loaded
     *
     * @param cache used to load nodes from, if provided, not loading deleted nodes
     * @param loader if provided used to define nested nodes to load
     */
    private suspend fun ensureLoaded(cache: NodeCache?, loader: (LazyLoadingSubqueryGenerator.() -> Unit)?) {
        if (!isLoaded) {
            if (parent.isPersisted) {
                parent.loadNodesOfRelationship(property, loader)
            } else {
                currentNodes = mutableSetOf()
            }
        }
        if (cache != null && nodeCache != cache) {
            nodeCache = cache
            currentNodes = currentNodes!!.mapNotNull(cache::getOrAdd).toMutableSet()
        }
    }

    override suspend fun getLoadedProperty(cache: NodeCache?, loader: (LazyLoadingSubqueryGenerator.() -> Unit)?): NodeSetProperty {
        ensureLoaded(cache, loader)
        return nodeSetProperty
    }

    override fun validate(
        savingNodes: Set,
        relationshipDefinition: RelationshipDefinition,
        nodeDefinitionCollection: NodeDefinitionCollection
    ) {}

    /**
     * Node property representing the many side of a node relation.
     * Provides set functionality.
     * The iterator supports remove.
     */
    inner class NodeSetProperty : AbstractSet(), MutableSet {

        override val size: Int
            get() {
                assert(isLoaded)
                return currentNodes!!.size
            }

        override fun contains(element: T): Boolean {
            assert(isLoaded)
            return currentNodes!!.contains(element)
        }

        override fun add(element: T): Boolean {
            assert(isLoaded)
            val result = currentNodes!!.add(element)
            if (result) {
                if (!removedNodes.remove(element)) {
                    addedNodes.add(element)
                }
            }
            return result
        }

        override fun remove(element: T): Boolean {
            assert(isLoaded)
            val result = currentNodes!!.remove(element)
            if (result) {
                if (!addedNodes.remove(element)) {
                    removedNodes.add(element)
                }
            }
            return result
        }

        override fun iterator(): MutableIterator {
            assert(isLoaded)
            return NodeSetIterator(currentNodes!!.iterator())
        }

        /**
         * Iterator which supports remove and delegates all other functionality to parent iterator
         */
        inner class NodeSetIterator(private val parentIterator: MutableIterator) : MutableIterator {
            /**
             * The current value, which should be removed on [remove]
             */
            private var current: T? = null

            override fun hasNext() = parentIterator.hasNext()

            override fun next(): T {
                val next = parentIterator.next()
                current = next
                return next
            }

            override fun remove() {
                parentIterator.remove()
                val current = current!!
                if (!addedNodes.remove(current)) {
                    removedNodes.add(current)
                }
            }

        }

    }
}

/**
 * Type which can be used to check the return type of node set properties
 */
val NODE_SET_PROPERTY_TYPE = LazyLoadingDelegate::class.createType(
    listOf(
        KTypeProjection.covariant(Node::class.createType(nullable = true)), KTypeProjection.covariant(
            NodeSetPropertyDelegate.NodeSetProperty::class.createType(
                listOf(
                    KTypeProjection.covariant(
                        Node::class.createType(nullable = true)
                    )
                )
            )
        )
    )
)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy