groovymetaprogrammingkatalon-studio

Is it possible to get the name of variable in Groovy?


I would like to know if it is possible to retrieve the name of a variable.

For example if I have a method:

def printSomething(def something){
//instead of having the literal String something, I want to be able to use the name of the variable that was passed
println('something is: ' + something)
}

If I call this method as follows:

 def ordinary = 58
 printSomething(ordinary)

I want to get:

ordinary is 58

On the other hand if I call this method like this:

def extraOrdinary = 67
printSomething(extraOrdinary)

I want to get:

extraOrdinary is 67

Edit

I need the variable name because I have this snippet of code which runs before each TestSuite in Katalon Studio, basically it gives you the flexibility of passing GlobalVariables using a katalon.features file. The idea is from: kazurayam/KatalonPropertiesDemo

  @BeforeTestSuite
    def sampleBeforeTestSuite(TestSuiteContext testSuiteContext) {
        KatalonProperties props = new KatalonProperties()
        // get appropriate value for GlobalVariable.hostname loaded from katalon.properties files
        WebUI.comment(">>> GlobalVariable.G_Url default value: \'${GlobalVariable.G_Url}\'");

//gets the internal value of GlobalVariable.G_Url, if it's empty then use the one from katalon.features file
            String preferedHostname = props.getProperty('GlobalVariable.G_Url')
            if (preferedHostname != null) {
                GlobalVariable.G_Url = preferedHostname;
                WebUI.comment(">>> GlobalVariable.G_Url new value: \'${preferedHostname}\'");
            } else {
                WebUI.comment(">>> GlobalVariable.G_Url stays unchanged");
            }
 //doing the same for other variables is a lot of duplicate code
        }

Now this only handles 1 variable value, if I do this for say 20 variables, that is a lot of duplicate code, so I wanted to create a helper function:

def setProperty(KatalonProperties props, GlobalVariable var){
   WebUI.comment(">>> " + var.getName()" + default value: \'${var}\'");

//gets the internal value of var, if it's null then use the one from katalon.features file
            GlobalVariable preferedVar = props.getProperty(var.getName())
            if (preferedVar != null) {
                var = preferedVar;
                WebUI.comment(">>> " + var.getName() + " new value: \'${preferedVar}\'");
            } else {
                WebUI.comment(">>> " + var.getName() + " stays unchanged");
            }
}

Here I just put var.getName() to explain what I am looking for, that is just a method I assume.


Solution

  • Yes, this is possible with ASTTransformations or with Macros (Groovy 2.5+).

    I currently don't have a proper dev environment, but here are some pointers:

    Not that both options are not trivial, are not what I would recommend a Groovy novice and you'll have to do some research. If I remember correctly either option requires a separate build/project from your calling code to work reliable. Also either of them might give you obscure and hard to debug compile time errors, for example when your code expects a variable as parameter but a literal or a method call is passed. So: there be dragons. That being said: I have worked a lot with these things and they can be really fun ;)

    Groovy Documentation for Macros

    If you are on Groovy 2.5+ you can use Macros. For your use-case take a look at the @Macro methods section. Your Method will have two parameters: MacroContext macroContext, MethodCallExpression callExpression the latter being the interesting one. The MethodCallExpression has the getArguments()-Methods, which allows you to access the Abstract Syntax Tree Nodes that where passed to the method as parameter. In your case that should be a VariableExpression which has the getName() method to give you the name that you're looking for.

    Developing AST transformations

    This is the more complicated version. You'll still get to the same VariableExpression as with the Macro-Method, but it'll be tedious to get there as you'll have to identify the correct MethodCallExpression yourself. You start from a ClassNode and work your way to the VariableExpression yourself. I would recommend to use a local transformation and create an Annotation. But identifying the correct MethodCallExpression is not trivial.