diff --git a/js/js.libraries/src/core/collections/AbstractMap.kt b/js/js.libraries/src/core/collections/AbstractMap.kt index c57759be10f..53bf3b681ba 100644 --- a/js/js.libraries/src/core/collections/AbstractMap.kt +++ b/js/js.libraries/src/core/collections/AbstractMap.kt @@ -20,7 +20,7 @@ package kotlin.collections -public abstract class AbstractMap protected constructor() : MutableMap { +public abstract class AbstractMutableMap protected constructor() : AbstractMap(), MutableMap { /** * A mutable [Map.Entry] shared by several [Map] implementations. @@ -44,67 +44,14 @@ public abstract class AbstractMap protected constructor() : MutableMap(override val key: K, override val value: V) : Map.Entry { - constructor(entry: Map.Entry) : this(entry.key, entry.value) - - override fun hashCode(): Int = entryHashCode(this) - override fun toString(): String = entryToString(this) - override fun equals(other: Any?): Boolean = entryEquals(this, other) - } - - override fun clear() { entries.clear() } - override fun containsKey(key: K): Boolean { - return implFindEntry(key, false) != null - } - - override fun containsValue(value: V): Boolean = entries.any { it.value == value } - - internal fun containsEntry(entry: Map.Entry<*, *>?): Boolean { - // since entry comes from @UnsafeVariance parameters it can be virtually anything - if (entry !is Map.Entry<*, *>) return false - val key = entry.key - val value = entry.value - val ourValue = get(key) - - if (value != ourValue) { - return false - } - - // Perhaps it was null and we don't contain the key? - if (ourValue == null && !containsKey(key)) { - return false - } - - return true - } - - override fun equals(other: Any?): Boolean { - if (other === this) return true - if (other !is Map<*, *>) return false - if (size != other.size) return false - - return other.entries.all { containsEntry(it) } - } - - override operator fun get(key: K): V? = implFindEntry(key, false)?.value - - override fun hashCode(): Int = entries.hashCode() - - override fun isEmpty(): Boolean = size == 0 - override val size: Int get() = entries.size - - override val keys: MutableSet get() { return object : AbstractMutableSet() { override fun clear() { - this@AbstractMap.clear() + this@AbstractMutableMap.clear() } override operator fun contains(element: K): Boolean = containsKey(element) @@ -120,13 +67,13 @@ public abstract class AbstractMap protected constructor() : MutableMap protected constructor() : MutableMap): String = toString(entry.key) + "=" + toString(entry.value) - - private fun toString(o: Any?): String = if (o === this) "(this Map)" else o.toString() - override val values: MutableCollection get() { return object : AbstractMutableCollection() { - override fun clear() = this@AbstractMap.clear() + override fun clear() = this@AbstractMutableMap.clear() override operator fun contains(element: V): Boolean = containsValue(element) @@ -163,7 +102,7 @@ public abstract class AbstractMap protected constructor() : MutableMap protected constructor() : MutableMap? { + override fun remove(key: K): V? { val iter = entries.iterator() while (iter.hasNext()) { var entry = iter.next() val k = entry.key if (key == k) { - if (remove) { - entry = SimpleEntry(entry) - iter.remove() - } - return entry + val value = entry.value + iter.remove() + return value } } return null } - companion object { - - internal fun entryHashCode(e: Map.Entry<*, *>): Int = with(e) { (key?.hashCode() ?: 0) xor (value?.hashCode() ?: 0) } - internal fun entryToString(e: Map.Entry<*, *>): String = with(e) { "$key=$value" } - internal fun entryEquals(e: Map.Entry<*, *>, other: Any?): Boolean { - if (other !is Map.Entry<*, *>) return false - return e.key == other.key && e.value == other.value - } - } } diff --git a/js/js.libraries/src/core/collections/HashMap.kt b/js/js.libraries/src/core/collections/HashMap.kt index 68753fe7a7a..4f75a7baf26 100644 --- a/js/js.libraries/src/core/collections/HashMap.kt +++ b/js/js.libraries/src/core/collections/HashMap.kt @@ -23,7 +23,7 @@ package kotlin.collections import kotlin.collections.Map.Entry import kotlin.collections.MutableMap.MutableEntry -open class HashMap : AbstractMap { +public open class HashMap : AbstractMutableMap { private inner class EntrySet : AbstractMutableSet>() { diff --git a/js/js.libraries/src/core/collections/InternalHashCodeMap.kt b/js/js.libraries/src/core/collections/InternalHashCodeMap.kt index 8b88063c45a..c4a8e532fe7 100644 --- a/js/js.libraries/src/core/collections/InternalHashCodeMap.kt +++ b/js/js.libraries/src/core/collections/InternalHashCodeMap.kt @@ -21,7 +21,7 @@ package kotlin.collections import kotlin.collections.MutableMap.MutableEntry -import kotlin.collections.AbstractMap.SimpleEntry +import kotlin.collections.AbstractMutableMap.SimpleEntry /** * A simple wrapper around JavaScriptObject to provide [java.util.Map]-like semantics for any diff --git a/js/js.libraries/src/core/collections/LinkedHashMap.kt b/js/js.libraries/src/core/collections/LinkedHashMap.kt index 0a2f4c94233..fc3a06a8ac8 100644 --- a/js/js.libraries/src/core/collections/LinkedHashMap.kt +++ b/js/js.libraries/src/core/collections/LinkedHashMap.kt @@ -21,7 +21,7 @@ package kotlin.collections import kotlin.collections.MutableMap.MutableEntry -open class LinkedHashMap : HashMap, Map { +public open class LinkedHashMap : HashMap, Map { /** * The entry we use includes next/prev pointers for a doubly-linked circular @@ -35,7 +35,7 @@ open class LinkedHashMap : HashMap, Map { * small modifications. Paying a small storage cost only if you use * LinkedHashMap and minimizing code size seemed like a better tradeoff */ - private class ChainEntry(key: K, value: V) : AbstractMap.SimpleEntry(key, value) { + private class ChainEntry(key: K, value: V) : AbstractMutableMap.SimpleEntry(key, value) { internal var next: ChainEntry? = null internal var prev: ChainEntry? = null } diff --git a/libraries/stdlib/src/kotlin/collections/AbstractMap.kt b/libraries/stdlib/src/kotlin/collections/AbstractMap.kt new file mode 100644 index 00000000000..2a78f02afa3 --- /dev/null +++ b/libraries/stdlib/src/kotlin/collections/AbstractMap.kt @@ -0,0 +1,123 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Based on GWT AbstractMap + * Copyright 2007 Google Inc. + */ + +package kotlin.collections + +public abstract class AbstractMap protected constructor() : Map { + + override fun containsKey(key: K): Boolean { + return implFindEntry(key) != null + } + + override fun containsValue(value: @UnsafeVariance V): Boolean = entries.any { it.value == value } + + internal fun containsEntry(entry: Map.Entry<*, *>?): Boolean { + // since entry comes from @UnsafeVariance parameters it can be virtually anything + if (entry !is Map.Entry<*, *>) return false + val key = entry.key + val value = entry.value + val ourValue = get(key) + + if (value != ourValue) { + return false + } + + // Perhaps it was null and we don't contain the key? + if (ourValue == null && !containsKey(key)) { + return false + } + + return true + } + + override fun equals(other: Any?): Boolean { + if (other === this) return true + if (other !is Map<*, *>) return false + if (size != other.size) return false + + return other.entries.all { containsEntry(it) } + } + + override operator fun get(key: K): V? = implFindEntry(key)?.value + + override fun hashCode(): Int = entries.hashCode() + + override fun isEmpty(): Boolean = size == 0 + override val size: Int get() = entries.size + + + override val keys: Set get() { + return object : AbstractSet() { + override operator fun contains(element: K): Boolean = containsKey(element) + + override operator fun iterator(): Iterator { + val outerIter = entries.iterator() + return object : Iterator { + override fun hasNext(): Boolean = outerIter.hasNext() + override fun next(): K = outerIter.next().key + } + } + + override val size: Int get() = this@AbstractMap.size + } + } + + override fun toString(): String = entries.joinToString(", ", "{", "}") { toString(it) } + + private fun toString(entry: Map.Entry): String = toString(entry.key) + "=" + toString(entry.value) + + private fun toString(o: Any?): String = if (o === this) "(this Map)" else o.toString() + + override val values: Collection get() { + return object : AbstractCollection() { + override operator fun contains(element: @UnsafeVariance V): Boolean = containsValue(element) + + override operator fun iterator(): Iterator { + val outerIter = entries.iterator() + return object : Iterator { + override fun hasNext(): Boolean = outerIter.hasNext() + override fun next(): V = outerIter.next().value + } + } + + override val size: Int get() = this@AbstractMap.size + + // TODO: should we implement them this way? Currently it's unspecified in JVM + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Collection<*>) return false + return AbstractList.orderedEquals(this, other) + } + override fun hashCode(): Int = AbstractList.orderedHashCode(this) + } + } + + private fun implFindEntry(key: K): Map.Entry? = entries.firstOrNull { it.key == key } + + internal companion object { + + internal fun entryHashCode(e: Map.Entry<*, *>): Int = with(e) { (key?.hashCode() ?: 0) xor (value?.hashCode() ?: 0) } + internal fun entryToString(e: Map.Entry<*, *>): String = with(e) { "$key=$value" } + internal fun entryEquals(e: Map.Entry<*, *>, other: Any?): Boolean { + if (other !is Map.Entry<*, *>) return false + return e.key == other.key && e.value == other.value + } + } +} diff --git a/libraries/stdlib/src/kotlin/collections/TypeAliases.kt b/libraries/stdlib/src/kotlin/collections/TypeAliases.kt index 9bf6bbb1fd1..0e5f12efadd 100644 --- a/libraries/stdlib/src/kotlin/collections/TypeAliases.kt +++ b/libraries/stdlib/src/kotlin/collections/TypeAliases.kt @@ -3,4 +3,5 @@ package kotlin.collections public typealias AbstractMutableCollection = java.util.AbstractCollection public typealias AbstractMutableList = java.util.AbstractList -public typealias AbstractMutableSet = java.util.AbstractSet \ No newline at end of file +public typealias AbstractMutableSet = java.util.AbstractSet +public typealias AbstractMutableMap = java.util.AbstractMap \ No newline at end of file diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt index b3ddcaa30ed..1898076611e 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib.txt @@ -125,6 +125,30 @@ public abstract class kotlin/collections/AbstractList : kotlin/collections/Abstr public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object; } +public abstract class kotlin/collections/AbstractMap : java/util/Map, kotlin/jvm/internal/markers/KMappedMarker { + public static final field Companion Lkotlin/collections/AbstractMap$Companion; + protected fun ()V + public fun clear ()V + public fun containsKey (Ljava/lang/Object;)Z + public fun containsValue (Ljava/lang/Object;)Z + public final fun entrySet ()Ljava/util/Set; + public fun equals (Ljava/lang/Object;)Z + public fun get (Ljava/lang/Object;)Ljava/lang/Object; + public abstract fun getEntries ()Ljava/util/Set; + public fun getKeys ()Ljava/util/Set; + public fun getSize ()I + public fun getValues ()Ljava/util/Collection; + public fun hashCode ()I + public fun isEmpty ()Z + public final fun keySet ()Ljava/util/Set; + public fun put (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; + public fun putAll (Ljava/util/Map;)V + public fun remove (Ljava/lang/Object;)Ljava/lang/Object; + public final fun size ()I + public fun toString ()Ljava/lang/String; + public final fun values ()Ljava/util/Collection; +} + public abstract class kotlin/collections/AbstractSet : kotlin/collections/AbstractCollection, java/util/Set, kotlin/jvm/internal/markers/KMappedMarker { public static final field Companion Lkotlin/collections/AbstractSet$Companion; protected fun ()V