/* * Copyright 2018-2023 KMath contributors. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ package space.kscience.attributes /** * A set of attributes. The implementation must guarantee that [content] keys correspond to their value types. */ public interface Attributes { /** * Raw content for this [Attributes] */ public val content: Map, Any?> /** * Attribute keys contained in this [Attributes] */ public val keys: Set> get() = content.keys /** * Provide an attribute value. Return null if attribute is not present or if its value is null. */ @Suppress("UNCHECKED_CAST") public operator fun get(attribute: Attribute): T? = content[attribute] as? T override fun toString(): String override fun equals(other: Any?): Boolean override fun hashCode(): Int public companion object { public val EMPTY: Attributes = AttributesImpl(emptyMap()) public fun equals(a1: Attributes, a2: Attributes): Boolean = a1.keys == a2.keys && a1.keys.all { a1[it] == a2[it] } } } internal class AttributesImpl(override val content: Map, Any?>) : Attributes { override fun toString(): String = "Attributes(value=${content.entries})" override fun equals(other: Any?): Boolean = other is Attributes && Attributes.equals(this, other) override fun hashCode(): Int = content.hashCode() } public fun Attributes.isEmpty(): Boolean = content.isEmpty() /** * Get attribute value or default */ public fun Attributes.getOrDefault(attribute: AttributeWithDefault): T = get(attribute) ?: attribute.default /** * Check if there is an attribute that matches given key by type and adheres to [predicate]. */ @Suppress("UNCHECKED_CAST") public inline fun > Attributes.hasAny(predicate: (value: T) -> Boolean): Boolean = content.any { (mapKey, mapValue) -> mapKey is A && predicate(mapValue as T) } /** * Check if there is an attribute of given type (subtypes included) */ public inline fun > Attributes.hasAny(): Boolean = content.any { (mapKey, _) -> mapKey is A } /** * Check if [Attributes] contains a flag. Multiple keys that are instances of a flag could be present */ public inline fun Attributes.hasFlag(): Boolean = content.keys.any { it is A } /** * Create [Attributes] with an added or replaced attribute key. */ public fun > Attributes.withAttribute( attribute: A, attrValue: T, ): Attributes = AttributesImpl(content + (attribute to attrValue)) public fun > Attributes.withAttribute(attribute: A): Attributes = withAttribute(attribute, Unit) /** * Create a new [Attributes] by modifying the current one */ public fun Attributes.modify(block: AttributesBuilder.() -> Unit): Attributes = Attributes { from(this@modify) block() } /** * Create new [Attributes] by removing [attribute] key */ public fun Attributes.withoutAttribute(attribute: Attribute<*>): Attributes = AttributesImpl(content.minus(attribute)) /** * Add an element to a [SetAttribute] */ public fun > Attributes.withAttributeElement( attribute: A, attrValue: T, ): Attributes { val currentSet: Set = get(attribute) ?: emptySet() return AttributesImpl( content + (attribute to (currentSet + attrValue)) ) } /** * Remove an element from [SetAttribute] */ public fun > Attributes.withoutAttributeElement( attribute: A, attrValue: T, ): Attributes { val currentSet: Set = get(attribute) ?: emptySet() return AttributesImpl(content + (attribute to (currentSet - attrValue))) } /** * Create [Attributes] with a single key */ public fun > Attributes( attribute: A, attrValue: T, ): Attributes = AttributesImpl(mapOf(attribute to attrValue)) /** * Create Attributes with a single [Unit] valued attribute */ public fun > Attributes( attribute: A, ): Attributes = AttributesImpl(mapOf(attribute to Unit)) public operator fun Attributes.plus(other: Attributes): Attributes = AttributesImpl(content + other.content)