INTERNAL-377: Placeholder generation from regex source expression
This commit is contained in:
parent
c74c23cf7a
commit
393ba8d8cb
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>>
|
||||
)
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in New Issue