improve function resolve

This commit is contained in:
ansgarz 2022-01-25 18:30:45 +01:00
parent c2fc62e13a
commit bfc459b667
2 changed files with 26 additions and 32 deletions

View file

@ -69,6 +69,7 @@ fun hostUserHome(): String = System.getProperty("user.home") + fileSeparator()
fun String.escapeAndEncloseByDoubleQuoteForShell(): String { fun String.escapeAndEncloseByDoubleQuoteForShell(): String {
return "\"" + this.escapeForShell() + "\"" return "\"" + this.escapeForShell() + "\""
} }
fun String.escapeForShell(): String { fun String.escapeForShell(): String {
// see https://www.shellscript.sh/escape.html // see https://www.shellscript.sh/escape.html
return this.escapeBackslash().escapeBacktick().escapeDoubleQuote().escapeDollar() return this.escapeBackslash().escapeBacktick().escapeDoubleQuote().escapeDollar()
@ -123,40 +124,33 @@ fun getResourceResolved(path: String, values: Map<String, String>): String {
/** /**
* Returns a String in which placeholders (e.g. $var or ${var}) are replaced by the specified values. * Returns a String in which placeholders (e.g. $var or ${var}) are replaced by the specified values.
* This function can be used for resolving templates at RUNTIME (e.g. for templates read from files) as * This function can be used for resolving templates at RUNTIME (e.g. for templates read from files) as
* for compile time this functionality is already provided by the compiler out-of-the-box, of course. * for compile time this functionality is already provided by the compiler out-of-the-box.
*
* For a usage example see the corresponding test. * For a usage example see the corresponding test.
*/ */
fun String.resolve( fun String.resolve(values: Map<String, String>): String {
values: Map<String, String>
): String {
var text = this
// replace all simple variable patterns (i.e. without curly braces) val result = StringBuilder()
val matcherSimple = Regex("\\$([a-zA-Z_][a-zA-Z_0-9]*)")
var match = matcherSimple.find(text) val matcherSimple = "\\$([a-zA-Z_][a-zA-Z_0-9]*)" // simple placeholder e.g. $var
while (match != null) { val matcherWithBraces = "\\$\\{([a-zA-Z_][a-zA-Z_0-9]*)}" // placeholder within braces e.g. ${var}
val variableName = match.groupValues.get(1)
val newText = values.get(variableName) // match a placeholder (like $var or ${var}) or ${'$'} (escaped dollar)
require(newText != null, { "No value found for: " + variableName }) val allMatches = Regex("$matcherSimple|$matcherWithBraces|\\\$\\{'(\\\$)'}").findAll(this)
text = text.replace("\$$variableName", newText)
match = matcherSimple.find(text) var position = 0
allMatches.forEach {
val range = it.range
val placeholder = this.substring(range)
val variableName = it.groups.filterNotNull()[1].value
val newText =
if ("\${'\$'}" == placeholder) "$"
else values[variableName] ?: throw IllegalArgumentException("Could not resolve placeholder $placeholder")
result.append(this.substring(position, range.start)).append(newText)
position = range.last + 1
} }
result.append(this.substring(position))
// replace all variables within curly braces return result.toString()
val matcherWithBraces = Regex("\\$\\{([a-zA-Z_][a-zA-Z_0-9]*)}")
match = matcherWithBraces.find(text)
while (match != null) {
val variableName = match.groupValues.get(1)
val newText = values.get(variableName)
require(newText != null, { "No value found for: " + variableName })
text = text.replace("\${$variableName}", newText)
match = matcherWithBraces.find(text)
}
// replace escaped dollars
text = text.replace("\${'$'}", "\$")
return text
} }
/** /**

View file

@ -120,6 +120,6 @@ internal class UtilsKtTest {
} }
// then // then
assertEquals(e.message, "No value found for: var3") assertEquals(e.message, "Could not resolve placeholder \${var3}")
} }
} }