From b16f46d932adca75c21a6b0ca6b5b74d732d4552 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Wed, 15 Jun 2016 20:53:21 +0300 Subject: [PATCH] Add extensions from kotlinx.support libraries: use for AutoCloseables: #KT-5899, extensions for java.util.Stream. --- libraries/stdlib/jre7/src/kotlin/Standard.kt | 35 ++++++ .../test/TryWithResourcesAutoCloseableTest.kt | 86 ++++++++++++++ .../test/TryWithResourcesCloseableTest.kt | 84 +++++++++++++ .../src/kotlin/collections/Collections.kt | 24 ++++ .../stdlib/jre8/src/kotlin/streams/Streams.kt | 51 ++++++++ .../jre8/test/collections/CollectionTest.kt | 32 +++++ .../jre8/test/collections/IterableTest.kt | 39 ++++++ .../stdlib/jre8/test/collections/ListTest.kt | 20 ++++ .../stdlib/jre8/test/collections/MapTest.kt | 111 ++++++++++++++++++ .../jre8/test/collections/StreamsTest.kt | 46 ++++++++ 10 files changed, 528 insertions(+) create mode 100644 libraries/stdlib/jre7/src/kotlin/Standard.kt create mode 100644 libraries/stdlib/jre7/test/TryWithResourcesAutoCloseableTest.kt create mode 100644 libraries/stdlib/jre7/test/TryWithResourcesCloseableTest.kt create mode 100644 libraries/stdlib/jre8/src/kotlin/collections/Collections.kt create mode 100644 libraries/stdlib/jre8/src/kotlin/streams/Streams.kt create mode 100644 libraries/stdlib/jre8/test/collections/CollectionTest.kt create mode 100644 libraries/stdlib/jre8/test/collections/IterableTest.kt create mode 100644 libraries/stdlib/jre8/test/collections/ListTest.kt create mode 100644 libraries/stdlib/jre8/test/collections/MapTest.kt create mode 100644 libraries/stdlib/jre8/test/collections/StreamsTest.kt diff --git a/libraries/stdlib/jre7/src/kotlin/Standard.kt b/libraries/stdlib/jre7/src/kotlin/Standard.kt new file mode 100644 index 00000000000..4e0cb01d6f2 --- /dev/null +++ b/libraries/stdlib/jre7/src/kotlin/Standard.kt @@ -0,0 +1,35 @@ +@file:JvmName("StandardJRE7Kt") +package kotlin + +@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +@kotlin.internal.InlineOnly +public inline fun T.use(block: (T) -> R): R { + var closed = false + try { + return block(this) + } catch (e: Throwable) { + closed = true + @Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") + closeSuppressed(e) + throw e + } finally { + if (!closed) { + close() + } + } +} + +/** + * Closes this [AutoCloseable] suppressing possible exception or error thrown by [AutoCloseable.close] function. + * The suppressed exception is added to the list of suppressed exceptions of [cause] exception. + */ +@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +@kotlin.internal.InlineExposed +internal fun AutoCloseable.closeSuppressed(cause: Throwable) { + try { + close() + } catch (closeException: Throwable) { + cause.addSuppressed(closeException) + } +} + diff --git a/libraries/stdlib/jre7/test/TryWithResourcesAutoCloseableTest.kt b/libraries/stdlib/jre7/test/TryWithResourcesAutoCloseableTest.kt new file mode 100644 index 00000000000..fa2f1fafeb2 --- /dev/null +++ b/libraries/stdlib/jre7/test/TryWithResourcesAutoCloseableTest.kt @@ -0,0 +1,86 @@ +package kotlin.jdk7.test + +import java.io.* +import org.junit.Test +import kotlin.test.* + + +class TryWithResourcesAutoCloseableTest { + + class Resource(val faultyClose: Boolean = false) : AutoCloseable { + + var isClosed = false + private set + + override fun close() { + if (faultyClose) + throw IOException("Close failed") + isClosed = true + } + } + + + @Test fun success() { + val resource = Resource() + val result = resource.use { "ok" } + assertEquals("ok", result) + assertTrue(resource.isClosed) + } + + @Test fun closeFails() { + val e = assertFails { + Resource(faultyClose = true).use { "" } + } + assertTrue(e is IOException) + } + + @Test fun opFailsCloseSuccess() { + val e = assertFails { + Resource().use { error("op fail") } + } + assertTrue(e is IllegalStateException) + assertTrue(e.suppressed.isEmpty()) + } + + @Test fun opFailsCloseFails() { + val e = assertFails { + Resource(faultyClose = true).use { error("op fail") } + } + assertTrue(e is IllegalStateException) + assertTrue(e.suppressed.single() is IOException) + } + + @Test fun opFailsCloseFailsTwice() { + val e = assertFails { + Resource(faultyClose = true).use { res1 -> + Resource(faultyClose = true).use { res2 -> + error("op fail") + } + } + } + assertTrue(e is IllegalStateException) + val suppressed = e.suppressed + assertEquals(2, suppressed.size) + assertTrue(suppressed.all { it is IOException }) + } + + @Test fun nonLocalReturnInBlock() { + fun Resource.operation(nonLocal: Boolean): String { + return use { if (nonLocal) return "nonLocal" else "local" } + } + + Resource().let { resource -> + val result = resource.operation(nonLocal = false) + assertEquals("local", result) + assertTrue(resource.isClosed) + } + + Resource().let { resource -> + val result = resource.operation(nonLocal = true) + assertEquals("nonLocal", result) + assertTrue(resource.isClosed) + } + + } + +} \ No newline at end of file diff --git a/libraries/stdlib/jre7/test/TryWithResourcesCloseableTest.kt b/libraries/stdlib/jre7/test/TryWithResourcesCloseableTest.kt new file mode 100644 index 00000000000..7ba7736b658 --- /dev/null +++ b/libraries/stdlib/jre7/test/TryWithResourcesCloseableTest.kt @@ -0,0 +1,84 @@ +package kotlin.jdk7.test + +import java.io.* +import org.junit.Test +import kotlin.test.* + +class TryWithResourcesCloseableTest { + + class Resource(val faultyClose: Boolean = false) : Closeable { + + var isClosed = false + private set + + override fun close() { + if (faultyClose) + throw IOException("Close failed") + isClosed = true + } + } + + @Test fun success() { + val resource = Resource() + val result = resource.use { "ok" } + assertEquals("ok", result) + assertTrue(resource.isClosed) + } + + @Test fun closeFails() { + val e = assertFails { + Resource(faultyClose = true).use { "" } + } + assertTrue(e is IOException) + } + + @Test fun opFailsCloseSuccess() { + val e = assertFails { + Resource().use { error("op fail") } + } + assertTrue(e is IllegalStateException) + assertTrue(e.suppressed.isEmpty()) + } + + @Test fun opFailsCloseFails() { + val e = assertFails { + Resource(faultyClose = true).use { error("op fail") } + } + assertTrue(e is IllegalStateException) + assertTrue(e.suppressed.single() is IOException) + } + + @Test fun opFailsCloseFailsTwice() { + val e = assertFails { + Resource(faultyClose = true).use { res1 -> + Resource(faultyClose = true).use { res2 -> + error("op fail") + } + } + } + assertTrue(e is IllegalStateException) + val suppressed = e.suppressed + assertEquals(2, suppressed.size) + assertTrue(suppressed.all { it is IOException }) + } + + @Test fun nonLocalReturnInBlock() { + fun Resource.operation(nonLocal: Boolean): String { + return use { if (nonLocal) return "nonLocal" else "local" } + } + + Resource().let { resource -> + val result = resource.operation(nonLocal = false) + assertEquals("local", result) + assertTrue(resource.isClosed) + } + + Resource().let { resource -> + val result = resource.operation(nonLocal = true) + assertEquals("nonLocal", result) + assertTrue(resource.isClosed) + } + + } + +} \ No newline at end of file diff --git a/libraries/stdlib/jre8/src/kotlin/collections/Collections.kt b/libraries/stdlib/jre8/src/kotlin/collections/Collections.kt new file mode 100644 index 00000000000..7e601082f70 --- /dev/null +++ b/libraries/stdlib/jre8/src/kotlin/collections/Collections.kt @@ -0,0 +1,24 @@ +@file:Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE") +@file:JvmName("CollectionsJRE8Kt") +package kotlin.collections + +/** + * Returns the value to which the specified key is mapped, or + * [defaultValue] if this map contains no mapping for the key. + */ +@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +@kotlin.internal.InlineOnly +public inline fun <@kotlin.internal.OnlyInputTypes K, V> Map.getOrDefault(key: K, defaultValue: V): V + = getOrDefault(key, defaultValue) + + +/** + * Removes the entry for the specified key only if it is currently + * mapped to the specified value. + */ +@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +@kotlin.internal.InlineOnly +public inline fun <@kotlin.internal.OnlyInputTypes K, @kotlin.internal.OnlyInputTypes V> MutableMap.remove(key: K, value: V): Boolean + = (this as MutableMap).remove(key, value) + + diff --git a/libraries/stdlib/jre8/src/kotlin/streams/Streams.kt b/libraries/stdlib/jre8/src/kotlin/streams/Streams.kt new file mode 100644 index 00000000000..cf76165394e --- /dev/null +++ b/libraries/stdlib/jre8/src/kotlin/streams/Streams.kt @@ -0,0 +1,51 @@ +@file:JvmName("StreamsKt") +package kotlin.streams + +import java.util.* +import java.util.stream.* + +/** + * Creates a [Sequence] instance that wraps the original stream iterating through its elements. + */ +public fun Stream.asSequence(): Sequence = Sequence { iterator() } + +/** + * Creates a [Sequence] instance that wraps the original stream iterating through its elements. + */ +public fun IntStream.asSequence(): Sequence = Sequence { iterator() } + +/** + * Creates a [Sequence] instance that wraps the original stream iterating through its elements. + */ +public fun LongStream.asSequence(): Sequence = Sequence { iterator() } + +/** + * Creates a [Sequence] instance that wraps the original stream iterating through its elements. + */ +public fun DoubleStream.asSequence(): Sequence = Sequence { iterator() } + +/** + * Creates a sequential [Stream] instance that produces elements from the original sequence. + */ +public fun Sequence.asStream(): Stream = StreamSupport.stream({ Spliterators.spliteratorUnknownSize(iterator(), 0) }, 0, false) + +/** + * Returns a [List] containing all elements produced by this stream. + */ +public fun Stream.toList(): List = collect(Collectors.toList()) + +/** + * Returns a [List] containing all elements produced by this stream. + */ +public fun IntStream.toList(): List = toArray().asList() + +/** + * Returns a [List] containing all elements produced by this stream. + */ +public fun LongStream.toList(): List = toArray().asList() + +/** + * Returns a [List] containing all elements produced by this stream. + */ +public fun DoubleStream.toList(): List = toArray().asList() + diff --git a/libraries/stdlib/jre8/test/collections/CollectionTest.kt b/libraries/stdlib/jre8/test/collections/CollectionTest.kt new file mode 100644 index 00000000000..cce80c66df6 --- /dev/null +++ b/libraries/stdlib/jre8/test/collections/CollectionTest.kt @@ -0,0 +1,32 @@ +package kotlin.jdk8.collections.test + +import org.junit.Test +import kotlin.test.* +import java.util.function.Predicate +import java.util.stream.Collectors +import kotlin.streams.* + +class CollectionTest { + + val data = listOf("abc", "fo", "baar") + + @Test fun stream() { + assertEquals( + data.flatMap { it.asIterable() }, + data.stream() + .flatMap { it.chars().boxed().map { it.toChar() } } + .collect(Collectors.toList())) + + assertEquals(data, data.parallelStream().toList()) + } + + + @Test fun removeIf() { + val coll: MutableCollection = data.toMutableList() + assertTrue(coll.removeIf { it.length < 3 }) + assertEquals(listOf("abc", "baar"), coll as Collection) + assertFalse(coll.removeIf(Predicate { it.length > 4 })) + } + + +} \ No newline at end of file diff --git a/libraries/stdlib/jre8/test/collections/IterableTest.kt b/libraries/stdlib/jre8/test/collections/IterableTest.kt new file mode 100644 index 00000000000..a06bc8fd7bc --- /dev/null +++ b/libraries/stdlib/jre8/test/collections/IterableTest.kt @@ -0,0 +1,39 @@ +package kotlin.jdk8.collections.test + +import org.junit.Test +import kotlin.test.* +import java.util.function.* + + + +class IterableTest { + + val data = listOf("foo", "bar") + val iterable = Iterable { data.iterator() } + + @Test fun spliterator() { + val spliterator = iterable.spliterator() + + assertEquals(-1, spliterator.exactSizeIfKnown) + val expected = data.toMutableList() + spliterator.forEachRemaining { + assertEquals(expected.removeAt(0), it) + } + } + + @Test fun forEach() { + val expected = data.toMutableList() + iterable.forEach(Consumer { + assertEquals(expected.removeAt(0), it) + }) + } + + @Test fun forEachRemaining() { + val expected = data.toMutableList() + val iterator = iterable.iterator() + iterator.forEachRemaining(Consumer { + assertEquals(expected.removeAt(0), it) + }) + } + +} \ No newline at end of file diff --git a/libraries/stdlib/jre8/test/collections/ListTest.kt b/libraries/stdlib/jre8/test/collections/ListTest.kt new file mode 100644 index 00000000000..e091c1ab5d3 --- /dev/null +++ b/libraries/stdlib/jre8/test/collections/ListTest.kt @@ -0,0 +1,20 @@ +package kotlin.jdk8.collections.test + +import org.junit.Test +import kotlin.test.* +import java.util.function.UnaryOperator + + +class ListTest { + + @Test fun replaceAll() { + val list = mutableListOf("ab", "cde", "x") + list.replaceAll { it.length.toString() } + + val expected = listOf("2", "3", "1") + assertEquals(expected, list) + + list.replaceAll(UnaryOperator.identity()) + assertEquals(expected, list) + } +} \ No newline at end of file diff --git a/libraries/stdlib/jre8/test/collections/MapTest.kt b/libraries/stdlib/jre8/test/collections/MapTest.kt new file mode 100644 index 00000000000..47351319b94 --- /dev/null +++ b/libraries/stdlib/jre8/test/collections/MapTest.kt @@ -0,0 +1,111 @@ +package kotlin.jdk8.collections.test + +import org.junit.Test +import java.util.function.BiFunction +import kotlin.test.* +import kotlin.jdk8.collections.* + +class MapTest { + + @Test fun getOrDefault() { + val map = mapOf("x" to 1, "z" to null) + assertEquals(1, map.getOrDefault("x", 0)) + assertEquals(0, map.getOrDefault("y", 0)) + assertEquals(null, map.getOrDefault("z", 0)) + assertEquals(null, map.getOrDefault("y" as CharSequence, null)) + } + + @Test fun forEach() { + val map = mapOf("k" to "v") + map.forEach { k, v -> + assertEquals("k", k) + assertEquals("v", v) + } + map.forEach { + assertEquals("k", it.key) + assertEquals("v", it.value) + } + } + + @Test fun replaceAll() { + val map: MutableMap = mutableMapOf("a" to "b", "c" to "d") + + map.replaceAll { k, v -> k + v } + assertEquals(mapOf("a" to "ab", "c" to "cd"), map) + + val operator = BiFunction { k, v -> k.toString() + v.toString() } + map.replaceAll(operator) + assertEquals(mapOf("a" to "aab", "c" to "ccd"), map) + } + + @Test fun putIfAbsent() { + val map = mutableMapOf(1 to "a") + + assertEquals("a", map.putIfAbsent(1, "b")) + assertEquals("a", map[1]) + + assertEquals(null, map.putIfAbsent(2, "b")) + assertEquals("b", map[2]) + } + + + @Test fun removeKeyValue() { + val map = mutableMapOf(1 to "a") + + assertEquals(false, map.remove(1 as Number, null as Any?)) // requires import + assertEquals(true, map.remove(1, "a")) + } + + @Test fun replace() { + val map = mutableMapOf(1 to "a", 2 to null) + assertTrue(map.replace(2, null, "x")) + assertEquals("x", map[2]) + + assertFalse(map.replace(2, null, "x")) + + assertEquals("a", map.replace(1, "b")) + assertEquals(null, map.replace(3, "c")) + + } + + @Test fun computeIfAbsent() { + val map = mutableMapOf(2 to "x", 3 to null) + assertEquals("x", map.computeIfAbsent(2) { it.toString() }) + assertEquals("3", map.computeIfAbsent(3) { it.toString() }) + assertEquals(null, map.computeIfAbsent(0) { null }) + } + + @Test fun computeIfPresent() { + val map = mutableMapOf(2 to "x") + assertEquals("2x", map.computeIfPresent(2) { k, v -> k.toString() + v }) + assertEquals(null, map.computeIfPresent(3) { k, v -> k.toString() + v }) + // fails due to KT-12144 + // assertEquals(null, map.computeIfPresent(2) { k, v -> null }) + // assertFalse(2 in map) + } + + @Test fun compute() { + val map = mutableMapOf(2 to "x") + assertEquals("2x", map.compute(2) { k, v -> k.toString() + v }) + // fails due to KT-12144 + // assertEquals(null, map.compute(2) { k, v -> null }) + // assertFalse { 2 in map } + assertEquals("1null", map.compute(1) { k, v -> k.toString() + v }) + } + + @Test fun merge() { + val map = mutableMapOf(2 to "x") + // fails due to KT-12144 +// assertEquals("y", map.merge(3, "y") { old, new -> null }) +// assertEquals(null, map.merge(3, "z") { old, new -> +// assertEquals("y", old) +// assertEquals("z", new) +// null +// }) + // assertFalse(3 in map) + + val map2 = mutableMapOf(1 to null) + // new value must be V!! + assertFails { map2.merge(1, null) { k, v -> 2 } } + } +} \ No newline at end of file diff --git a/libraries/stdlib/jre8/test/collections/StreamsTest.kt b/libraries/stdlib/jre8/test/collections/StreamsTest.kt new file mode 100644 index 00000000000..dcc06bee9e9 --- /dev/null +++ b/libraries/stdlib/jre8/test/collections/StreamsTest.kt @@ -0,0 +1,46 @@ +package kotlin.jdk8.streams.test + +import kotlin.streams.* +import org.junit.Test +import java.util.stream.* +import kotlin.test.* + +class StreamsTest { + + @Test fun toList() { + val data = arrayOf(1, 2L, 1.23, null) + val streamBuilder = { Stream.of(*data) } + + assertEquals(data.asList(), streamBuilder().toList()) + assertEquals(listOf(1), streamBuilder().filter { it is Int }.mapToInt { it as Int }.toList()) + assertEquals(listOf(2L), streamBuilder().filter { it is Long }.mapToLong { it as Long }.toList()) + assertEquals(listOf(1.23), streamBuilder().filter { it is Double }.mapToDouble { it as Double }.toList()) + } + + + @Test fun asSequence() { + val data = arrayOf(1, 2L, 1.23, null) + + fun assertSequenceContent(expected: List, actual: Sequence) { + assertEquals(expected, actual.toList()) + assertFailsWith ("Second iteration fails") { actual.toList() } + + } + + assertSequenceContent(data.asList(), Stream.of(*data).asSequence()) + assertSequenceContent(listOf(1, 2), IntStream.of(1, 2).asSequence()) + assertSequenceContent(listOf(1L, 2L), LongStream.of(1L, 2L).asSequence()) + assertSequenceContent(listOf(1.0, 2.0), DoubleStream.of(1.0, 2.0).asSequence()) + } + + @Test fun asStream() { + val sequence = generateSequence(0) { it -> it * it + 1 } + val stream = sequence.asStream() + val expected = Stream.iterate(0) { it -> it * it + 1 } + + assertEquals(expected.limit(7).toList(), stream.limit(7).toList()) + } + + + +}