Skip to content

Commit

Permalink
Merge pull request #510 from FWDekker/inverse-regex
Browse files Browse the repository at this point in the history
Implement generating non-matching regex strings
  • Loading branch information
FWDekker authored Dec 8, 2023
2 parents 6932789 + a87ce65 commit 55a870b
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 26 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog
## Unreleased
### Added
* Added ability to generate non-matching strings.
([#447](https://github.com/FWDekker/intellij-randomness/issues/447))

### Changed
* When inserting arrays at multiple carets, the number of elements per array is now independently chosen for each array.
([#450](https://github.com/FWDekker/intellij-randomness/issues/450))
Expand Down
24 changes: 18 additions & 6 deletions src/main/kotlin/com/fwdekker/randomness/string/StringScheme.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@ import kotlin.random.asJavaRandom
*
* @property pattern The regex-like pattern according to which the string is generated.
* @property isRegex `true` if and only if [pattern] should be interpreted as a regex.
* @property removeLookAlikeSymbols Whether the symbols in [LOOK_ALIKE_CHARACTERS] should be removed.
* @property isNonMatching `true` if and only if non-matching values should be generated if [isRegex] is `true`.
* @property capitalization The capitalization mode of the generated string.
* @property removeLookAlikeSymbols Whether the symbols in [LOOK_ALIKE_CHARACTERS] should be removed.
* @property arrayDecorator Settings that determine whether the output should be an array of values.
*/
data class StringScheme(
var pattern: String = DEFAULT_PATTERN,
var isRegex: Boolean = DEFAULT_IS_REGEX,
var removeLookAlikeSymbols: Boolean = DEFAULT_REMOVE_LOOK_ALIKE_SYMBOLS,
var isNonMatching: Boolean = DEFAULT_IS_NON_MATCHING,
var capitalization: CapitalizationMode = DEFAULT_CAPITALIZATION,
var removeLookAlikeSymbols: Boolean = DEFAULT_REMOVE_LOOK_ALIKE_SYMBOLS,
val arrayDecorator: ArrayDecorator = DEFAULT_ARRAY_DECORATOR,
) : Scheme() {
override val name = Bundle("string.title")
Expand All @@ -50,7 +52,10 @@ data class StringScheme(
val rawStrings =
if (isRegex) {
val rgxGen = RgxGen(pattern)
List(count) { rgxGen.generate(random.asJavaRandom()) }
List(count) {
if (this.isNonMatching) rgxGen.generateNotMatching(random.asJavaRandom())
else rgxGen.generate(random.asJavaRandom())
}
} else {
List(count) { pattern }
}
Expand All @@ -73,7 +78,9 @@ data class StringScheme(
else ->
@Suppress("detekt:TooGenericExceptionCaught") // Consequence of incomplete validation in RgxGen
try {
RgxGen(pattern).generate()
if (this.isNonMatching) RgxGen(pattern).generate()
else RgxGen(pattern).generateNotMatching()

arrayDecorator.doValidate()
} catch (exception: RgxGenParseException) {
exception.message
Expand Down Expand Up @@ -117,9 +124,9 @@ data class StringScheme(
const val DEFAULT_IS_REGEX = true

/**
* The default value of the [removeLookAlikeSymbols] field.
* The default value of the [isNonMatching] field.
*/
const val DEFAULT_REMOVE_LOOK_ALIKE_SYMBOLS = false
const val DEFAULT_IS_NON_MATCHING = false

/**
* The preset values for the [capitalization] field.
Expand All @@ -136,6 +143,11 @@ data class StringScheme(
*/
val DEFAULT_CAPITALIZATION get() = CapitalizationMode.RETAIN

/**
* The default value of the [removeLookAlikeSymbols] field.
*/
const val DEFAULT_REMOVE_LOOK_ALIKE_SYMBOLS = false

/**
* The default value of the [arrayDecorator] field.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import com.intellij.ui.dsl.builder.bindText
import com.intellij.ui.dsl.builder.panel
import com.intellij.ui.dsl.builder.toNullableProperty
import com.intellij.ui.dsl.gridLayout.HorizontalAlign
import com.intellij.ui.layout.selected
import javax.swing.JCheckBox


/**
Expand All @@ -38,19 +40,19 @@ class StringSchemeEditor(scheme: StringScheme = StringScheme()) : SchemeEditor<S
}

row("") {
lateinit var isRegexBox: JCheckBox

checkBox(Bundle("string.ui.value.is_regex_option"))
.loadMnemonic()
.withName("isRegex")
.bindSelected(scheme::isRegex)
}
.also { isRegexBox = it.component }

row("") {
checkBox(Bundle("string.ui.value.remove_look_alike"))
checkBox(Bundle("string.ui.value.is_non_matching_option"))
.loadMnemonic()
.withName("removeLookAlikeCharacters")
.bindSelected(scheme::removeLookAlikeSymbols)

contextHelp(Bundle("string.ui.value.remove_look_alike_help", StringScheme.LOOK_ALIKE_CHARACTERS))
.withName("isNonMatching")
.bindSelected(scheme::isNonMatching)
.enabledIf(isRegexBox.selected)
}.bottomGap(BottomGap.SMALL)

row(Bundle("string.ui.value.capitalization_option")) {
Expand All @@ -59,6 +61,15 @@ class StringSchemeEditor(scheme: StringScheme = StringScheme()) : SchemeEditor<S
.withName("capitalization")
.bindItem(scheme::capitalization.toNullableProperty())
}

row {
checkBox(Bundle("string.ui.value.remove_look_alike"))
.loadMnemonic()
.withName("removeLookAlikeCharacters")
.bindSelected(scheme::removeLookAlikeSymbols)

contextHelp(Bundle("string.ui.value.remove_look_alike_help", StringScheme.LOOK_ALIKE_CHARACTERS))
}
}

row {
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/randomness.properties
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ string.error.trailing_backslash=Regex pattern must not end in unescaped backslas
string.title=String
string.ui.value.capitalization_option=&Capitalization:
string.ui.value.header=Value
string.ui.value.is_non_matching_option=&Non-matching
string.ui.value.is_regex_option=Parse as rege&x
string.ui.value.pattern_help=Supported syntax
string.ui.value.pattern_help_url=https://github.com/curious-odd-man/RgxGen/tree/1.4#supported-syntax
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ object StringSchemeEditorTest : FunSpec({
false,
)
},
"isInverseRegex" to {
row(
frame.checkBox("isNonMatching").isSelectedProp(),
editor.scheme::isNonMatching.prop(),
true,
)
},
"removeLookAlikeCharacters" to {
row(
frame.checkBox("removeLookAlikeCharacters").isSelectedProp(),
Expand Down
38 changes: 25 additions & 13 deletions src/test/kotlin/com/fwdekker/randomness/string/StringSchemeTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,32 @@ object StringSchemeTest : FunSpec({
mapOf(
"false if invalid" to
row(StringScheme(pattern = "{}"), false),
"false if pattern uses quantifier" to
row(StringScheme(pattern = "[u]{4}"), false),
"false if pattern uses grouping" to
row(StringScheme(pattern = "(a|b)"), false),
"true if pattern is plain string, as non-regex" to
row(StringScheme(pattern = "text", isRegex = false), true),
"true if pattern is plain string, as regex" to
row(StringScheme(pattern = "text", isRegex = true), true),
"true if pattern is plain string, as matching regex" to
row(StringScheme(pattern = "text"), true),
"false if pattern is plain string, as non-matching regex" to
row(StringScheme(pattern = "text", isNonMatching = true), false),
"true if pattern escapes character, as non-regex" to
row(StringScheme(pattern = """te\[xt""", isRegex = false), true),
"true if pattern escapes character, as regex" to
row(StringScheme(pattern = """te\[xt""", isRegex = true), true),
"true if pattern escapes character, as matching regex" to
row(StringScheme(pattern = """te\[xt"""), true),
"false if pattern escapes character, as non-matching regex" to
row(StringScheme(pattern = """te\[xt""", isNonMatching = true), false),
"true if pattern escapes backslash, as non-regex" to
row(StringScheme(pattern = """te\\xt""", isRegex = false), true),
"true if pattern escapes backslash, as regex" to
row(StringScheme(pattern = """te\\xt""", isRegex = true), true),
"true if pattern uses quantifier, as non-regex" to
row(StringScheme(pattern = "[u]{4}", isRegex = false), true),
"true if pattern escapes backslash, as matching regex" to
row(StringScheme(pattern = """te\\xt"""), true),
"false if pattern escapes backslash, as non-matching regex" to
row(StringScheme(pattern = """te\\xt""", isNonMatching = true), false),
"false if pattern uses quantifier, as matching regex" to
row(StringScheme(pattern = "[u]{4}"), false),
"false if pattern uses quantifier, as non-matching regex" to
row(StringScheme(pattern = "[u]{4}", isNonMatching = true), false),
"false if pattern uses grouping, as matching regex" to
row(StringScheme(pattern = "(a|b)"), false),
"false if pattern uses grouping, as non-matching regex" to
row(StringScheme(pattern = "(a|b)", isNonMatching = true), false),
)
) { (scheme, isSimple) -> scheme.isSimple() shouldBe isSimple }
}
Expand All @@ -63,6 +71,8 @@ object StringSchemeTest : FunSpec({
row(StringScheme(pattern = "a[bc]d", isRegex = false), "a[bc]d"),
"returns reverse-regexed string" to
row(StringScheme(pattern = "[x]{4}"), "xxxx"),
"returns non-matching reverse-regexed string" to
row(StringScheme(pattern = ".", isNonMatching = true), ""),
)
) { (scheme, output) -> scheme.generateStrings()[0] shouldBe output }
}
Expand All @@ -72,8 +82,10 @@ object StringSchemeTest : FunSpec({
mapOf(
"succeeds for default state" to
row(StringScheme(), null),
"fails if pattern is invalid" to
"fails if matching pattern is invalid" to
row(StringScheme(pattern = "{x"), ""),
"fails if non-matching pattern is invalid" to
row(StringScheme(pattern = "{x", isNonMatching = true), ""),
"fails if pattern is empty curly braces" to
row(StringScheme(pattern = "{}"), "string.error.empty_curly"),
"fails if pattern has empty curly braces" to
Expand Down

0 comments on commit 55a870b

Please sign in to comment.