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

kotlin.collections.Maps.kt Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

@file:kotlin.jvm.JvmMultifileClass
@file:kotlin.jvm.JvmName("MapsKt")

package kotlin.collections

import kotlin.contracts.*

private object EmptyMap : Map, Serializable {
    private const val serialVersionUID: Long = 8246714829545688274

    override fun equals(other: Any?): Boolean = other is Map<*, *> && other.isEmpty()
    override fun hashCode(): Int = 0
    override fun toString(): String = "{}"

    override val size: Int get() = 0
    override fun isEmpty(): Boolean = true

    override fun containsKey(key: Any?): Boolean = false
    override fun containsValue(value: Nothing): Boolean = false
    override fun get(key: Any?): Nothing? = null
    override val entries: Set> get() = EmptySet
    override val keys: Set get() = EmptySet
    override val values: Collection get() = EmptyList

    private fun readResolve(): Any = EmptyMap
}

/**
 * Returns an empty read-only map of specified type.
 *
 * The returned map is serializable (JVM).
 * @sample samples.collections.Maps.Instantiation.emptyReadOnlyMap
 */
public fun  emptyMap(): Map = @Suppress("UNCHECKED_CAST") (EmptyMap as Map)

/**
 * Returns a new read-only map with the specified contents, given as a list of pairs
 * where the first value is the key and the second is the value.
 *
 * If multiple pairs have the same key, the resulting map will contain the value from the last of those pairs.
 *
 * Entries of the map are iterated in the order they were specified.
 *
 * The returned map is serializable (JVM).
 *
 * @sample samples.collections.Maps.Instantiation.mapFromPairs
 */
public fun  mapOf(vararg pairs: Pair): Map =
    if (pairs.size > 0) pairs.toMap(LinkedHashMap(mapCapacity(pairs.size))) else emptyMap()

/**
 * Returns an empty read-only map.
 *
 * The returned map is serializable (JVM).
 * @sample samples.collections.Maps.Instantiation.emptyReadOnlyMap
 */
@kotlin.internal.InlineOnly
public inline fun  mapOf(): Map = emptyMap()

/**
 * Returns an empty new [MutableMap].
 *
 * The returned map preserves the entry iteration order.
 * @sample samples.collections.Maps.Instantiation.emptyMutableMap
 */
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun  mutableMapOf(): MutableMap = LinkedHashMap()

/**
 * Returns a new [MutableMap] with the specified contents, given as a list of pairs
 * where the first component is the key and the second is the value.
 *
 * If multiple pairs have the same key, the resulting map will contain the value from the last of those pairs.
 *
 * Entries of the map are iterated in the order they were specified.
 *
 * @sample samples.collections.Maps.Instantiation.mutableMapFromPairs
 * @sample samples.collections.Maps.Instantiation.emptyMutableMap
 */
public fun  mutableMapOf(vararg pairs: Pair): MutableMap =
    LinkedHashMap(mapCapacity(pairs.size)).apply { putAll(pairs) }

/**
 * Returns an empty new [HashMap].
 */
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun  hashMapOf(): HashMap = HashMap()

/**
 * Returns a new [HashMap] with the specified contents, given as a list of pairs
 * where the first component is the key and the second is the value.
 *
 * @sample samples.collections.Maps.Instantiation.hashMapFromPairs
 */
public fun  hashMapOf(vararg pairs: Pair): HashMap = HashMap(mapCapacity(pairs.size)).apply { putAll(pairs) }

/**
 * Returns an empty new [LinkedHashMap].
 */
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun  linkedMapOf(): LinkedHashMap = LinkedHashMap()

/**
 * Returns a new [LinkedHashMap] with the specified contents, given as a list of pairs
 * where the first component is the key and the second is the value.
 *
 * If multiple pairs have the same key, the resulting map will contain the value from the last of those pairs.
 *
 * Entries of the map are iterated in the order they were specified.
 *
 * @sample samples.collections.Maps.Instantiation.linkedMapFromPairs
 */
public fun  linkedMapOf(vararg pairs: Pair): LinkedHashMap = pairs.toMap(LinkedHashMap(mapCapacity(pairs.size)))

/**
 * Calculate the initial capacity of a map, based on Guava's com.google.common.collect.Maps approach. This is equivalent
 * to the Collection constructor for HashSet, (c.size()/.75f) + 1, but provides further optimisations for very small or
 * very large sizes, allows support non-collection classes, and provides consistency for all map based class construction.
 */
@PublishedApi
internal fun mapCapacity(expectedSize: Int): Int {
    if (expectedSize < 3) {
        return expectedSize + 1
    }
    if (expectedSize < INT_MAX_POWER_OF_TWO) {
        return expectedSize + expectedSize / 3
    }
    return Int.MAX_VALUE // any large value
}

private const val INT_MAX_POWER_OF_TWO: Int = Int.MAX_VALUE / 2 + 1

/** Returns `true` if this map is not empty. */
@kotlin.internal.InlineOnly
public inline fun  Map.isNotEmpty(): Boolean = !isEmpty()

/**
 * Returns `true` if this nullable map is either null or empty.
 * @sample samples.collections.Maps.Usage.mapIsNullOrEmpty
 */
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun  Map?.isNullOrEmpty(): Boolean {
    contract {
        returns(false) implies (this@isNullOrEmpty != null)
    }

    return this == null || isEmpty()
}

/**
 * Returns the [Map] if its not `null`, or the empty [Map] otherwise.
 *
 * @sample samples.collections.Maps.Usage.mapOrEmpty
 */
@kotlin.internal.InlineOnly
public inline fun  Map?.orEmpty(): Map = this ?: emptyMap()

/**
 * Returns this map if it's not empty
 * or the result of calling [defaultValue] function if the map is empty.
 *
 * @sample samples.collections.Maps.Usage.mapIfEmpty
 */
@SinceKotlin("1.3")
@kotlin.internal.InlineOnly
public inline fun  M.ifEmpty(defaultValue: () -> R): R where M : Map<*, *>, M : R =
    if (isEmpty()) defaultValue() else this

/**
 * Checks if the map contains the given key.
 *
 * This method allows to use the `x in map` syntax for checking whether an object is contained in the map.
 */
@kotlin.internal.InlineOnly
public inline operator fun <@kotlin.internal.OnlyInputTypes K, V> Map.contains(key: K): Boolean = containsKey(key)

/**
 * Returns the value corresponding to the given [key], or `null` if such a key is not present in the map.
 */
@kotlin.internal.InlineOnly
public inline operator fun <@kotlin.internal.OnlyInputTypes K, V> Map.get(key: K): V? =
    @Suppress("UNCHECKED_CAST") (this as Map).get(key)

/**
 * Allows to use the index operator for storing values in a mutable map.
 */
@kotlin.internal.InlineOnly
public inline operator fun  MutableMap.set(key: K, value: V): Unit {
    put(key, value)
}

/**
 * Returns `true` if the map contains the specified [key].
 *
 * Allows to overcome type-safety restriction of `containsKey` that requires to pass a key of type `K`.
 */
@kotlin.internal.InlineOnly
public inline fun <@kotlin.internal.OnlyInputTypes K> Map.containsKey(key: K): Boolean =
    @Suppress("UNCHECKED_CAST") (this as Map).containsKey(key)

/**
 * Returns `true` if the map maps one or more keys to the specified [value].
 *
 * Allows to overcome type-safety restriction of `containsValue` that requires to pass a value of type `V`.
 *
 * @sample samples.collections.Maps.Usage.containsValue
 */
@Suppress("EXTENSION_SHADOWED_BY_MEMBER") // false warning, extension takes precedence in some cases
@kotlin.internal.InlineOnly
public inline fun  Map.containsValue(value: V): Boolean = this.containsValue(value)


/**
 * Removes the specified key and its corresponding value from this map.
 *
 * @return the previous value associated with the key, or `null` if the key was not present in the map.

 * Allows to overcome type-safety restriction of `remove` that requires to pass a key of type `K`.
 */
@kotlin.internal.InlineOnly
public inline fun <@kotlin.internal.OnlyInputTypes K, V> MutableMap.remove(key: K): V? =
    @Suppress("UNCHECKED_CAST") (this as MutableMap).remove(key)

/**
 * Returns the key component of the map entry.
 *
 * This method allows to use destructuring declarations when working with maps, for example:
 * ```
 * for ((key, value) in map) {
 *     // do something with the key and the value
 * }
 * ```
 */
@kotlin.internal.InlineOnly
public inline operator fun  Map.Entry.component1(): K = key

/**
 * Returns the value component of the map entry.
 *
 * This method allows to use destructuring declarations when working with maps, for example:
 * ```
 * for ((key, value) in map) {
 *     // do something with the key and the value
 * }
 * ```
 */
@kotlin.internal.InlineOnly
public inline operator fun  Map.Entry.component2(): V = value

/**
 * Converts entry to [Pair] with key being first component and value being second.
 */
@kotlin.internal.InlineOnly
public inline fun  Map.Entry.toPair(): Pair = Pair(key, value)

/**
 * Returns the value for the given key, or the result of the [defaultValue] function if there was no entry for the given key.
 *
 * @sample samples.collections.Maps.Usage.getOrElse
 */
@kotlin.internal.InlineOnly
public inline fun  Map.getOrElse(key: K, defaultValue: () -> V): V = get(key) ?: defaultValue()


internal inline fun  Map.getOrElseNullable(key: K, defaultValue: () -> V): V {
    val value = get(key)
    if (value == null && !containsKey(key)) {
        return defaultValue()
    } else {
        @Suppress("UNCHECKED_CAST")
        return value as V
    }
}

/**
 * Returns the value for the given [key] or throws an exception if there is no such key in the map.
 *
 * If the map was created by [withDefault], resorts to its `defaultValue` provider function
 * instead of throwing an exception.
 *
 * @throws NoSuchElementException when the map doesn't contain a value for the specified key and
 * no implicit default value was provided for that map.
 */
@SinceKotlin("1.1")
public fun  Map.getValue(key: K): V = getOrImplicitDefault(key)

/**
 * Returns the value for the given key. If the key is not found in the map, calls the [defaultValue] function,
 * puts its result into the map under the given key and returns it.
 *
 * Note that the operation is not guaranteed to be atomic if the map is being modified concurrently.
 *
 * @sample samples.collections.Maps.Usage.getOrPut
 */
public inline fun  MutableMap.getOrPut(key: K, defaultValue: () -> V): V {
    val value = get(key)
    return if (value == null) {
        val answer = defaultValue()
        put(key, answer)
        answer
    } else {
        value
    }
}

/**
 * Returns an [Iterator] over the entries in the [Map].
 *
 * @sample samples.collections.Maps.Usage.forOverEntries
 */
@kotlin.internal.InlineOnly
public inline operator fun  Map.iterator(): Iterator> = entries.iterator()

/**
 * Returns a [MutableIterator] over the mutable entries in the [MutableMap].
 *
 */
@kotlin.jvm.JvmName("mutableIterator")
@kotlin.internal.InlineOnly
public inline operator fun  MutableMap.iterator(): MutableIterator> = entries.iterator()

/**
 * Populates the given [destination] map with entries having the keys of this map and the values obtained
 * by applying the [transform] function to each entry in this [Map].
 */
public inline fun > Map.mapValuesTo(destination: M, transform: (Map.Entry) -> R): M {
    return entries.associateByTo(destination, { it.key }, transform)
}

/**
 * Populates the given [destination] map with entries having the keys obtained
 * by applying the [transform] function to each entry in this [Map] and the values of this map.
 *
 * In case if any two entries are mapped to the equal keys, the value of the latter one will overwrite
 * the value associated with the former one.
 */
public inline fun > Map.mapKeysTo(destination: M, transform: (Map.Entry) -> R): M {
    return entries.associateByTo(destination, transform, { it.value })
}

/**
 * Puts all the given [pairs] into this [MutableMap] with the first component in the pair being the key and the second the value.
 */
public fun  MutableMap.putAll(pairs: Array>): Unit {
    for ((key, value) in pairs) {
        put(key, value)
    }
}

/**
 * Puts all the elements of the given collection into this [MutableMap] with the first component in the pair being the key and the second the value.
 */
public fun  MutableMap.putAll(pairs: Iterable>): Unit {
    for ((key, value) in pairs) {
        put(key, value)
    }
}

/**
 * Puts all the elements of the given sequence into this [MutableMap] with the first component in the pair being the key and the second the value.
 */
public fun  MutableMap.putAll(pairs: Sequence>): Unit {
    for ((key, value) in pairs) {
        put(key, value)
    }
}

/**
 * Returns a new map with entries having the keys of this map and the values obtained by applying the [transform]
 * function to each entry in this [Map].
 *
 * The returned map preserves the entry iteration order of the original map.
 *
 * @sample samples.collections.Maps.Transformations.mapValues
 */
public inline fun  Map.mapValues(transform: (Map.Entry) -> R): Map {
    return mapValuesTo(LinkedHashMap(mapCapacity(size)), transform) // .optimizeReadOnlyMap()
}

/**
 * Returns a new Map with entries having the keys obtained by applying the [transform] function to each entry in this
 * [Map] and the values of this map.
 *
 * In case if any two entries are mapped to the equal keys, the value of the latter one will overwrite
 * the value associated with the former one.
 *
 * The returned map preserves the entry iteration order of the original map.
 *
 * @sample samples.collections.Maps.Transformations.mapKeys
 */
public inline fun  Map.mapKeys(transform: (Map.Entry) -> R): Map {
    return mapKeysTo(LinkedHashMap(mapCapacity(size)), transform) // .optimizeReadOnlyMap()
}

/**
 * Returns a map containing all key-value pairs with keys matching the given [predicate].
 *
 * The returned map preserves the entry iteration order of the original map.
 * @sample samples.collections.Maps.Filtering.filterKeys
 */
public inline fun  Map.filterKeys(predicate: (K) -> Boolean): Map {
    val result = LinkedHashMap()
    for (entry in this) {
        if (predicate(entry.key)) {
            result.put(entry.key, entry.value)
        }
    }
    return result
}

/**
 * Returns a map containing all key-value pairs with values matching the given [predicate].
 *
 * The returned map preserves the entry iteration order of the original map.
 *  @sample samples.collections.Maps.Filtering.filterValues
 */
public inline fun  Map.filterValues(predicate: (V) -> Boolean): Map {
    val result = LinkedHashMap()
    for (entry in this) {
        if (predicate(entry.value)) {
            result.put(entry.key, entry.value)
        }
    }
    return result
}


/**
 * Appends all entries matching the given [predicate] into the mutable map given as [destination] parameter.
 *
 * @return the destination map.
 * @sample samples.collections.Maps.Filtering.filterTo
 */
public inline fun > Map.filterTo(destination: M, predicate: (Map.Entry) -> Boolean): M {
    for (element in this) {
        if (predicate(element)) {
            destination.put(element.key, element.value)
        }
    }
    return destination
}

/**
 * Returns a new map containing all key-value pairs matching the given [predicate].
 *
 * The returned map preserves the entry iteration order of the original map.
 * @sample samples.collections.Maps.Filtering.filter
 */
public inline fun  Map.filter(predicate: (Map.Entry) -> Boolean): Map {
    return filterTo(LinkedHashMap(), predicate)
}

/**
 * Appends all entries not matching the given [predicate] into the given [destination].
 *
 * @return the destination map.
 * @sample samples.collections.Maps.Filtering.filterNotTo
 */
public inline fun > Map.filterNotTo(destination: M, predicate: (Map.Entry) -> Boolean): M {
    for (element in this) {
        if (!predicate(element)) {
            destination.put(element.key, element.value)
        }
    }
    return destination
}

/**
 * Returns a new map containing all key-value pairs not matching the given [predicate].
 *
 * The returned map preserves the entry iteration order of the original map.
 * @sample samples.collections.Maps.Filtering.filterNot
 */
public inline fun  Map.filterNot(predicate: (Map.Entry) -> Boolean): Map {
    return filterNotTo(LinkedHashMap(), predicate)
}

/**
 * Returns a new map containing all key-value pairs from the given collection of pairs.
 *
 * The returned map preserves the entry iteration order of the original collection.
 * If any of two pairs would have the same key the last one gets added to the map.
 */
public fun  Iterable>.toMap(): Map {
    if (this is Collection) {
        return when (size) {
            0 -> emptyMap()
            1 -> mapOf(if (this is List) this[0] else iterator().next())
            else -> toMap(LinkedHashMap(mapCapacity(size)))
        }
    }
    return toMap(LinkedHashMap()).optimizeReadOnlyMap()
}

/**
 * Populates and returns the [destination] mutable map with key-value pairs from the given collection of pairs.
 */
public fun > Iterable>.toMap(destination: M): M =
    destination.apply { putAll(this@toMap) }

/**
 * Returns a new map containing all key-value pairs from the given array of pairs.
 *
 * The returned map preserves the entry iteration order of the original array.
 * If any of two pairs would have the same key the last one gets added to the map.
 */
public fun  Array>.toMap(): Map = when (size) {
    0 -> emptyMap()
    1 -> mapOf(this[0])
    else -> toMap(LinkedHashMap(mapCapacity(size)))
}

/**
 *  Populates and returns the [destination] mutable map with key-value pairs from the given array of pairs.
 */
public fun > Array>.toMap(destination: M): M =
    destination.apply { putAll(this@toMap) }

/**
 * Returns a new map containing all key-value pairs from the given sequence of pairs.
 *
 * The returned map preserves the entry iteration order of the original sequence.
 * If any of two pairs would have the same key the last one gets added to the map.
 */
public fun  Sequence>.toMap(): Map = toMap(LinkedHashMap()).optimizeReadOnlyMap()

/**
 * Populates and returns the [destination] mutable map with key-value pairs from the given sequence of pairs.
 */
public fun > Sequence>.toMap(destination: M): M =
    destination.apply { putAll(this@toMap) }

/**
 * Returns a new read-only map containing all key-value pairs from the original map.
 *
 * The returned map preserves the entry iteration order of the original map.
 */
@SinceKotlin("1.1")
public fun  Map.toMap(): Map = when (size) {
    0 -> emptyMap()
    1 -> toSingletonMap()
    else -> toMutableMap()
}

/**
 * Returns a new mutable map containing all key-value pairs from the original map.
 *
 * The returned map preserves the entry iteration order of the original map.
 */
@SinceKotlin("1.1")
public fun  Map.toMutableMap(): MutableMap = LinkedHashMap(this)

/**
 * Populates and returns the [destination] mutable map with key-value pairs from the given map.
 */
@SinceKotlin("1.1")
public fun > Map.toMap(destination: M): M =
    destination.apply { putAll(this@toMap) }

/**
 * Creates a new read-only map by replacing or adding an entry to this map from a given key-value [pair].
 *
 * The returned map preserves the entry iteration order of the original map.
 * The [pair] is iterated in the end if it has a unique key.
 */
public operator fun  Map.plus(pair: Pair): Map =
    if (this.isEmpty()) mapOf(pair) else LinkedHashMap(this).apply { put(pair.first, pair.second) }

/**
 * Creates a new read-only map by replacing or adding entries to this map from a given collection of key-value [pairs].
 *
 * The returned map preserves the entry iteration order of the original map.
 * Those [pairs] with unique keys are iterated in the end in the order of [pairs] collection.
 */
public operator fun  Map.plus(pairs: Iterable>): Map =
    if (this.isEmpty()) pairs.toMap() else LinkedHashMap(this).apply { putAll(pairs) }

/**
 * Creates a new read-only map by replacing or adding entries to this map from a given array of key-value [pairs].
 *
 * The returned map preserves the entry iteration order of the original map.
 * Those [pairs] with unique keys are iterated in the end in the order of [pairs] array.
 */
public operator fun  Map.plus(pairs: Array>): Map =
    if (this.isEmpty()) pairs.toMap() else LinkedHashMap(this).apply { putAll(pairs) }

/**
 * Creates a new read-only map by replacing or adding entries to this map from a given sequence of key-value [pairs].
 *
 * The returned map preserves the entry iteration order of the original map.
 * Those [pairs] with unique keys are iterated in the end in the order of [pairs] sequence.
 */
public operator fun  Map.plus(pairs: Sequence>): Map =
    LinkedHashMap(this).apply { putAll(pairs) }.optimizeReadOnlyMap()

/**
 * Creates a new read-only map by replacing or adding entries to this map from another [map].
 *
 * The returned map preserves the entry iteration order of the original map.
 * Those entries of another [map] that are missing in this map are iterated in the end in the order of that [map].
 */
public operator fun  Map.plus(map: Map): Map =
    LinkedHashMap(this).apply { putAll(map) }


/**
 * Appends or replaces the given [pair] in this mutable map.
 */
@kotlin.internal.InlineOnly
public inline operator fun  MutableMap.plusAssign(pair: Pair) {
    put(pair.first, pair.second)
}

/**
 * Appends or replaces all pairs from the given collection of [pairs] in this mutable map.
 */
@kotlin.internal.InlineOnly
public inline operator fun  MutableMap.plusAssign(pairs: Iterable>) {
    putAll(pairs)
}

/**
 * Appends or replaces all pairs from the given array of [pairs] in this mutable map.
 */
@kotlin.internal.InlineOnly
public inline operator fun  MutableMap.plusAssign(pairs: Array>) {
    putAll(pairs)
}

/**
 * Appends or replaces all pairs from the given sequence of [pairs] in this mutable map.
 */
@kotlin.internal.InlineOnly
public inline operator fun  MutableMap.plusAssign(pairs: Sequence>) {
    putAll(pairs)
}

/**
 * Appends or replaces all entries from the given [map] in this mutable map.
 */
@kotlin.internal.InlineOnly
public inline operator fun  MutableMap.plusAssign(map: Map) {
    putAll(map)
}

/**
 * Returns a map containing all entries of the original map except the entry with the given [key].
 *
 * The returned map preserves the entry iteration order of the original map.
 */
@SinceKotlin("1.1")
public operator fun  Map.minus(key: K): Map =
    this.toMutableMap().apply { minusAssign(key) }.optimizeReadOnlyMap()

/**
 * Returns a map containing all entries of the original map except those entries
 * the keys of which are contained in the given [keys] collection.
 *
 * The returned map preserves the entry iteration order of the original map.
 */
@SinceKotlin("1.1")
public operator fun  Map.minus(keys: Iterable): Map =
    this.toMutableMap().apply { minusAssign(keys) }.optimizeReadOnlyMap()

/**
 * Returns a map containing all entries of the original map except those entries
 * the keys of which are contained in the given [keys] array.
 *
 * The returned map preserves the entry iteration order of the original map.
 */
@SinceKotlin("1.1")
public operator fun  Map.minus(keys: Array): Map =
    this.toMutableMap().apply { minusAssign(keys) }.optimizeReadOnlyMap()

/**
 * Returns a map containing all entries of the original map except those entries
 * the keys of which are contained in the given [keys] sequence.
 *
 * The returned map preserves the entry iteration order of the original map.
 */
@SinceKotlin("1.1")
public operator fun  Map.minus(keys: Sequence): Map =
    this.toMutableMap().apply { minusAssign(keys) }.optimizeReadOnlyMap()

/**
 * Removes the entry with the given [key] from this mutable map.
 */
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline operator fun  MutableMap.minusAssign(key: K) {
    remove(key)
}

/**
 * Removes all entries the keys of which are contained in the given [keys] collection from this mutable map.
 */
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline operator fun  MutableMap.minusAssign(keys: Iterable) {
    this.keys.removeAll(keys)
}

/**
 * Removes all entries the keys of which are contained in the given [keys] array from this mutable map.
 */
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline operator fun  MutableMap.minusAssign(keys: Array) {
    this.keys.removeAll(keys)
}

/**
 * Removes all entries from the keys of which are contained in the given [keys] sequence from this mutable map.
 */
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline operator fun  MutableMap.minusAssign(keys: Sequence) {
    this.keys.removeAll(keys)
}


// do not expose for now @PublishedApi
internal fun  Map.optimizeReadOnlyMap() = when (size) {
    0 -> emptyMap()
    1 -> toSingletonMapOrSelf()
    else -> this
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy