Sunday, 6 September 2015

My Tryst with Groovy DSL - Part2

In this concluding episode of "My Tryst with Groovy DSL" we are going to stress on building various components with Groovy DSL. Lets say, We have an annotation Imitate as:

@Retention (RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
@GroovyASTTransformationClass("com.groovy.ast.transformers.ImitateTransformation")
@interface Imitate {  
}

Ohhh Yes the Groovy Transformation class is ImitateTransformation, soon we will get to know its role soon. and the Usage of the annotation is like:

@Main
class MainExample {
 
 static def name = "MICKY"
 
 @Imitate   
 def testMeth(def args,def m){
  def x = 0
  m(args)
 }
 
 def m2={String ok ->
  println "Okkkk haishaaaa $ok"
 } 
}

class MyClass {

 static def args33 = "Mic in MyClass"
}

Now our Objective is something like this:

public Object mi3 = {String args21 ->
      this.println '##### Closure Declaration ######'
}

public static Object mi4 = {String args31 ->
       this.println args33
       this.println args31
}

public void NEWOKNew1(String args)
{
 this.testMeth(args) {String args2 -> 
     this.println 'Closure Call Done'
     this.println args2
     }
 this.mi3()
 mi4.setResolveStrategy(Closure.DELEGATE_FIRST)
 mi4.setDelegate(new MyClass())
 MainExample.mi4(args)
     
 
 
}

So, to put it in words, our objective is: To declare two closures (mi3,mi4) one having static scope. Then we will declare a new method NEWOKNew1() and invoke the closures in order and all these operation we going to implement with Groovy AST using transformation class. The GroovyASTTransformationClass class is:

@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
class ImitateTransformation implements ASTTransformation {

 @Override
 public void visit(ASTNode[] astNodes, SourceUnit sourceUnit) {
  // TODO Auto-generated method stub

  def statement = astNodes[1].code

  def varscope = statement.variableScope

  Map referencedLocalVariablesMap = varscope.referencedLocalVariables
  referencedLocalVariablesMap.each { key , value ->

   println "-->${key.class}"
   println "-->${value.class}"
  }

  /**
   * 
   * Source Unit is the Groovy Source Code File
   * containing the annotation.
   * 
   */

  println "===========The SourceUnit is ${sourceUnit.name}"



  def shareVariables = { variableScope ->
   println "The variable scope is: ${variableScope.referencedClassVariables}"
   //def scope = variableScope.copy()
   def map = variableScope.referencedLocalVariables
   println "&&&& The map is:$map"
   map.each{ key, value ->
    println "***** The Variable is $value.name"
    value.setClosureSharedVariable(true)
   }
   variableScope
  }

  shareVariables(statement.variableScope)

  // create new block statement
  /* BlockStatement block = new BlockStatement()
   block.variableScope = shareVariables(statement.variableScope)
   // create closure expression, use code as an argument
   ClosureExpression closure = new ClosureExpression(Parameter.EMPTY_ARRAY, statement)
   closure.variableScope = statement.variableScope.copy()
   // create method call expression, use the closure as an argument
   MethodCallExpression methodCall = new MethodCallExpression(new VariableExpression('this'),
   'NewOk', new ArgumentListExpression(closure))
   // add method call to the block statement
   block.addStatement(new ExpressionStatement(methodCall))
   nodes[1].code = block
   */

  MethodNode annotatedMethod = astNodes[1]
  ClassNode declaringClass = astNodes[1].declaringClass

  def closre = new AstBuilder().buildFromSpec {
   closure {

    parameters {
     parameter 'args2': String.class
    }

    block {
     expression {
      methodCall {
       variable "this"
       constant "println"
       argumentList {
        constant "Closure Call Done"
        //parameter "args2"
       }
       //constant "OKKK"
      }
     }

     expression {
      methodCall {
       variable "this"
       constant "println"
       argumentList {
        //constant "Closure Call Done"
        variable "args2"
       }
      }
     }
    }
   }
  }

  closre[0].variableScope = new VariableScope()


  /**
   * 
   * @author MIC
   * 
   * Defining another Closure
   *
   */


  def closre2 = new AstBuilder().buildFromSpec {
   closure {

    parameters {
     parameter 'args21': String.class
    }

    block {
     expression {
      methodCall {
       variable "this"
       constant "println"
       argumentList {
        constant "##### Closure Declaration ######"
       }
      }
     }

     /*expression {
      methodCall {
       variable "this"
       constant "println"
       argumentList {
        //constant "Closure Call Done"
        variable "args"
       }
      }
     }*/
    }
   }
  }

  closre2[0].variableScope = new VariableScope();//shareVariables(astNodes[1].getVariableScope())


  /**
   * Defining a static Closure
   */

  def closre3 = new AstBuilder().buildFromSpec {
   closure {

    parameters {
     parameter 'args31': String.class
    }

    block {
     expression {
      methodCall {
       variable "this"
       constant "println"
       argumentList {
        variable "args33"
       }
      }
     }

     expression {
      methodCall {
       variable "this"
       constant "println"
       argumentList {
        variable "args31"
       }
      }
     }
    }
   }
  }


  closre3[0].variableScope = new VariableScope()



  //End of Closure Declaration



  /** 
   * @author MIC
   * 
   * Adding a field node to a class
   *
   */


  FieldNode fieldNode= new FieldNode('mi3', 1, new ClassNode(Object.class), new ClassNode(declaringClass.getClass()), closre2[0])

  FieldNode staticFieldNode= new FieldNode('mi4', ACC_PUBLIC | ACC_STATIC, new ClassNode(Object.class), new ClassNode(declaringClass.getClass()), closre3[0])


  //End of Adding a FieldNode

  def ast = new AstBuilder().buildFromSpec {
   method('NEWOKNew1', ACC_PUBLIC , Void.TYPE) {

    parameters {
     parameter 'args': String.class
    }

    exceptions{

    }
   }

  }


  def consCallExpr = new ConstructorCallExpression(new ClassNode(MyClass.class), MethodCallExpression.NO_ARGUMENTS);

  def methCallExpr = new MethodCallExpression(new VariableExpression("mi4"), "setDelegate", consCallExpr);

  PropertyExpression propertyExpression = new PropertyExpression(new ClassExpression(new ClassNode(Closure.class)), "DELEGATE_FIRST")

  def methCallExpr2 = new MethodCallExpression(new VariableExpression("mi4"), "setResolveStrategy", propertyExpression);


  ArgumentListExpression argListExpr = new ArgumentListExpression()
  argListExpr.addExpression(new VariableExpression("args"))
  argListExpr.addExpression(closre[0])

  ArgumentListExpression argListExpr2 = new ArgumentListExpression()
  argListExpr2.addExpression(new VariableExpression("args"))


  MethodCallExpression methodCall = new MethodCallExpression(new VariableExpression('this'),
    'testMeth',argListExpr)

  MethodCallExpression methodCall2 = new MethodCallExpression(new VariableExpression('this'),
    'mi3', new ArgumentListExpression())

  StaticMethodCallExpression methodCall3 = new StaticMethodCallExpression(new ClassNode(MainExample.class), 'mi4', argListExpr2)


  def methNode = ast[0]
  def blockStatementList = [new ExpressionStatement(methodCall), new ExpressionStatement(methodCall2), new ExpressionStatement(methCallExpr2) ,new ExpressionStatement(methCallExpr), new ExpressionStatement(methodCall3)]
  BlockStatement blockStatement = new BlockStatement(blockStatementList, new VariableScope())
  methNode.setCode(blockStatement)


  declaringClass.addProperty(new PropertyNode(fieldNode, ACC_PUBLIC, null, null));
  declaringClass.addProperty(new PropertyNode(staticFieldNode, ACC_PUBLIC, null, null));
  declaringClass.addMethod(methNode)



 }



}

Now lets explain the the groovy transformation code : At the beginning lies my some of findings with VariableScope, but really I was not able to figure it out how it is applicable and its true scope, It would be a great help, if someone could point me so, or help me in getting hold some resources, which will clarify my doubts :-) :-) The there comes the closure declarations, closre, closre2 & closre3 and we also set variablescope for each of them, otherwise the AST changes won't be applied. Like:

def closre = new AstBuilder().buildFromSpec {
   closure {

    parameters {
     parameter 'args2': String.class
    }

    block {
     expression {
      methodCall {
       variable "this"
       constant "println"
       argumentList {
        constant "Closure Call Done"
        //parameter "args2"
       }
       //constant "OKKK"
      }
     }

     expression {
      methodCall {
       variable "this"
       constant "println"
       argumentList {
        //constant "Closure Call Done"
        variable "args2"
       }
      }
     }
    }
   }
  }

  closre[0].variableScope = new VariableScope()
  
  .........

Then we decalre two properties of type FieldNode, and they point to closre2 & closre3 respectively.

  FieldNode fieldNode= new FieldNode('mi3', 1, new ClassNode(Object.class), new ClassNode(declaringClass.getClass()), closre2[0])

  FieldNode staticFieldNode= new FieldNode('mi4', ACC_PUBLIC | ACC_STATIC, new ClassNode(Object.class), new ClassNode(declaringClass.getClass()), closre3[0])


Then we declare the new method NEWOKNew1(),

   def ast = new AstBuilder().buildFromSpec {
   method('NEWOKNew1', ACC_PUBLIC , Void.TYPE) {

    parameters {
     parameter 'args': String.class
    }

    exceptions{

    }
   }

  }


followed by setting Delegate & ResolveStrategy for closure pointed by mi4.
 
  def consCallExpr = new ConstructorCallExpression(new ClassNode(MyClass.class), MethodCallExpression.NO_ARGUMENTS);

  def methCallExpr = new MethodCallExpression(new VariableExpression("mi4"), "setDelegate", consCallExpr);

  PropertyExpression propertyExpression = new PropertyExpression(new ClassExpression(new ClassNode(Closure.class)), "DELEGATE_FIRST")

  def methCallExpr2 = new MethodCallExpression(new VariableExpression("mi4"), "setResolveStrategy", propertyExpression);
 
 
Then we declare the Argument List to be passed to testMeth(), mi3(), & mi4() respectively and we also defined the MethodCall expressions for testMeth(), mi3(), & mi4() with the help of Argument List prepared earlier.
  ArgumentListExpression argListExpr = new ArgumentListExpression()
  argListExpr.addExpression(new VariableExpression("args"))
  argListExpr.addExpression(closre[0])

  ArgumentListExpression argListExpr2 = new ArgumentListExpression()
  argListExpr2.addExpression(new VariableExpression("args"))

  MethodCallExpression methodCall = new MethodCallExpression(new VariableExpression('this'),
    'testMeth',argListExpr)

  MethodCallExpression methodCall2 = new MethodCallExpression(new VariableExpression('this'),
    'mi3', new ArgumentListExpression())

  StaticMethodCallExpression methodCall3 = new StaticMethodCallExpression(new ClassNode(MainExample.class), 'mi4', argListExpr2)


Lastly, we put all those MethodCall Expressions and PropertyExpressions in BlockStatement with a new VariableScope and it to the newly created method NEWOKNew1()
 
  def methNode = ast[0]
  def blockStatementList = [new ExpressionStatement(methodCall), new ExpressionStatement(methodCall2), new ExpressionStatement(methCallExpr2) ,new ExpressionStatement(methCallExpr), new ExpressionStatement(methodCall3)]
  BlockStatement blockStatement = new BlockStatement(blockStatementList, new VariableScope())
  
  
  declaringClass.addProperty(new PropertyNode(fieldNode, ACC_PUBLIC, null, null));
  declaringClass.addProperty(new PropertyNode(staticFieldNode, ACC_PUBLIC, null, null));
  declaringClass.addMethod(methNode)
  methNode.setCode(blockStatement)
 
 
So, for this time thats all that I had been doing with Groovy AST.

Please feel free to share your views and comments and till next time keep coding and keep sharing.

View Subhankar Paul's profile on LinkedIn