How to avoid evaluating an GString - groovy

I'm working on extending a legacy script system using groovy. The source scripts are "java-like", so it mostly parses as a groovy script with a little pre-processing.
I'm using invokeMethod() and missingMethod() to pass-through the legacy code, enabling me to use closures and other groovy features to enhance the scripts. However, the original script uses strings of the type "$foo" to refer to variables. When a legacy method is caught by missingMethod(), I need it to not evaluate this string as a GString, but simply outputting it verbatim. Is this possible in any way?

there are two way to escape the $foo:
escape the '$' as '\$'
use ' instead of " as string delimiter
example:
def test = "bad"
def s0 = "$test"
def s1 = "\$test"
assert s1 != s0
def s2 = '$test'
assert s2 == s1
println s0
println s1
println s2
So I guess you have to use your preprocessor in order to escape your strings

Related

How to compare strings in groovy script

I cannot understand why my simple String equality test is returning false.
Code is:
boolean isDevelopment() {
//config.project_stage is set to "Development"
String cfgvar = "${config.project_stage}"
String comp = "Development"
assert cfgvar.equals(comp)
}
Result is:
assert cfgvar.equals(comp)
| | |
| false Development
Development
I also get false if I do:
assert cfgvar == comp
toString() is not necessary. Most probably you have some trailing
spaces in config.project_stage, so they are retained also in cfgvar.
comp has no extra spaces, what can be seen from your code.
Initially the expression "${config.project_stage}" is of GString
type, but since you assign it to a variable typed as String,
it is coerced just to String, so toString() will not change anything.
It is up to you whether you use equals(...) or ==.
Actually Groovy silently translates the second form to the first.
So, to sum up, you can write assert cfgvar.trim() == comp.
You can also trim cfgvar at the very beginning, writing:
cfgvar = "${config.project_stage}".trim()
and then not to worry about any trailing spaces.
Have you checked for trailing spaces? At least your output as one for the first Development. Try a .trim() when you compare those strings (and maybe a .toLowerCase() too)
And remember: .equals() in Groovy is a pointer comparison. What want to do is ==. Yes, just the opposite from what it is defined in Java, but the Groovy definition makes more sense :-)
Update: see comment by #tim_yates - I mixed .equals() up with .is()
On of the objects you comparing is not a String but GString, try:
cfgvar.toString().equals(comp)
However your code works with groovy v. 2.4.5. Which version are you using?

Replacing a word with $ in groovy

I have the following piece of groovy code from a gradle build file I am using for replacing a word in a php file:
def str1="My application version is $app_version"
def str2 = str1.replaceAll('$app_version','2016072601')
println str2​
I want to replace $app_version with defined number in this method but somehow it is not working. Do I need to escape or do any special action to perform this replacement?
Strings with double quotes and $ are GStrings, which triggers Groovy's string interpolation. Also, replaceAll receives a regex as first argument, and $ is a special character in regex, thus, you need to double escape that too.
You can use single quotes OR you can escape the $ character in your str1:
def str1='My application version is $app_version'
def str2 = str1.replaceAll('\\$app_version','2016072601')
assert str2 == 'My application version is 2016072601'
Update: Expanding string interpolation a bit, it replaces the $ placeholder in your string with the variable value of the same name (though not immediately, as it creates an instance of GString first). It is roughly akin to this in Java:
String str1 = "My application version is " + app_version;
So, instead of replacing the variable with replaceAll, you could have a variable in your script with the name app_version, like def app_version = "2016072601" (which is kinda like #BZ.'s answer)
Update 2: By using string interpolation (as per #BZ.'s answer) or string concatenation, you don't need to use replaceAll, but you need a variable called app_version:
def app_version = '2016072601'
def str1 = "My application version is $app_version"
assert str1 == 'My application version is 2016072601'
And with string concatenation you also need a variable called app_version:
def app_version = '2016072601'
def str1 = "My application version is " + app_version
assert str1 == 'My application version is 2016072601'
Alternately:
def versionString = "2016072601"
def string2 = "My application version is $versionString"

Are Groovy String expressions anything more than syntactic sugar?

I saw this piece of code in the Groovy tutorial -
import groovy.sql.Sql
sql = Sql.newInstance( 'jdbc:jtds:sqlserver://serverName/dbName-CLASS;domain=domainName', 'username', 'password', 'net.sourceforge.jtds.jdbc.Driver' )
sql.eachRow( 'select * from tableName' ) { println "$it.id -- ${it.firstName} --" }
And it is the first occurrence of Groovy expressions (anything inside a ${} gets evaluated as an expression, not a string). My question is, does this Groovy expression feature actually provide some new functionality? It seems to me like there is nothing here that can be done with a good old string concat. IE -
println it.id + " -- " + it.firstName + " --"
Yes. That's what they are. Being able to add code and variables into strings is a feature of Groovy that make Groovy groovy. String in Groovy can be just like templates.
Now, Groovy strings are enclosed in (") quotes. But a standard Java String in Groovy is enclosed in an apostrophe ('). Standard Java strings in groovy cannot contain variable references or code.
It makes code easier to read. Sometimes looking at all the '+' signs in Java as part of string concatenation is a PITA.
What would you rather code and read:
println "$it.id -- ${it.firstName} --"
or
println it.id + " -- " + it.firstName + " --"
Now, with local variables it becomes much easier to read too:
Groovy
def name = "some name"
def age = 30
def waist = 42
println "Did $name really have a size $waist waist at age $age?"
Java:
String name = "some name";
int age = 30;
int waistSize = 42;
System.out.println("Did " + name + " really have a " + waist + " waist at age " age + "?";
Another thing you can do with them is use them as slightly Lazy Templates, ie:
def x = "abc"
// Closure expansion rather than variable
def s = "x: ${->x}"
// Prints 'x: abc'
println s
// Change x
x = 400
// Prints 'x: 400'
println s
With the pure String concatenation, you'd end up writing a print function, or repeating your concatenation code
Also, don't forget the multi-line string operators -- such as """ -- and the stripMargin method which allows you to :
def (name,age,sender) = [ 'Alice', 30, 'Tim' ]
println """Hello $name
|
|$age today? Congratulations!
|
|$sender""".stripMargin()
Which prints:
Hello Alice
30 today? Congratulations!
Tim
Again, could be done with String concatenation, but ends up being loads more typing and error prone (imo)
Lots more about Groovy strings (various flavours) here: http://groovy.codehaus.org/Strings+and+GString
I believe any string of the form "a string" (i.e. double quotes) is an instance of GString in Groovy (not String). It is GString that provides these extra capabilities.
With the accepted answer here we seem to have landed on the conclusion that, yes, GString (i.e. a double-quoted string with one or more ${<expr>} expressions) is just syntax sugar. (I'm going by the first sentence of the accepted answer: "Yes. That's what they are.")
But that seems to be wrong because of lazy evaluation, as noted above by tim_yates.
Expanding a bit on the above, if one or more expressions in the string are closures, they are only evaluated when toString() is called on the GString. So, in groovyconsole:
def stime = "The time is: ${-> new Date().getTime()}."
println stime
Thread.sleep(500)
println stime
The time is: 1384180043679.
The time is: 1384180044194.
How would you do this with + without creating a new string every time? (The answer, in two Java projects I've worked on, was to invent a Template.java class, to do this sort of thing.) This suggests to me that there is more than just syntax sugar. Or perhaps that it is syntax sugar - but for the GString class, not for java.lang.String or strings generally.
Another example:
def vals = ['a':42]
def tmpl = "The answer is ${-> vals.a}."
println tmpl
vals.a = 43
println tmpl
The answer is 42.
The answer is 43.
I'm not sure I actually like this feature of GString; I'm sort of used to strings being immutable (coming from Java). Groovy's strings are not (quite). It seems like you ought to be able to assume that if two Groovy 'strings' reference the same instance, they will have the same textual value, but that is not quite true.
http://groovy.codehaus.org/Strings+and+GString (about halfway down)

illegal string body character after dollar sign

if i define a groovy variable
def x = "anish$"
it will throw me error, the fix is
def x = "anish\$"
apart form "$" what are the blacklist characters that needs to be backslash,Is there a Groovy reference that lists the reserved characters. Most “language specifications” mention these details, but I don’t see it in the Groovy language spec (many “TODO” comments).
Just use single quotes:
def x = 'anish$'
If this isn't possible, the only thing that's going to cause you problems is $, as that is the templating char used by GString (see the GString section on this page -- about half way down)
Obviously, the backslash char needs escaping as well, ie:
def x = 'anish\\'
You can use octal representation. the character $ represents 044 in octal, then:
def x = 'anish\044'
or
def x = 'anish\044'
For example, in Java i did use like this:
def x = 'anish\044'
If you wants knows others letters or symbols converters, click here :)
The solution from tim_yates does not work in some contexts, e.g. in a Jasper report.
So if still everything with a $ sign wants to be interpreted as some variable (${varX}), e.g. in
"xyz".replaceAll("^(.{4}).{3}.+$", "$1...")
then simply make the dollar sign a single concatenated character '$', e.g.
"xyz".replaceAll("^(.{4}).{3}.+"+'$', '$'+"1...")
It might be a cheap method, but the following works for me.
def x = "anish" + '$'
Another alternative that is useful in Groovy templating is ${'$'}, like:
def x = "anish${'$'}" // anish$
Interpolate the Java String '$' into your GString.

What's the difference of strings within single or double quotes in groovy?

def a = "a string"
def b = 'another'
Is there any difference? Or just like javascript to let's input ' and " easier in strings?
Single quotes are a standard java String
Double quotes are a templatable String, which will either return a GString if it is templated, or else a standard Java String. For example:
println 'hi'.class.name // prints java.lang.String
println "hi".class.name // prints java.lang.String
def a = 'Freewind'
println "hi $a" // prints "hi Freewind"
println "hi $a".class.name // prints org.codehaus.groovy.runtime.GStringImpl
If you try templating with single quoted strings, it doesn't do anything, so:
println 'hi $a' // prints "hi $a"
Also, the link given by julx in their answer is worth reading (esp. the part about GStrings not being Strings about 2/3 of the way down.
My understanding is that double-quoted string may contain embedded references to variables and other expressions. For example: "Hello $name", "Hello ${some-expression-here}". In this case a GString will be instantiated instead of a regular String. On the other hand single-quoted strings do not support this syntax and always result in a plain String. More on the topic here:
http://docs.groovy-lang.org/latest/html/documentation/index.html#all-strings
I know this is a very old question, but I wanted to add a caveat.
While it is correct that single (or triple single) quotes prevent interpolation in groovy, if you pass a shell command a single quoted string, the shell will perform parameter substitution, if the variable is an environment variable. Local variables or params will yield a bad substitution.

Resources