From d1d865aa0f65e286dc99340cb95718168fbe6b74 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Wed, 14 Oct 2015 07:44:19 +0300 Subject: [PATCH] Rename methods in Regex. Add matchEntire method to match entire string against regex. --- js/js.libraries/src/core/regex.kt | 22 ++++++++- .../stdlib/src/kotlin/text/regex/RegexJVM.kt | 24 ++++++++-- libraries/stdlib/test/text/RegexJVMTest.kt | 8 ++-- libraries/stdlib/test/text/RegexTest.kt | 46 +++++++++++++------ libraries/stdlib/test/text/StringTest.kt | 2 +- .../gradle/plugin/FinishBuildListener.kt | 2 +- .../kotlin/gradle/KotlinGradlePluginIT.kt | 2 +- 7 files changed, 80 insertions(+), 26 deletions(-) diff --git a/js/js.libraries/src/core/regex.kt b/js/js.libraries/src/core/regex.kt index 264ecb11d8b..6508cd16153 100644 --- a/js/js.libraries/src/core/regex.kt +++ b/js/js.libraries/src/core/regex.kt @@ -70,11 +70,29 @@ public class Regex(pattern: String, options: Set) { * @param startIndex An index to start search with, by default 0. Must be not less than zero and not greater than `input.length()` * @return An instance of [MatchResult] if match was found or `null` otherwise. */ - public fun match(input: CharSequence, startIndex: Int = 0): MatchResult? = nativePattern.findNext(input.toString(), startIndex) + public fun find(input: CharSequence, startIndex: Int = 0): MatchResult? = nativePattern.findNext(input.toString(), startIndex) + + @Deprecated("Use find() instead.", ReplaceWith("find(input, startIndex)")) + public fun match(input: CharSequence, startIndex: Int = 0): MatchResult? = find(input, startIndex) /** Returns a sequence of all occurrences of a regular expression within the [input] string, beginning at the specified [startIndex]. */ - public fun matchAll(input: CharSequence, startIndex: Int = 0): Sequence = sequence({ match(input, startIndex) }, { match -> match.next() }) + public fun findAll(input: CharSequence, startIndex: Int = 0): Sequence = sequence({ find(input, startIndex) }, { match -> match.next() }) + + @Deprecated("Use findAll() instead.", ReplaceWith("findAll(input, startIndex)")) + public fun matchAll(input: CharSequence, startIndex: Int = 0): Sequence = findAll(input, startIndex) + + /** + * Attempts to match the entire [input] CharSequence against the pattern. + * + * @return An instance of [MatchResult] if the entire input matches or `null` otherwise. + */ + public fun matchEntire(input: CharSequence): MatchResult? { + if (pattern.startsWith('^') && pattern.endsWith('$')) + return find(input) + else + return Regex("^${pattern.trimStart('^').trimEnd('$')}$", options).find(input) + } /** * Replaces all occurrences of this regular expression in the specified [input] string with specified [replacement] expression. diff --git a/libraries/stdlib/src/kotlin/text/regex/RegexJVM.kt b/libraries/stdlib/src/kotlin/text/regex/RegexJVM.kt index 4f6d31c6e85..2ef1deb471f 100644 --- a/libraries/stdlib/src/kotlin/text/regex/RegexJVM.kt +++ b/libraries/stdlib/src/kotlin/text/regex/RegexJVM.kt @@ -123,12 +123,25 @@ public class Regex internal constructor(private val nativePattern: Pattern) { * @param startIndex An index to start search with, by default 0. Must be not less than zero and not greater than `input.length()` * @return An instance of [MatchResult] if match was found or `null` otherwise. */ - public fun match(input: CharSequence, startIndex: Int = 0): MatchResult? = nativePattern.matcher(input).findNext(startIndex, input) + public fun find(input: CharSequence, startIndex: Int = 0): MatchResult? = nativePattern.matcher(input).findNext(startIndex, input) + + @Deprecated("Use find() instead.", ReplaceWith("find(input, startIndex)")) + public fun match(input: CharSequence, startIndex: Int = 0): MatchResult? = find(input, startIndex) /** * Returns a sequence of all occurrences of a regular expression within the [input] string, beginning at the specified [startIndex]. */ - public fun matchAll(input: CharSequence, startIndex: Int = 0): Sequence = sequence({ match(input, startIndex) }, { match -> match.next() }) + public fun findAll(input: CharSequence, startIndex: Int = 0): Sequence = sequence({ find(input, startIndex) }, { match -> match.next() }) + + @Deprecated("Use findAll() instead.", ReplaceWith("findAll(input, startIndex)")) + public fun matchAll(input: CharSequence, startIndex: Int = 0): Sequence = findAll(input, startIndex) + + /** + * Attempts to match the entire [input] CharSequence against the pattern. + * + * @return An instance of [MatchResult] if the entire input matches or `null` otherwise. + */ + public fun matchEntire(input: CharSequence): MatchResult? = nativePattern.matcher(input).matchEntire(input) /** * Replaces all occurrences of this regular expression in the specified [input] string with specified [replacement] expression. @@ -143,8 +156,7 @@ public class Regex internal constructor(private val nativePattern: Pattern) { * replacement for that match. */ public inline fun replace(input: CharSequence, transform: (MatchResult) -> String): String { - var match = match(input) - if (match == null) return input.toString() + var match: MatchResult? = find(input) ?: return input.toString() var lastStart = 0 val length = input.length() @@ -212,6 +224,10 @@ private fun Matcher.findNext(from: Int, input: CharSequence): MatchResult? { return if (!find(from)) null else MatcherMatchResult(this, input) } +private fun Matcher.matchEntire(input: CharSequence): MatchResult? { + return if (!matches()) null else MatcherMatchResult(this, input) +} + private class MatcherMatchResult(private val matcher: Matcher, private val input: CharSequence) : MatchResult { private val matchResult = matcher.toMatchResult() override val range: IntRange diff --git a/libraries/stdlib/test/text/RegexJVMTest.kt b/libraries/stdlib/test/text/RegexJVMTest.kt index 46dcf8c1670..9ddeefd309e 100644 --- a/libraries/stdlib/test/text/RegexJVMTest.kt +++ b/libraries/stdlib/test/text/RegexJVMTest.kt @@ -1,17 +1,17 @@ package test.text import kotlin.test.* -import org.junit.Test as test +import org.junit.Test class RegexJVMTest { - @test fun matchGroups() { + @Test fun matchGroups() { val input = "1a 2b 3c" val regex = "(\\d)(\\w)".toRegex() - val matches = regex.matchAll(input).toList() - assertTrue(matches.all { it.groups.size() == 3 }) + val matches = regex.findAll(input).toList() + assertTrue(matches.all { it.groups.size == 3 }) val m1 = matches[0] assertEquals("1a", m1.groups[0]?.value) assertEquals(0..1, m1.groups[0]?.range) diff --git a/libraries/stdlib/test/text/RegexTest.kt b/libraries/stdlib/test/text/RegexTest.kt index 52c85b00e54..21a6515ac7e 100644 --- a/libraries/stdlib/test/text/RegexTest.kt +++ b/libraries/stdlib/test/text/RegexTest.kt @@ -16,7 +16,7 @@ class RegexTest { assertTrue(p.hasMatch(input)) - val first = p.match(input) + val first = p.find(input) assertTrue(first != null); first!! assertEquals("123", first.value) @@ -26,7 +26,7 @@ class RegexTest { assertEquals("456", second1.value) assertEquals(second1.value, second2.value) - assertEquals("56", p.match(input, startIndex = 5)?.value) + assertEquals("56", p.find(input, startIndex = 5)?.value) val last = second1.next()!! assertEquals("789", last.value) @@ -44,12 +44,12 @@ class RegexTest { val input = "123 456 789" val pattern = "\\d+".toRegex() - val matches = pattern.matchAll(input) + val matches = pattern.findAll(input) val values = matches.map { it.value } val expected = listOf("123", "456", "789") assertEquals(expected, values.toList()) assertEquals(expected, values.toList(), "running match sequence second time") - assertEquals(expected.drop(1), pattern.matchAll(input, startIndex = 3).map { it.value }.toList()) + assertEquals(expected.drop(1), pattern.findAll(input, startIndex = 3).map { it.value }.toList()) assertEquals(listOf(0..2, 4..6, 8..10), matches.map { it.range }.toList()) } @@ -57,18 +57,18 @@ class RegexTest { @test fun matchAllSequence() { val input = "test" val pattern = ".*".toRegex() - val matches = pattern.matchAll(input).toList() + val matches = pattern.findAll(input).toList() assertEquals(input, matches[0].value) assertEquals(input, matches.joinToString("") { it.value }) - assertEquals(2, matches.size()) + assertEquals(2, matches.size) } @test fun matchGroups() { val input = "1a 2b 3c" val pattern = "(\\d)(\\w)".toRegex() - val matches = pattern.matchAll(input).toList() - assertTrue(matches.all { it.groups.size() == 3 }) + val matches = pattern.findAll(input).toList() + assertTrue(matches.all { it.groups.size == 3 }) val m1 = matches[0] assertEquals("1a", m1.groups[0]?.value) assertEquals("1", m1.groups[1]?.value) @@ -82,23 +82,43 @@ class RegexTest { @test fun matchOptionalGroup() { val pattern = "(hi)|(bye)".toRegex(RegexOption.IGNORE_CASE) - val m1 = pattern.match("Hi!")!! - assertEquals(3, m1.groups.size()) + val m1 = pattern.find("Hi!")!! + assertEquals(3, m1.groups.size) assertEquals("Hi", m1.groups[1]?.value) assertEquals(null, m1.groups[2]) - val m2 = pattern.match("bye...")!! - assertEquals(3, m2.groups.size()) + val m2 = pattern.find("bye...")!! + assertEquals(3, m2.groups.size) assertEquals(null, m2.groups[1]) assertEquals("bye", m2.groups[2]?.value) } @test fun matchMultiline() { val regex = "^[a-z]*$".toRegex(setOf(RegexOption.IGNORE_CASE, RegexOption.MULTILINE)) - val matchedValues = regex.matchAll("test\n\nLine").map { it.value }.toList() + val matchedValues = regex.findAll("test\n\nLine").map { it.value }.toList() assertEquals(listOf("test", "", "Line"), matchedValues) } + + @test fun matchEntire() { + val regex = "(\\d)(\\w)".toRegex() + + assertNull(regex.matchEntire("1a 2b")) + assertNotNull(regex.matchEntire("3c")) { m -> + assertEquals("3c", m.value) + assertEquals(3, m.groups.size) + assertEquals(listOf("3c", "3", "c"), m.groups.map { it!!.value }) + } + } + + @test fun matchEntireLazyQuantor() { + val regex = "a+b+?".toRegex() + val input = StringBuilder("aaaabbbb") + + assertEquals("aaaab", regex.find(input)!!.value) + assertEquals("aaaabbbb", regex.matchEntire(input)!!.value) + } + @test fun escapeLiteral() { val literal = """[-\/\\^$*+?.()|[\]{}]""" assertTrue(Regex.fromLiteral(literal).matches(literal)) diff --git a/libraries/stdlib/test/text/StringTest.kt b/libraries/stdlib/test/text/StringTest.kt index aa9c8694ae8..95499bf46a6 100644 --- a/libraries/stdlib/test/text/StringTest.kt +++ b/libraries/stdlib/test/text/StringTest.kt @@ -664,7 +664,7 @@ ${" "} """.trimIndent() assertEquals(23, deindented.lines().size()) - val indents = deindented.lines().map { "^\\s*".toRegex().match(it)!!.value.length } + val indents = deindented.lines().map { "^\\s*".toRegex().find(it)!!.value.length } assertEquals(0, indents.min()) assertEquals(42, indents.max()) assertEquals(1, deindented.lines().count { it.isEmpty() }) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/FinishBuildListener.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/FinishBuildListener.kt index 53fca256bb9..82ec1deefc7 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/FinishBuildListener.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/FinishBuildListener.kt @@ -32,7 +32,7 @@ internal fun getUsedMemoryKb(): Long { private fun comparableVersionStr(version: String) = "(\\d+)\\.(\\d+).*" .toRegex() - .match(version) + .find(version) ?.groups ?.drop(1)?.take(2) // checking if two subexpression groups are found and length of each is >0 and <4 diff --git a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt index c1df35e7ace..7db4c7b0562 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinGradlePluginIT.kt @@ -64,7 +64,7 @@ class KotlinGradleIT: BaseGradleIT() { for (i in 1..3) { project.build(userVariantArg, "build", options = BaseGradleIT.BuildOptions(withDaemon = true)) { assertSuccessful() - val matches = "\\[PERF\\] Used memory after build: (\\d+) kb \\(([+-]?\\d+) kb\\)".toRegex().match(output) + val matches = "\\[PERF\\] Used memory after build: (\\d+) kb \\(([+-]?\\d+) kb\\)".toRegex().find(output) assert(matches != null && matches.groups.size() == 3, "Used memory after build is not reported by plugin") val reportedGrowth = matches!!.groups.get(2)!!.value.removePrefix("+").toInt() assert(reportedGrowth <= 700, "Used memory growth $reportedGrowth > 700")