tags:

views:

69

answers:

2

Hello,
I am trying to use recursion in groovy to traverse a tree relationship. The below code runs one cycle, upto childNodes && recurClosure(childNodes ) , but doesn't call the closure recurClosure again. At that instant childNodes had two objects (array list) same type as root.

In the code, recurClosure is defined and calls with a list of objects (root). It then iterates through each element and fines the child nodes (uses grails dsl for this).If the childNodes is not null, it recursively calls the parent method.

Should I break it up, or what is wrong?

def parentval 
def root = Domain.list()

def recurClosure
recurClosure = {inroot ->
  inroot.each {
    returnList << it
    parentval = it
    childNodes = Domain.withCriteria {
      eq('parent', parentval )
    }
  }
  childNodes && recurClosure(childNodes )
}(root)

return returnList

}

thanks in advance.

Update: The following exception is noted

    ERROR [2010-06-24 08:20:04,742] [groovy.grails.web.errors.GrailsExceptionResolver] Cannot invoke method call() on null object
java.lang.NullPointerException: Cannot invoke method call() on null object
    at com.bsr.test.DomainService$_closure2_closure7.doCall(com.bsr.test.DomainService:68)
    at com.bsr.test.DomainService$_closure2.doCall(com.bsr.test.DomainService:58)
    at com.bsr.test.DomainController$_closure3.doCall(DomainController.groovy:45)
    at com.bsr.test.DomainController$_closure3.doCall(DomainController.groovy)
    at org.apache.shiro.web.servlet.ShiroFilter.executeChain(ShiroFilter.java:687)
    at org.apache.shiro.web.servlet.ShiroFilter.doFilterInternal(ShiroFilter.java:616)
    at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:81)
    at java.lang.Thread.run(Thread.java:619)

Update 2: Now trying Daniel's suggestion.

{ inroot ->
    inroot.each {
        returnList << it
        parentval = it
        childNodes = Domain.withCriteria {
            eq('parent', parentval )
        }
           if(childNodes)
           call(childNodes)
    }
       /*if(childNodes)
        call(childNodes)*/

}(root)

In the above implementation, root is an arraylist, The inner closure takes each element out of it and recursively calls the anonymous closure. When I moved the 'call' inside the each closure, it doesn't call the outer anonymous closure, but the inroot.each {} itself. So, I get an error

ERROR [2010-06-24 08:47:46,438] [groovy.grails.web.errors.GrailsExceptionResolver] instance not of expected entity type: java.util.ArrayList is not a: com.bsr.test.Domain

I see a blog post about how to name the closure through 'this' > I'll update my finding.. thanks

Update 3: The way to call the outer closure is owner.call(childNodes)

+1  A: 

Do you get any exceptions? Have you tried invoking it separately like this:

def recurClosure
recurClosure = {inroot ->
  [... stuff ...]
}

recurClosure(root)

What exactly do you want to do in this line:

childNodes && recurClosure(childNodes )
air_blob
bsreekanth
+5  A: 

The problem is, that by

recurClosure = {
    [...]
}(root)

you don't assign the closure to recurClosure, but rather the return value of its invocation! Thus, of course, you can't call the closure via recurClosure()...

Two possible solutions:

First define the closure, and then call it separately, as air_blob suggested:

def recurClosure = {
    [...]
}
recurClosure(root)

Use the implicit call() function for recursion. This way you can even work with an anonymous closure. IMHO a very nice way to implement recursion in Groovy:

{ inroot ->
    inroot.each {
        returnList << it
        parentval = it
        childNodes = Domain.withCriteria {
            eq('parent', parentval )
        }
    }
    if(childNodes)
        call(childNodes)
}(root)

Two more comments on your code:

  1. You may want to declare returnList: def returnList = []
  2. While childNodes && recurClosure(childNodes ) may do what you want, it's much more readable to sacrifice one more char and spell out the if.. ;-)
  3. Don't you want to recursively call your closure inside the each?

Another (higher-level) remark on your code: If the parents and their children are of the same type (Domain), won't Domain.list() actually return all children, too? Is there really a need for traversing the tree manually?

Daniel Rinser
Daniel.. excited to see your answer, as it catches all the mistakes in the code.. Well, I adapted my code listing to make it simpler for the question. So, 1. I did defined returnList as an array list. 2. I do agree 3. You are right, that what I wanted (I tried moving the statement in and out of that closure to see the effect). I certainly was not doing a list() on the domain class, infact get it from another table. I never intented to mislead an expert like you (both in groovy and grails :-|) .. I am trying out your anonymous closure implementation .. will update in a bit .. thanks a lot
bsreekanth
Wow, excellent answer +1
air_blob
Daniel, I have updated the question with some more details.. now the question is, how to invoke the outer anonymous closure through 'call'. thanks again.
bsreekanth
thanks.. found the solution to call the outer closure. I need to invoke owner.call(childNodes) .. thanks again for ur detailed answer.
bsreekanth