From 3459a24b0a3083f3378e31e6047bc4411ae7c6f9 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Tue, 22 Dec 2015 17:58:21 +0300 Subject: [PATCH] Regex: Introduce MatchResult.groupValues to get list of numbered group values exlcuding zeroth group with the entire match. --- js/js.libraries/src/core/regex.kt | 18 +++++++++++++-- .../stdlib/src/kotlin/text/regex/Regex.kt | 22 +++++++++++++++---- .../stdlib/src/kotlin/text/regex/RegexJVM.kt | 13 +++++++++++ libraries/stdlib/test/text/RegexTest.kt | 8 +++++++ 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/js/js.libraries/src/core/regex.kt b/js/js.libraries/src/core/regex.kt index eb7481a4f9d..f1e83c1017f 100644 --- a/js/js.libraries/src/core/regex.kt +++ b/js/js.libraries/src/core/regex.kt @@ -198,8 +198,8 @@ private fun RegExp.findNext(input: String, from: Int): MatchResult? { get() = match[0]!! override val groups: MatchGroupCollection = object : MatchGroupCollection { - override val size: Int get() = match.size() - override fun isEmpty(): Boolean = size() == 0 + override val size: Int get() = match.size + override fun isEmpty(): Boolean = size == 0 override fun contains(o: MatchGroup?): Boolean = this.any { it == o } override fun containsAll(c: Collection): Boolean = c.all({contains(it)}) @@ -209,6 +209,20 @@ private fun RegExp.findNext(input: String, from: Int): MatchResult? { override fun get(index: Int): MatchGroup? = match[index]?.let { MatchGroup(it) } } + + private var groupValues_: List? = null + + override val groupValues: List + get() { + if (groupValues_ == null) { + groupValues_ = object : java.util.AbstractList() { + override val size: Int get() = match.size - 1 + override fun get(index: Int): String = match[index + 1] ?: "" + } + } + return groupValues_!! + } + override fun next(): MatchResult? = this@findNext.findNext(input, if (range.isEmpty()) range.start + 1 else range.end + 1) } } \ No newline at end of file diff --git a/libraries/stdlib/src/kotlin/text/regex/Regex.kt b/libraries/stdlib/src/kotlin/text/regex/Regex.kt index e2d024174b0..1773809b280 100644 --- a/libraries/stdlib/src/kotlin/text/regex/Regex.kt +++ b/libraries/stdlib/src/kotlin/text/regex/Regex.kt @@ -19,8 +19,8 @@ package kotlin.text /** * Represents a collection of captured groups in a single match of a regular expression. * - * Groups are indexed from 1 to the count of groups in the regular expression. - * The first group (with the index 0) corresponds to the entire match. + * This collection has size of `groupCount + 1` where `groupCount` is the count of groups in the regular expression. + * Groups are indexed from 1 to `groupCount` and group with the index 0 corresponds to the entire match. * * An element of the collection at the particular index can be `null`, * if the corresponding group in the regular expression is optional and @@ -48,9 +48,23 @@ public interface MatchResult { public val range: IntRange /** The substring from the input string captured by this match. */ public val value: String - /** A collection of groups matched by the regular expression. */ + /** + * A collection of groups matched by the regular expression. + * + * This collection has size of `groupCount + 1` where `groupCount` is the count of groups in the regular expression. + * Groups are indexed from 1 to `groupCount` and group with the index 0 corresponds to the entire match. + */ public val groups: MatchGroupCollection - // TODO: Should we have groupCount (equals groups.size()-1)? + /** + * A list of matched indexed group values except zeroth group corresponding to the entire match. + * + * Unlike [groups] collection this list contains exactly `groupCount` items where `groupCount` is the count of groups in the regular expression. + * Groups are are indexed from *zero* to the `groupCount - 1`. + * + * If the group in the regular expression is optional and there was not match captured by that group, + * corresponding item in [groupValues] would be an empty string. + */ + public val groupValues: List /** Returns a new [MatchResult] with the results for the next match, starting at the position * at which the last match ended (at the character after the last matched character). diff --git a/libraries/stdlib/src/kotlin/text/regex/RegexJVM.kt b/libraries/stdlib/src/kotlin/text/regex/RegexJVM.kt index 2fcfbfcd3fd..812138a1ba6 100644 --- a/libraries/stdlib/src/kotlin/text/regex/RegexJVM.kt +++ b/libraries/stdlib/src/kotlin/text/regex/RegexJVM.kt @@ -257,6 +257,19 @@ private class MatcherMatchResult(private val matcher: Matcher, private val input } } + private var groupValues_: List? = null + + override val groupValues: List + get() { + if (groupValues_ == null) { + groupValues_ = object : AbstractList() { + override val size: Int get() = matchResult.groupCount() + override fun get(index: Int): String = matchResult.group(index + 1) ?: "" + } + } + return groupValues_!! + } + override fun next(): MatchResult? { val nextIndex = matchResult.end() + if (matchResult.end() == matchResult.start()) 1 else 0 return if (nextIndex <= input.length) matcher.findNext(nextIndex, input) else null diff --git a/libraries/stdlib/test/text/RegexTest.kt b/libraries/stdlib/test/text/RegexTest.kt index f314e87f7c2..f61ea4e56a2 100644 --- a/libraries/stdlib/test/text/RegexTest.kt +++ b/libraries/stdlib/test/text/RegexTest.kt @@ -74,9 +74,13 @@ class RegexTest { assertEquals("1", m1.groups[1]?.value) assertEquals("a", m1.groups[2]?.value) + assertEquals(listOf("1", "a"), m1.groupValues) + val m2 = matches[1] assertEquals("2", m2.groups[1]?.value) assertEquals("b", m2.groups[2]?.value) + + assertEquals(listOf("2", "b"), m2.groupValues) } @test fun matchOptionalGroup() { @@ -87,10 +91,14 @@ class RegexTest { assertEquals("Hi", m1.groups[1]?.value) assertEquals(null, m1.groups[2]) + assertEquals(listOf("Hi", ""), m1.groupValues) + val m2 = pattern.find("bye...")!! assertEquals(3, m2.groups.size) assertEquals(null, m2.groups[1]) assertEquals("bye", m2.groups[2]?.value) + + assertEquals(listOf("", "bye"), m2.groupValues) } @test fun matchMultiline() {