diff --git a/libraries/stdlib/samples/test/samples/collections/arrays.kt b/libraries/stdlib/samples/test/samples/collections/arrays.kt index 449156d92a4..2a6a5152d91 100644 --- a/libraries/stdlib/samples/test/samples/collections/arrays.kt +++ b/libraries/stdlib/samples/test/samples/collections/arrays.kt @@ -45,6 +45,21 @@ class Arrays { val array: Array? = arrayOf('a', 'b', 'c') assertFalse(array.isNullOrEmpty()) } + + @Sample + fun arrayIfEmpty() { + val emptyArray: Array = emptyArray() + + val emptyOrNull: Array? = emptyArray.ifEmpty { null } + assertPrints(emptyOrNull, "null") + + val emptyOrDefault: Array = emptyArray.ifEmpty { arrayOf("default") } + assertPrints(emptyOrDefault.contentToString(), "[default]") + + val nonEmptyArray = arrayOf(1) + val sameArray = nonEmptyArray.ifEmpty { arrayOf(2) } + assertTrue(nonEmptyArray === sameArray) + } } class Transformations { diff --git a/libraries/stdlib/samples/test/samples/collections/collections.kt b/libraries/stdlib/samples/test/samples/collections/collections.kt index 81c4b2a1f61..e644fecb1f0 100644 --- a/libraries/stdlib/samples/test/samples/collections/collections.kt +++ b/libraries/stdlib/samples/test/samples/collections/collections.kt @@ -63,6 +63,21 @@ class Collections { assertFalse(collection.isNullOrEmpty()) } + @Sample + fun collectionIfEmpty() { + val empty: List = emptyList() + + val emptyOrNull: List? = empty.ifEmpty { null } + assertPrints(emptyOrNull, "null") + + val emptyOrDefault: List = empty.ifEmpty { listOf("default") } + assertPrints(emptyOrDefault, "[default]") + + val nonEmpty = listOf("x") + val sameList: List = nonEmpty.ifEmpty { listOf("empty") } + assertTrue(nonEmpty === sameList) + } + @Sample fun collectionContainsAll() { val collection = mutableListOf('a', 'b') diff --git a/libraries/stdlib/samples/test/samples/collections/maps.kt b/libraries/stdlib/samples/test/samples/collections/maps.kt index 87bcf00e265..d7acda3eb8b 100644 --- a/libraries/stdlib/samples/test/samples/collections/maps.kt +++ b/libraries/stdlib/samples/test/samples/collections/maps.kt @@ -118,6 +118,21 @@ class Maps { val map: Map? = mapOf('a' to 1, 'b' to 2, 'c' to 3) assertFalse(map.isNullOrEmpty()) } + + @Sample + fun mapIfEmpty() { + val emptyMap: Map = emptyMap() + + val emptyOrNull = emptyMap.ifEmpty { null } + assertPrints(emptyOrNull, "null") + + val emptyOrDefault: Map = emptyMap.ifEmpty { mapOf("s" to "a") } + assertPrints(emptyOrDefault, "{s=a}") + + val nonEmptyMap = mapOf("x" to 1) + val sameMap = nonEmptyMap.ifEmpty { null } + assertTrue(nonEmptyMap === sameMap) + } } class Filtering { diff --git a/libraries/stdlib/samples/test/samples/collections/sequences.kt b/libraries/stdlib/samples/test/samples/collections/sequences.kt index 395be08575f..ca6814bed85 100644 --- a/libraries/stdlib/samples/test/samples/collections/sequences.kt +++ b/libraries/stdlib/samples/test/samples/collections/sequences.kt @@ -169,6 +169,19 @@ class Sequences { val sequence: Sequence? = sequenceOf(1, 2, 3) assertPrints(sequence.orEmpty().toList(), "[1, 2, 3]") } + + @Sample + fun sequenceIfEmpty() { + val empty = emptySequence() + + val emptyOrDefault = empty.ifEmpty { sequenceOf("default") } + assertPrints(emptyOrDefault.toList(), "[default]") + + val nonEmpty = sequenceOf("value") + + val nonEmptyOrDefault = nonEmpty.ifEmpty { sequenceOf("default") } + assertPrints(nonEmptyOrDefault.toList(), "[value]") + } } class Transformations { diff --git a/libraries/stdlib/samples/test/samples/text/strings.kt b/libraries/stdlib/samples/test/samples/text/strings.kt index c081705153c..5d07328916d 100644 --- a/libraries/stdlib/samples/test/samples/text/strings.kt +++ b/libraries/stdlib/samples/test/samples/text/strings.kt @@ -1,6 +1,7 @@ package samples.text import samples.* +import kotlin.test.assertTrue class Strings { @@ -146,4 +147,33 @@ class Strings { assertPrints(builder, "") } + @Sample + fun stringIfEmpty() { + val empty = "" + + val emptyOrNull: String? = empty.ifEmpty { null } + assertPrints(emptyOrNull, "null") + + val emptyOrDefault = empty.ifEmpty { "default" } + assertPrints(emptyOrDefault, "default") + + val nonEmpty = "abc" + val sameString = nonEmpty.ifEmpty { "def" } + assertTrue(nonEmpty === sameString) + } + + @Sample + fun stringIfBlank() { + val blank = " " + + val blankOrNull: String? = blank.ifBlank { null } + assertPrints(blankOrNull, "null") + + val blankOrDefault = blank.ifBlank { "default" } + assertPrints(blankOrDefault, "default") + + val nonBlank = "abc" + val sameString = nonBlank.ifBlank { "def" } + assertTrue(nonBlank === sameString) + } } diff --git a/libraries/stdlib/src/kotlin/collections/Arrays.kt b/libraries/stdlib/src/kotlin/collections/Arrays.kt index 1234e6a0941..caaf80e0b4d 100644 --- a/libraries/stdlib/src/kotlin/collections/Arrays.kt +++ b/libraries/stdlib/src/kotlin/collections/Arrays.kt @@ -53,3 +53,15 @@ public inline fun Array<*>?.isNullOrEmpty(): Boolean { return this == null || this.isEmpty() } + +/** + * Returns this array if it's not empty + * or the result of calling [defaultValue] function if the array is empty. + * + * @sample samples.collections.Arrays.Usage.arrayIfEmpty + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +@Suppress("UPPER_BOUND_CANNOT_BE_ARRAY") +public inline fun C.ifEmpty(defaultValue: () -> R): R where C : Array<*>, C : R = + if (isEmpty()) defaultValue() else this diff --git a/libraries/stdlib/src/kotlin/collections/Collections.kt b/libraries/stdlib/src/kotlin/collections/Collections.kt index 1575d30bd1f..6bf7b307f1e 100644 --- a/libraries/stdlib/src/kotlin/collections/Collections.kt +++ b/libraries/stdlib/src/kotlin/collections/Collections.kt @@ -197,6 +197,18 @@ public inline fun Collection?.orEmpty(): Collection = this ?: emptyLis @kotlin.internal.InlineOnly public inline fun List?.orEmpty(): List = this ?: emptyList() +/** + * Returns this collection if it's not empty + * or the result of calling [defaultValue] function if the collection is empty. + * + * @sample samples.collections.Collections.Collections.collectionIfEmpty + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun C.ifEmpty(defaultValue: () -> R): R where C : Collection<*>, C : R = + if (isEmpty()) defaultValue() else this + + /** * Checks if all elements in the specified collection are contained in this collection. * diff --git a/libraries/stdlib/src/kotlin/collections/Maps.kt b/libraries/stdlib/src/kotlin/collections/Maps.kt index 83e27d6c7e0..89509c196be 100644 --- a/libraries/stdlib/src/kotlin/collections/Maps.kt +++ b/libraries/stdlib/src/kotlin/collections/Maps.kt @@ -162,6 +162,17 @@ public inline fun Map?.isNullOrEmpty(): Boolean { @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. * diff --git a/libraries/stdlib/src/kotlin/collections/Sequences.kt b/libraries/stdlib/src/kotlin/collections/Sequences.kt index 7430d3ff881..2c531bf8c89 100644 --- a/libraries/stdlib/src/kotlin/collections/Sequences.kt +++ b/libraries/stdlib/src/kotlin/collections/Sequences.kt @@ -9,6 +9,7 @@ package kotlin.sequences import kotlin.* +import kotlin.coroutines.experimental.buildSequence /** * Given an [iterator] function constructs a [Sequence] that returns values through the [Iterator] @@ -55,6 +56,23 @@ private object EmptySequence : Sequence, DropTakeSequence { @kotlin.internal.InlineOnly public inline fun Sequence?.orEmpty(): Sequence = this ?: emptySequence() + +/** + * Returns a sequence that iterates through the elements either of this sequence + * or, if this sequence turns out to be empty, of the sequence returned by [defaultValue] function. + * + * @sample samples.collections.Sequences.Usage.sequenceIfEmpty + */ +@SinceKotlin("1.3") +public fun Sequence.ifEmpty(defaultValue: () -> Sequence): Sequence = buildSequence { + val iterator = this@ifEmpty.iterator() + if (iterator.hasNext()) { + yieldAll(iterator) + } else { + yieldAll(defaultValue()) + } +} + /** * Returns a sequence of all elements from all sequences in this sequence. * diff --git a/libraries/stdlib/src/kotlin/text/Strings.kt b/libraries/stdlib/src/kotlin/text/Strings.kt index 3ca18e47569..bb9f270693e 100644 --- a/libraries/stdlib/src/kotlin/text/Strings.kt +++ b/libraries/stdlib/src/kotlin/text/Strings.kt @@ -276,6 +276,28 @@ public operator fun CharSequence.iterator(): CharIterator = object : CharIterato @kotlin.internal.InlineOnly public inline fun String?.orEmpty(): String = this ?: "" +/** + * Returns this char sequence if it's not empty + * or the result of calling [defaultValue] function if the char sequence is empty. + * + * @sample samples.text.Strings.stringIfEmpty + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun C.ifEmpty(defaultValue: () -> R): R where C : CharSequence, C : R = + if (isEmpty()) defaultValue() else this + +/** + * Returns this char sequence if it is not empty and doesn't consist solely of whitespace characters, + * or the result of calling [defaultValue] function otherwise. + * + * @sample samples.text.Strings.stringIfBlank + */ +@SinceKotlin("1.3") +@kotlin.internal.InlineOnly +public inline fun C.ifBlank(defaultValue: () -> R): R where C : CharSequence, C : R = + if (isBlank()) defaultValue() else this + /** * Returns the range of valid character indices for this char sequence. */ diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index bb9254faa25..a010834fa6a 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -4486,6 +4486,7 @@ public final class kotlin/sequences/SequencesKt { public static final fun groupByTo (Lkotlin/sequences/Sequence;Ljava/util/Map;Lkotlin/jvm/functions/Function1;)Ljava/util/Map; public static final fun groupByTo (Lkotlin/sequences/Sequence;Ljava/util/Map;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Ljava/util/Map; public static final fun groupingBy (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)Lkotlin/collections/Grouping; + public static final fun ifEmpty (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function0;)Lkotlin/sequences/Sequence; public static final fun indexOf (Lkotlin/sequences/Sequence;Ljava/lang/Object;)I public static final fun indexOfFirst (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)I public static final fun indexOfLast (Lkotlin/sequences/Sequence;Lkotlin/jvm/functions/Function1;)I