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