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

it.unibo.alchemist.model.Node.kt Maven / Gradle / Ivy

/*
 * Copyright (C) 2010-2023, Danilo Pianini and contributors
 * listed, for each module, in the respective subproject's build.gradle.kts file.
 *
 * This file is part of Alchemist, and is distributed under the terms of the
 * GNU General Public License, with a linking exception,
 * as described in the file LICENSE in the Alchemist distribution's top directory.
 */
package it.unibo.alchemist.model

import java.io.Serializable
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.jvm.jvmErasure

/**
 * @param 
 * The type of the concentration
 *
 * This interface must be implemented in every realization of node
 */
interface Node :
    Serializable,
    Iterable>,
    Comparable> {
    /**
     * Adds a reaction to this node.
     * The reaction is added only in the node,
     * but not in the [it.unibo.alchemist.core.Simulation] scheduler,
     * so it will never be executed.
     * To add the reaction also in the scheduler (and start to execute it),
     * you have to call also the method
     * [it.unibo.alchemist.core.Simulation.reactionAdded].
     *
     * @param reactionToAdd the reaction to be added
     */
    fun addReaction(reactionToAdd: Reaction)

    /**
     * Creates a new Node which is a clone of the current Node. The new Node
     * will have all the current Node's properties, such as reactions and
     * molecules, but it will also have a different ID.
     *
     * @param currentTime
     * the time at which the cloning operation happens
     *
     * @return A new Node which is a clone of the current one.
     *
     * @throws UnsupportedOperationException
     * if the implementation does not support node cloning.
     */
    fun cloneNode(currentTime: Time): Node

    /**
     * Tests whether a node contains a [Molecule].
     *
     * @param molecule
     * the molecule to check
     * @return true if the molecule is present, false otherwise
     */
    operator fun contains(molecule: Molecule): Boolean

    /**
     * Calculates the concentration of a molecule.
     *
     * @param molecule
     * the molecule whose concentration will be returned
     * @return the concentration of the molecule
     */
    fun getConcentration(molecule: Molecule): T

    /**
     * @return the molecule corresponding to the i-th position
     */
    val contents: Map

    /**
     * @return an univocal id for this node in the environment
     */
    val id: Int

    /**
     * @return the count of different molecules in this node
     */
    val moleculeCount: Int

    /**
     * @return a list of the node's properties/capabilities
     */
    val properties: List>

    /**
     * This method allows to access all the reaction of the node.
     *
     * @return the list of rections belonging to this node
     */
    val reactions: List>

    override fun hashCode(): Int

    override fun equals(other: Any?): Boolean

    /**
     * @param moleculeToRemove the molecule that should be removed
     */
    fun removeConcentration(moleculeToRemove: Molecule)

    /**
     * Removes a reaction from this node.
     * The reaction is removed only in the node,
     * but not in the [it.unibo.alchemist.core.Simulation] scheduler,
     * so the scheduler will continue to execute the reaction.
     * To remove the reaction also in the scheduler (and stop to execute it),
     * you have to call also the method [it.unibo.alchemist.core.Simulation.reactionRemoved].
     *
     * @param reactionToRemove the reaction to be removed
     */
    fun removeReaction(reactionToRemove: Reaction)

    /**
     * Sets the concentration of mol to c.
     *
     * @param molecule
     * the molecule you want to set the concentration
     * @param concentration
     * the concentration you want for mol
     */
    fun setConcentration(
        molecule: Molecule,
        concentration: T,
    )

    /**
     * Adds a capability to the node.
     * @param nodeProperty the capability you want to add to the node
     */
    fun addProperty(nodeProperty: NodeProperty)

    /**
     * returns a [NodeProperty] of the provided [superType] [C].
     * @param [C] type of capability
     * @param superType the type of capability to retrieve
     * @return a capability of the provided type [C]
     */
    fun > asPropertyOrNull(superType: Class): C? = asPropertyOrNull(superType.kotlin)

    /**
     * returns a [NodeProperty] of the provided [superType] [C].
     * @param [C] type of capability
     * @param superType the type of capability to retrieve
     * @return a capability of the provided type [C]
     */
    @Suppress("UNCHECKED_CAST")
    fun > asPropertyOrNull(superType: KClass): C? =
        when {
            properties.size <= 1 -> properties.firstOrNull()?.takeIfInstance(superType)
            else -> {
                val validProperties: List = properties.mapNotNull { it.takeIfInstance(superType) }
                when {
                    validProperties.size <= 1 -> validProperties.firstOrNull()
                    else -> {
                        validProperties
                            .mapNotNull { nodeProperty: NodeProperty ->
                                nodeProperty::class.distanceFrom(superType)?.let { nodeProperty to it }
                            }.minByOrNull { it.second }
                            ?.first as? C
                    }
                }
            }
        }

    /**
     * returns a [NodeProperty] of the provided [superType] [C].
     * @param [C] type of capability
     * @param superType the type of capability to retrieve
     * @return a capability of the provided type [C]
     */
    fun > asProperty(superType: KClass): C =
        requireNotNull(asPropertyOrNull(superType)) {
            "A ${superType.simpleName} is required for node ${this.id} but is missing"
        }

    /**
     * returns a [NodeProperty] of the provided [superType] [C].
     * @param [C] type of capability
     * @param superType the type of capability to retrieve
     * @return a capability of the provided type [C]
     */
    fun > asProperty(superType: Class): C = asProperty(superType.kotlin)

    /**
     * Utilities for [Node]s.
     */
    companion object {
        /**
         * returns a [NodeProperty] of the provided type [C].
         * @param [C] type of capability
         * @return a capability of the provided type [C]
         */
        inline fun > Node.asProperty(): C = asProperty(C::class)

        /**
         * returns a [NodeProperty] of the provided type [C] or null if the node does not have a compatible property.
         * @param [C] type of capability
         * @return if present, a capability of the provided type [C]
         */
        inline fun > Node.asPropertyOrNull(): C? =
            when {
                properties.size <= 1 -> properties.firstOrNull() as? C
                else -> {
                    val validProperties: List = properties.filterIsInstance()
                    when {
                        validProperties.size <= 1 ->
                            validProperties.firstOrNull()
                        else ->
                            validProperties
                                .mapNotNull { nodeProperty: NodeProperty ->
                                    nodeProperty::class.distanceFrom(C::class)?.let { nodeProperty to it }
                                }.minByOrNull { it.second }
                                ?.first as? C
                    }
                }
            }

        @JvmSynthetic @PublishedApi
        internal fun KClass<*>.distanceFrom(
            superType: KClass<*>,
            depth: Int = 0,
        ): Int? =
            when {
                !isSubclassOf(superType) -> null
                superType == this -> depth
                else ->
                    supertypes
                        .asSequence()
                        .map { it.jvmErasure }
                        .mapNotNull { it.distanceFrom(superType, depth + 1) }
                        .minOrNull()
            }

        @Suppress("UNCHECKED_CAST")
        private fun > NodeProperty.takeIfInstance(type: KClass): C? =
            this.takeIf { type.isInstance(it) }?.let { it as? C }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy