I'm new to gradle and am currently just trying to follow the tutorials and quite a few times I've seen single and double quotes intermixed. I just wanted to know if there was a difference of when one set should be used over the other. One example of this is section 6.12 of the tutorial - Default tasks:
defaultTasks 'clean', 'run'
task clean << {
println 'Default Cleaning!'
}
task run << {
println 'Default Running!'
}
task other << {
println "I'm not a default task!"
}
So, I would just like to know if I should be paying attention to these differences or if they are inter-changable and I can use either single or double quotes when printing strings in gradle.
Gradle build scripts are written in Groovy. Groovy has both double-quoted and single-quoted String literals. The main difference is that double-quoted String literals support String interpolation:
def x = 10
println "result is $x" // prints: result is 10
You can learn more about Groovy String interpolation in this or other Groovy articles on the web.
Yes, you can use one or the other. The only difference is that double-quoted strings can be GStrings, which can contain evaluated expressions like in the following example taken from the Groovy documentation:
foxtype = 'quick'
foxcolor = ['b', 'r', 'o', 'w', 'n']
println "The $foxtype ${foxcolor.join()} fox"
// => The quick brown fox
According to the gradle docs:
Favor single quotes for plain strings in build script listings
This is
mostly to ensure consistency across guides, but single quotes are also
a little less noisy than double quotes. Only use double quotes if you
want to include an embedded expression in the string.
Single-quoted strings are a series of characters surrounded by single quotes.
like :
def str='a single quoted string'
println str
Ouput :
a single quoted string
Whereas Double-quoted strings allow us the String interpolation
Here, we have a string with a placeholder referencing a local variable:
def name = 'Guillaume' // a plain string
def greeting = "Hello ${name}"
Output : Hello Guillaume
In your code,If you want to print the task name. So in that case, you need to use Double-quotes:
defaultTasks 'clean', 'run'
task clean << {
println 'Default Cleaning!'
}
task run << {
println "Default Running $run.name!"
// here Double Quotes are required to interpolate task-name
}
task other << {
println "I'm not a default task!"
}
Kotlin DSL files (like build.gradle.kts or settings.gradle.kts) are regular Kotlin code.
So, string literals are always wrapped in double quotes:
val myFirstString = "4 / 2 equals ${4 / 2}"
val mySecondString = """The property "hide" was not found"""
Related
I rather have this ugly way of building a string from a list as:
val input = listOf("[A,B]", "[C,D]")
val builder = StringBuilder()
builder.append("Serialized('IDs((")
for (pt in input) {
builder.append(pt[0] + " " + pt[1])
builder.append(", ")
}
builder.append("))')")
The problem is that it adds a comma after the last element and if I want to avoid that I need to add another if check in the loop for the last element.
I wonder if there is a more concise way of doing this in kotlin?
EDIT
End result should be something like:
Serialized('IDs((A B,C D))')
In Kotlin you can use joinToString for this kind of use case (it deals with inserting the separator only between elements).
It is very versatile because it allows to specify a transform function for each element (in addition to the more classic separator, prefix, postfix). This makes it equivalent to mapping all elements to strings and then joining them together, but in one single call.
If input really is a List<List<String>> like you mention in the title and you assume in your loop, you can use:
input.joinToString(
prefix = "Serialized('IDs((",
postfix = "))')",
separator = ", ",
) { (x, y) -> "$x $y" }
Note that the syntax with (x, y) is a destructuring syntax that automatically gets the first and second element of the lists inside your list (parentheses are important).
If your input is in fact a List<String> as in listOf("[A,B]", "[C,D]") that you wrote at the top of your code, you can instead use:
input.joinToString(
prefix = "Serialized('IDs((",
postfix = "))')",
separator = ", ",
) { it.removeSurrounding("[", "]").replace(",", " ") }
val input = listOf("[A,B]", "[C,D]")
val result =
"Serialized('IDs((" +
input.joinToString(",") { it.removeSurrounding("[", "]").replace(",", " ") } +
"))')"
println(result) // Output: Serialized('IDs((A B,C D))')
Kotlin provides an extension function [joinToString][1] (in Iterable) for this type of purpose.
input.joinToString(",", "Serialized('IDs((", "))')")
This will correctly add the separator.
The embedded expressions are not replaced when appended, prepended or surrounded by characters in the following simplified and very basic scenario:
* def jobId = '0001'
* def out =
"""
{
"jobId": "#(jobId)",
"outputMetadata": {
"fileName_OK": "#(jobId)",
"fileName_Fail_1": "some_text_#(jobId)",
"fileName_Fail_2": "#(jobId)-and-some-more-text",
"fileName_Fail_3": "prepend #(jobId) and append"
}
}
"""
* print out
Executing the scenario returns:
{
"jobId": "0001",
"outputMetadata": {
"fileName_OK": "0001",
"fileName_Fail_1": "some_text_#(jobId)",
"fileName_Fail_2": "#(jobId)-and-some-more-text",
"fileName_Fail_3": "prepend #(jobId) and append"
}
}
Is it a feature, a limitation, or a bug? Or, did I miss something?
This is as designed ! You can do this:
"fileName_Fail_2": "#(jobId + '-and-some-more-text')"
Any valid JS expression can be stuffed into an embedded expression, so this is not a limitation. And this works only within JSON string values or when the entire RHS is a string within quotes and keeps the parsing simple. Hope that helps !
I have written a code that reads a text file. The text files contain placeholders which I would like to replace. The substitution does not work this way and the string is printed with the placeholders. Here is the code that I have written for this:
class TestSub(val sub: Sub) {
fun create() = template()
fun template() = Files.newBufferedReader(ClassPathResource(templateId.location).file.toPath()).readText()
}
data class Sub(val name: String, val age: Int)
Here is the main function that tries to print the final string:
fun main(args: Array<String>) {
val sub = Sub("Prashant", 32)
println(TestSub(sub).create())
}
However, when, instead of reading a file, I use a String, the following code works (Replacing fun template())
fun template() = "<h1>Hello ${sub.name}. Your age is ${sub.age}</h1>"
Is there a way to make string Substitution work when reading the content of a file?
Kotlin does not support String templates from files. I.e. code like "some variable: $variable" gets compiled to "some variable: " + variable. String templates are handled at compile time, which means it does not work with text loaded from files, or if you do something else to get the String escaped into a raw form. Either way, it would, as danielspaniol mentioned, be a security threat.
That leaves three options:
String.format(str)
MessageFormat.format(str)
Creating a custom engine
I don't know what your file contains, but if it's the String you used in the template function, change it to:
<h1>Hello {0}. Your age is {1,integer}</h1>
This is for MessageFormat, which is my personal preference. If you use String.format, use %s instead, and the other appropriate formats.
Now, use that in MessageFormat.format:
val result = MessageFormat.format(theString, name, age);
Note that if you use MessageFormat, you'll need to escape ' as ''. See this.
String substitution using ${...} is part of the string literals syntax and works roughly like this
val a = 1
val b = "abc ${a} def" // gets translated to something like val b = "abc " + a + " def"
So there is no way for this to work when you load from a text file. This would also be a huge security risk as it would allow for arbitrary code execution.
However I assume that Kotlin has something like a sprintf function where you can have placeholders like %s in your string and you can replace them with values
Take a look here. It looks like the easiest way is to use String.format
You are looking for something similar to Kotlin String templates for raw Strings, where placeholders like $var or ${var} are substituted by values, but this functionality needs to be available at runtime (for text read from files).
Methods like String.format(str) or MessageFormat.format(str) use other formats than the notation with the dollar prefix of Kotlin String templates. For "Kotlin-like" placeholder substitution you could use the function below (which I developed for similar reasons). It supports placeholders as $var or ${var} as well as dollar escaping by ${'$'}
/**
* 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).
*
* Example:
* "\$var1\${var2}".resolve(mapOf("var1" to "VAL1", "var2" to "VAL2"))
* returns VAL1VAL2
*/
fun String.resolve(values: Map<String, String>): String {
val result = StringBuilder()
val matcherSimple = "\\$([a-zA-Z_][a-zA-Z_0-9]*)" // simple placeholder e.g. $var
val matcherWithBraces = "\\$\\{([a-zA-Z_][a-zA-Z_0-9]*)}" // placeholder within braces e.g. ${var}
// match a placeholder (like $var or ${var}) or ${'$'} (escaped dollar)
val allMatches = Regex("$matcherSimple|$matcherWithBraces|\\\$\\{'(\\\$)'}").findAll(this)
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))
return result.toString()
}
String templates only work for compile-time Sting literals, while what u read from a file is generated at runtime.
What u need is a template engine, which can render templates with variables or models at runtime.
For simple cases, String.format or MessageFormat.format in Java would work.
And for complex cases, check thymeleaf, velocity and so on.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
I'm using the directory mail merge feature in Word 2010 to create a formatted document (of requirements) using an Excel 2010 spreadsheet as the source of data.
Things work, but I have a compact mess of braces, formatted single characters, and fields, with very little whitespace (as this would change my output). I inserted fields with CTRL-F9 and Word treats them as their own objects rather than individual characters.
The complications come from having a lot of conditional fields, some of which are nested, and don't have the typical cues that most code has for seeing the nesting structure (line breaks, indentation, etc.)
Is there a straightforward way to document the mail merge template code from within the document? Or will I need to maintain a separate text file if I want to take advantage of whitespace and other readability cues?
Things you can do to help with field code documentation include the following. But there are quite a few gotchas, which complicates the answer. I've tried to cover them below.
Use formatting characters such as paragraph marks inside field code
braces to improve layout
Use field code types with empty results such as "SET" to insert
documentation
Use hidden text inside field codes to insert documentation
Remove of QUOTATION MARK (") characters that are not absolutely
necessary
An example of (1):
If you have something like:
{ IF { MERGEFIELD A } = { bookmarka } "{ MERGEFIELD B }" "{ IF { MERGEFIELD C } = { bookmarkb } "result2" "result3" }" }
Then you can lay it out like this without altering the result of the IF fields:
{ IF { MERGEFIELD A } = { bookmarka }
"{ MERGEFIELD B }"
"{ IF { MERGEFIELD C } = { bookmarkb }
"result2"
"result3" }" }
by clicking after { bookmarka } and pressing Enter to enter a normal Word paragraph mark, then inserting some spaces before the QUOTATION MARK character.
Different people might choose different indentation patterns and ways of dealing with closing "}". There are no standards or widely used traditions in this area as there are with, say, C code.
There is a drawback. Every time you switch between field code and field results views, Word may repaginate in a long document and the chances are that you will "lose your place" in the code.
An example of (2) is that you can insert a { SET } field in many places in your code (but not all) and put pretty much any text after the "SET variablename " part. Or you could define a bookmark called "REMARK" ("COMMENT" seems too easily confused with the built-in field code COMMENTS)
e.g.
{ SET REMARK I wouldn't use "COMMENT" }
or put
{ SET REMARK "" }
at the beginning of your code then use
{ REMARK I wouldn't use "COMMENT" }
{ REMARK because it might be confused with the "COMMENTS" field }
Just bear in mind that you can't put a field code absolutely anywhere in your field code "block", because in some places Word's intepretation of the field coding will change. e.g.
{ IF 1 = 0 { MERGEFIELD X } { MERGEFIELD Y } }
should always insert the value of { MERGEFIELD Y }
but
{ IF 1 = 1 { MERGEFIELD X }{ REMARK insert X } { MERGEFIELD Y } }
would insert the result of { REMARK insert X }
You could use
{ IF 1 = 1 "{ MERGEFIELD X }{ REMARK insert X }" { MERGEFIELD Y } }
or in this case,
{ IF 1 = 1 { MERGEFIELD X { REMARK insert X } } { MERGEFIELD Y } }
On point (3) Word generally ignores hidden text in field codes, so you can use hidden text to insert documentation. But again, you have to be careful where you put it. For example, this approach is useless for field types that are normally marked as Hidden, such as TC and XE. Personally, I do not think the idea of inserting documentation then hiding it is ideal - quite apart from anything else, it would be very easy to delete without even knowing it was there. But it can be done.
As for point 4, the way Microsoft Word inserts some field types and the documentation (such as it is) for the "field code language" means that there is a tradition of surrounding some things with QUOTATION MARK characters. Examples typically show this in IF fields...
{ IF comparand-1 operator comparand-2 "result if true" "result if false" }
In that particular case, you would need the quotation marks, but if the "result if true" or "result if false" are either a single word (as far as Word is concerned) or the result of a single field code, then you can omit the quotation marks, e.g.
{ IF comparand-1 operator comparand-2 true false }
or
{ IF comparand-1 operator comparand-2 { REF bookmarka } { REF bookmarkb } }
Personally, I prefer to use the quotation marks, because it is the pattern that many users are familiar with, and if you modify the field code in some way you don't have to work out whether you need to re-insert quotaion marks, and so on. But that is just my preference. In the case where you have nested IF fields in particular, you very often have individual field codes as results and the quotation marks probably do not really increase clarity.
You also have to bear in mind that in some cases you must surround fields with quotation marks. For example, if you want to ensure that two values are compared as strings, at the very least you should quote them.
For example, suppose you have a mergefield X that could contain any text string and you want to compare that string with "3". If you use
{ IF { MERGEFIELD X } = 3 True False }
then if X is "1+2", "3", "4-1" you will get a True result. In this case you need
{ IF "{ MERGEFIELD X }" = 3 True False }
and if the "3" was replaced by some kind of variable field, you would probably need
{ IF "{ MERGEFIELD X }" = "{ theVariable }" 3 True False }
Word's tendency to evaluate simple arithmetic expressions in such scenarios is not the only thing that can go wrong. If { MERGEFIELD X } evaluates to the name of a bookmark that you have defined in your document, Word tends to dereference that, and use the value of that bookmark instead of the value of the MERGEFIELD field, unless you surround the { MERGEFIELD X } by quotation marks.
I got this parameter:
$objDbCmd.Parameters.Add("#telephone", [System.Data.SqlDbType]::VarChar, 18) | Out-Null;
$objDbCmd.Parameters["#telephone"].Value = $objUser.Telephone;
Where the string $objUser.Telephone can be empty. If it's empty, how can I convert it to [DBNull]::Value?
I tried:
if ([string]:IsNullOrEmpty($objUser.Telephone)) { $objUser.Telephone = [DBNull]::Value };
But that gives me the error:
Exception calling "ExecuteNonQuery" with "0" argument(s): "Failed to convert parameter value from a ResultPropertyValueCollection to a String."
And if I convert it to a string, it inserts an empty string "", and not DBNull.
How can this be accomplished?
Thanks.
In PowerShell, you can treat null/empty strings as a boolean.
$x = $null
if ($x) { 'this wont print' }
$x = ""
if ($x) { 'this wont print' }
$x = "blah"
if ($x) { 'this will' }
So.... having said that you can do:
$Parameter.Value = $(if ($x) { $x } else { [DBNull]::Value })
But I'd much rather wrap this up in a function like:
function CatchNull([String]$x) {
if ($x) { $x } else { [DBNull]::Value }
}
I don't know about powershell, but in C# I would do something like this:
if ([string]::IsNullOrEmpty($objUser.Telephone))
{
$objDbCmd.Parameters["#telephone"].Value = [DBNull]::Value;
}
else
{
$objDbCmd.Parameters["#telephone"].Value = $objUser.Telephone;
}
Always append +"" at the end of db values...
$command.Parameters["#EmployeeType"].Value= $ADResult.EmployeeType + ""
Many years later, let me clarify:
Josh's answer shows a helpful simplification for testing strings for emptiness (relying on PowerShell's implicit to-Boolean conversion[1]), but it is unrelated to Tommy's (the OP's) problem.
Instead, the error message
"Failed to convert parameter value from a ResultPropertyValueCollection to a String."
implies that it is the non-null case that caused the problem, because $objDbCmd.Parameters["#telephone"].Value expects either a string value or [DBNull]::Value, whereas $objUser.Telephone is of type [ResultPropertyValueCollection], i.e. a collection of values.
Thus, in the non-null case, a string value must be assigned, which must be derived from the collection; one option is to take the first collection element's value, another would be to join all values with a separator to form a single string, using, e.g., [string]::Join(';', $objUser.Telephone) or, if joining the elements with spaces is acceptable (not a good idea with multiple phone numbers), simply with "$($objUser.Telephone)".[2]
Detecting an empty collection via [string]:IsNullOrEmpty() actually worked, despite the type mismatch, due to how PowerShell implicitly stringifies collections when passing a value to a [string] typed method parameter.[2]
Similarly, using implicit to-Boolean conversion works as expected with collections too: an empty collection evaluates to $false, a non-empty one to $true (as long as there are either at least two elements or the only element by itself would be considered $true[1])
Therefore, one solution is to use the first telephone number entry:
$objDbCmd.Parameters["#telephone"].Value = if ($objUser.Telephone) {
$objUser.Telephone[0].ToString() # use first entry
} else {
[DBNull]::Value
}
Note: If $objUser.Telephone[0] directly returns a [string], you can omit the .ToString() call.
In PowerShell v7+ you can alternatively shorten the statement via a ternary conditional:
$objDbCmd.Parameters["#telephone"].Value =
$objUser.Telephone ? $objUser.Telephone[0].ToString() : [DBNull]::Value
[1] For a comprehensive summary of PowerShell's automatic to-Boolean conversions, see the bottom section of this answer.
[2] When implicitly converting a collection to a string, PowerShell joins the stringified elements of a collection with a single space as the separator by default; you can override the separator with the automatic $OFS variable, but that is rarely done in practice; e.g., array 'foo', 'bar' is converted to 'foo bar'; note that this conversion does not apply when you call the collection's .ToString() method explicitly, but it does apply inside expandable (interpolating) strings, e.g., "$array".