INTERNAL-377: Placeholder generation from regex source expression

This commit is contained in:
airatmeister 2022-12-20 16:14:37 +03:00
parent c74c23cf7a
commit 393ba8d8cb
8 changed files with 63 additions and 227 deletions

View File

@ -1,9 +0,0 @@
package ru.touchin.roboswag.textprocessing
object Constants {
internal const val digits = "1234567890"
internal const val charsRuUpper = "АБВГДЕЖЗИЙКЛМНОПРСТУФЧЦЧЭЮЯЪЬЫШ"
internal const val charsEngUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
internal const val charsRuLower = "абвгдежзийклмнопрстуфчцэюяъьыш"
internal const val charsEngLower = "abcdefghijklmnopqrstuvwxyz"
}

View File

@ -11,7 +11,7 @@ class TextFormatter(private val regex: String) {
private val decoroMaskGenerator = DecoroMaskGenerator()
private val pcreGeneratorItem = regexReplaceGenerator.regexToRegexReplace(regex)
private val regexReplaceString = pcreGeneratorItem.regexReplaceString
private val listForPlaceholder = pcreGeneratorItem.listForPlaceholder
private val listForPlaceholder = pcreGeneratorItem.list
private val placeholderGenerator = PlaceholderGenerator(listForPlaceholder)
fun getFormatText(inputText: String) = inputText.replace(Regex(regex), regexReplaceString)

View File

@ -11,11 +11,11 @@ class DecoroMaskGenerator {
/** Генерация маски и слотов на основе возможных символов для placeholder,
* если возможные символы не более одного, то символ хардкодится в слот
* **/
fun mask(placeholder: String, listForPlaceholder: List<String>): MaskFormatWatcher {
fun mask(placeholder: String, listForPlaceholder: List<List<Char>>): MaskFormatWatcher {
val slots = mutableListOf<Slot>()
for (i in placeholder.indices) {
slots.add(
if (listForPlaceholder[i].length == 1) {
if (listForPlaceholder[i].size == 1) {
PredefinedSlots.hardcodedSlot(placeholder[i])
} else {
CustomValidator.customSlot(listForPlaceholder[i])

View File

@ -1,90 +1,29 @@
package ru.touchin.roboswag.textprocessing.generators
import ru.touchin.roboswag.textprocessing.Constants.charsEngLower
import ru.touchin.roboswag.textprocessing.Constants.charsEngUpper
import ru.touchin.roboswag.textprocessing.Constants.charsRuLower
import ru.touchin.roboswag.textprocessing.Constants.charsRuUpper
import ru.touchin.roboswag.textprocessing.Constants.digits
class PlaceholderGenerator(
listForPlaceholder: List<String>
listForPlaceholder: List<List<Char>>
) {
private var placeholder: String = ""
init {
/** Индексы для всех возможных вариантов:
* Цифр, Ангийских букв верхнего регистра,
* Русских букв верхнего регистра,
* Английских букв нижнего регистра,
* Русских букв верхнего и нижнего регистра,
* Английских букв верхнего и нижнего регистра,
* На случай остальных повторений
* **/
var indexDigits = 0
var indexCharsEngUpper = 0
var indexCharsRuUpper = 0
var indexCharsEngLower = 0
var indexCharsRuLower = 0
var indexUpNLowerRu = 0
var indexUpNLowerEng = 0
var indexElse = 0
for (str in listForPlaceholder) {
if (str.isEmpty()) continue
val hashMap = hashMapOf<List<Char>, Int>()
for (listOfChar in listForPlaceholder) {
hashMap[listOfChar] = 0
}
for (listOfChar in listForPlaceholder) {
if (listOfChar.isEmpty()) continue
/** Если элемент без повторений **/
if (str.length == 1) {
placeholder += str
if (listOfChar.size == 1) {
placeholder += listOfChar[0]
continue
}
when {
/** Если элемент повторяет цифры **/
digits.contains(str) -> {
if (str.length <= indexDigits) indexDigits = 0
placeholder += str[indexDigits]
indexDigits++
}
/** Если элемент повторяет Английские буквы верхнего регистра **/
charsEngUpper.contains(str) -> {
if (str.length <= indexCharsEngUpper) indexCharsEngUpper = 0
placeholder += str[indexCharsEngUpper]
indexCharsEngUpper++
}
/** Если элемент повторяет Английские буквы нижнего регистра **/
charsEngLower.contains(str) -> {
if (str.length <= indexCharsEngLower) indexCharsEngLower = 0
placeholder += str[indexCharsEngLower]
indexCharsEngLower++
}
/** Если элемент повторяет Русские буквы верхнего регистра **/
charsRuUpper.contains(str) -> {
if (str.length <= indexCharsRuUpper) indexCharsRuUpper = 0
placeholder += str[indexCharsRuUpper]
indexCharsRuUpper++
}
/** Если элемент повторяет Русские буквы нижнего регистра **/
charsRuLower.contains(str) -> {
if (str.length <= indexCharsRuLower) indexCharsRuLower = 0
placeholder += str[indexCharsRuLower]
indexCharsRuLower++
}
/** Если элемент повторяет Русские буквы верхнего и нижнего регистра **/
charsRuLower.contains(str[str.length - 1]) -> {
if (str.length <= indexUpNLowerRu) indexUpNLowerRu = 0
placeholder += str[indexUpNLowerRu]
indexUpNLowerRu++
}
/** Если элемент повторяет Аглийские буквы верхнего и нижнего регистра **/
charsEngLower.contains(str[str.length - 1]) -> {
if (str.length <= indexUpNLowerEng) indexUpNLowerEng = 0
placeholder += str[indexUpNLowerEng]
indexUpNLowerEng++
}
/** Если элемент повторяет Другие символы **/
else -> {
if (str.length <= indexElse) indexElse = 0
placeholder += str[indexElse]
indexElse++
}
hashMap[listOfChar]?.let { j ->
var i = j
if (listOfChar.size <= i) i = 0
placeholder += listOfChar[i]
i++
hashMap[listOfChar] = i
}
}
}

View File

@ -2,5 +2,5 @@ package ru.touchin.roboswag.textprocessing.generators.regexgenerator
class PCREGeneratorItem(
val regexReplaceString: String,
val listForPlaceholder: List<String>
val list: List<List<Char>>
)

View File

@ -1,6 +1,5 @@
package ru.touchin.roboswag.textprocessing.generators.regexgenerator
import ru.touchin.roboswag.textprocessing.Constants
import ru.touchin.roboswag.textprocessing.pcre.parser.PCREBaseListener
import ru.touchin.roboswag.textprocessing.pcre.parser.PCREParser
@ -9,7 +8,7 @@ class PCREGeneratorListener : PCREBaseListener() {
* Лист для placeholder, где индекс - номер буквы для placeholder
* значение - возможные символы для placeholder
* **/
private var mutableList = mutableListOf<String>()
private var mutableListOfListChar = mutableListOf<List<Char>>()
private var currentGroupIndex = 1
private var regexReplaceString = ""
@ -19,7 +18,7 @@ class PCREGeneratorListener : PCREBaseListener() {
* [1-2], \\d, [A-B], а так же элементы не относящиеся к регулярным выражениям
* или экранизированые
* **/
private var charsAtom = ""
private var charsList = mutableListOf<Char>()
override fun enterCapture(ctx: PCREParser.CaptureContext) {
super.enterCapture(ctx)
@ -30,8 +29,8 @@ class PCREGeneratorListener : PCREBaseListener() {
override fun enterShared_atom(ctx: PCREParser.Shared_atomContext) {
super.enterShared_atom(ctx)
/** Найдено соответствие цифр \\d **/
charsAtom = Constants.digits
mutableList.add(charsAtom)
charsList = '1'.rangeTo('9').toMutableList().apply { add('0') }
mutableListOfListChar.add(charsList)
}
override fun enterCharacter_class(ctx: PCREParser.Character_classContext) {
@ -41,37 +40,65 @@ class PCREGeneratorListener : PCREBaseListener() {
* false - если, например [А-д]
* **/
if (ctx.cc_atom().size > 1) {
charsAtom = ""
charsList = mutableListOf<Char>()
val firstChar = ctx.CharacterClassStart().text
val endChar = ctx.CharacterClassEnd()[0].text
for (i in 0 until ctx.cc_atom().size) {
charsAtom += checkRules(firstChar + ctx.cc_atom()[i].text + endChar)
charsList += checkRules(firstChar + ctx.cc_atom()[i].text + endChar)
}
} else {
charsAtom = checkRules(ctx.text)
charsList = checkRules(ctx.text)
}
mutableList.add(charsAtom)
mutableListOfListChar.add(charsList)
}
/** Дублирование повторений для placeholder при их наличии, например [A-B]{6}, где 6 - повторения **/
override fun enterDigits(ctx: PCREParser.DigitsContext) {
super.enterDigits(ctx)
repeat(ctx.text.toInt() - 1) {
mutableList.add(charsAtom)
mutableListOfListChar.add(charsList)
}
}
override fun enterLiteral(ctx: PCREParser.LiteralContext) {
super.enterLiteral(ctx)
regexReplaceString += ctx.shared_literal().text
charsAtom = ctx.text
mutableList.add(charsAtom)
charsList = mutableListOf<Char>()
for (s in ctx.text) {
charsList.add(s)
}
mutableListOfListChar.add(charsList)
}
fun toPCREGeneratorItem() = PCREGeneratorItem(
regexReplaceString,
mutableList.map {
it.replace("\\", "")
}.toMutableList()
mutableListOfListChar.map { it ->
it.filter {
it != '\\'
}
}
)
private fun checkRules(ctxText: String): MutableList<Char> {
/** endAtomStr index of atomStr.length - 2 вычисляется потому что с поиском, например,
* в [A-B] должен проверяться последний допуск для строки в данном случае это В
* startAtomStr = atomStr[1] - потому что должен проверяться первый допуск для строки
* в [A-B] это будет А
* **/
val endAtomStr = ctxText[ctxText.length - 2]
val startAtomStr = ctxText[1]
return if (startAtomStr.isLetterOrDigit() && endAtomStr.isLetterOrDigit()) {
getListRangeChars(ctxText).filter {
it.isLetterOrDigit()
}.toMutableList()
} else {
mutableListOf(startAtomStr, endAtomStr)
}
}
private fun getListRangeChars(atomStr: String): MutableList<Char> {
val startRange = atomStr[1]
val endRange = atomStr[atomStr.length - 2]
return startRange.rangeTo(endRange).toMutableList()
}
}

View File

@ -1,121 +0,0 @@
package ru.touchin.roboswag.textprocessing.generators.regexgenerator
import ru.touchin.roboswag.textprocessing.Constants
data class IndexesItem(val firstIndex: Int, val lastIndex: Int)
internal fun PCREGeneratorListener.checkRules(ctxText: String): String {
var atomStr = ctxText
var resultCharsAtom = ""
/** endAtomStr index of atomStr.length - 2 вычисляется потому что с поиском, например,
* в [A-B] должен проверяться последний допуск для строки в данном случае это В
* startAtomStr = atomStr[1] - потому что должен проверяться первый допуск для строки
* в [A-B] это будет А
* **/
val endAtomStr = atomStr[atomStr.length - 2]
val startAtomStr = atomStr[1]
when {
atomStr.contains('-') -> {
when {
/** Проверка соответствия начала диапазона Русских букв на верхний регистр **/
Constants.charsRuUpper.contains(startAtomStr) -> {
when {
/** Проверка соответствия диапазона Русских букв с верхним и нижнем регистром **/
Constants.charsRuLower.contains(endAtomStr) -> {
atomStr = atomStr.uppercase()
val (firstIndex, lastIndex) = findFirstAndLastIndex(atomStr, Constants.charsRuUpper)
resultCharsAtom = Constants.charsRuUpper.substring(firstIndex, lastIndex) +
Constants.charsRuLower.substring(firstIndex, lastIndex)
}
/** Проверка соответствия диапазона Русских букв с верхним регистром **/
else -> {
atomStr = atomStr.uppercase()
val (firstIndex, lastIndex) = findFirstAndLastIndex(atomStr, Constants.charsRuUpper)
resultCharsAtom = Constants.charsRuUpper.substring(firstIndex, lastIndex)
}
}
}
/** Проверка соответствия начала диапазона Русских букв на нижний регистр **/
Constants.charsRuLower.contains(startAtomStr) -> {
when {
/** Проверка соответствия диапазона Русских букв с нижним и верхнем регистром **/
Constants.charsRuUpper.contains(endAtomStr) -> {
atomStr = atomStr.lowercase()
val (firstIndex, lastIndex) = findFirstAndLastIndex(atomStr, Constants.charsRuLower)
resultCharsAtom = Constants.charsRuLower.substring(firstIndex, lastIndex) +
Constants.charsRuUpper.substring(firstIndex, lastIndex)
}
/** Проверка соответствия диапазона Русских букв с нижним регистром **/
else -> {
atomStr = atomStr.lowercase()
val (firstIndex, lastIndex) = findFirstAndLastIndex(atomStr, Constants.charsRuLower)
resultCharsAtom = Constants.charsRuLower.substring(firstIndex, lastIndex)
}
}
}
/** Проверка соответствия начала диапазона Английских букв на верхний регистр **/
Constants.charsEngUpper.contains(startAtomStr) -> {
when {
/** Проверка соответствия диапазона Английских букв с верхним и нижнем регистром **/
Constants.charsEngLower.contains(endAtomStr) -> {
atomStr = atomStr.uppercase()
val (firstIndex, lastIndex) = findFirstAndLastIndex(atomStr, Constants.charsEngUpper)
resultCharsAtom = Constants.charsEngUpper.substring(firstIndex, lastIndex) +
Constants.charsEngLower.substring(firstIndex, lastIndex)
}
/** Проверка соответствия диапазона Аглийских букв с верхним регистром **/
else -> {
atomStr = atomStr.uppercase()
val (firstIndex, lastIndex) = findFirstAndLastIndex(atomStr, Constants.charsEngUpper)
resultCharsAtom = Constants.charsEngUpper.substring(firstIndex, lastIndex)
}
}
}
/** Проверка соответствия начала диапазона Английских букв на нижний регистр **/
Constants.charsEngLower.contains(startAtomStr) -> {
when {
/** Проверка соответствия диапазона Английских букв с нижним и верхнем регистром **/
Constants.charsEngUpper.contains(endAtomStr) -> {
atomStr = atomStr.lowercase()
val (firstIndex, lastIndex) = findFirstAndLastIndex(atomStr, Constants.charsEngLower)
resultCharsAtom = Constants.charsEngLower.substring(firstIndex, lastIndex) +
Constants.charsEngUpper.substring(firstIndex, lastIndex)
}
/** Проверка соответствия диапазона Аглийских букв с нижним регистром **/
else -> {
atomStr = atomStr.lowercase()
val (firstIndex, lastIndex) = findFirstAndLastIndex(atomStr, Constants.charsEngLower)
resultCharsAtom = Constants.charsEngLower.substring(firstIndex, lastIndex)
}
}
}
/** Проверка соответствия диапазона Цифр букв с нижним регистром **/
Constants.digits.contains(startAtomStr) -> {
/** Регулярки проверяют с 0, а placeholder с 1 **/
val digitsRegex = "0123456789"
val (firstIndex, lastIndex) = findFirstAndLastIndex(atomStr, digitsRegex)
resultCharsAtom = digitsRegex.substring(firstIndex, lastIndex)
}
}
}
/** Результат при остальных соответствиях **/
else -> {
resultCharsAtom = atomStr.substring(1, atomStr.length - 1)
}
}
return resultCharsAtom
}
/** atomStr в себе должен содержать atomStr(описание выше)
* chars в себе должен содержать элементы по порядку алфавитному или возрастанию
* тех символов, которые находятся в atomStr
* **/
internal fun PCREGeneratorListener.findFirstAndLastIndex(atomStr: String, chars: String): IndexesItem {
var firstIndex = 0
var lastIndex = chars.length - 1
for (i in chars.indices) {
if (chars[i] == atomStr[1]) firstIndex = i
if (chars[i] == atomStr[atomStr.length - 2]) lastIndex = i + 1
}
return IndexesItem(firstIndex, lastIndex)
}

View File

@ -3,11 +3,11 @@ package ru.touchin.roboswag.textprocessing.validators
import ru.tinkoff.decoro.slots.Slot
class CustomValidator private constructor(
private val slotItems: String
private val slotItems: List<Char>
) : Slot.SlotValidator {
companion object {
fun customSlot(slotItems: String) = Slot(null, CustomValidator(slotItems))
fun customSlot(slotItems: List<Char>) = Slot(null, CustomValidator(slotItems))
}
override fun validate(value: Char): Boolean {