Skip to content

reading a reflexive association in a recursive way for grails and groovy

Imagine you have a Menu with multiple items, those items have multiple items and so on. The Menu Items also depend on a Role a User has in the Application Context like ‘ROLE_ADMIN’, ‘ROLE_COOK’, ‘ROLE_SOME’ …
I think the best practice is to save the menu items as a reflexive association in a database table like this:

class MenuItem {
 
    Long id;
    String label;
    Boolean topLevel;
    Integer position;
 
    static hasMany = [roles:Role, childs:MenuItem]
    static belongsTo = [parent:MenuItem]
 
    static mapping = {
        topLevel type:'true_false'
        childs sort:'position'
    }
    static constraints = {
    }
 
    String toString() {
        "[MenuItem id: ${id}, label: ${label}, pos: ${position}]"
    }
 
}

so lets initialize some dummy menu Items:

def file =new MenuItem(label:'file', position:1,topLevel:true,parent:null, roles:[[somerole,cookrole,adminrole]).save();
def view =new MenuItem(label:'view', position:2,topLevel:true,parent:null, roles:[cookrole,adminrole]).save();
def help =new MenuItem(label:'help', position:2,topLevel:true,parent:null, roles:[someroleadminrole]).save();
def logout =new MenuItem(label:'logout', position:2,topLevel:true,parent:null, roles:[somerole,cookrole,adminrole]).save();

those 4 toplevel MenuItems have different roles assiciated with them, for example the view menu can only be accessed by a user wich has the cookrole or the adminrole. the roles have also the property of a level as a number which is zero for an admin user and one for a role which has less rights than the admin. the algorithm then extracts the hightest role from a user instance.
so lets insert some submenus to the database table:

new MenuItem(label:'save File', position:1,topLevel:false,parent:file, roles:[[somerole,cookrole,adminrole]).save();
new MenuItem(label:'save All Files', position:2,topLevel:false,parent:file, roles:[[somerole,cookrole,adminrole]).save();
def serivces = new MenuItem(label:'services', position:3,topLevel:false,parent:file, roles:[[somerole,cookrole,adminrole]).save();
new MenuItem(label:'capture Image', position:1,topLevel:false,parent:serivces, roles:[[somerole,cookrole,adminrole]).save();
new MenuItem(label:'send As Email', position:2,topLevel:false,parent:serivces, roles:[[somerole,adminrole]).save();

notice that the last menu item (send As Email) is not accessible for the cookrole
We now have a three dimensional menu like this:

  • file
    • save File
    • save All Files
    • services
      • capture Image
      • send As Email
  • view
  • help
  • logout

here is the code to render this menu structure in a controller and send it to the view:

class MainController {
 
    def authenticateService
    def testUser = null
    def menuItems = [:]
 
    void populateMenuItems(map,hightestRole){
 
        //fill the first time with topLevel Items
        if(menuItems.isEmpty()){
            map.each({
                    if(it.roles.contains(hightestRole))
                        menuItems[it] = [:]
            })
        }
 
        menuItems.each({ mi ->
 
                MenuItem.findAllByParent(mi.key).each({ subMi ->
 
                        if(subMi.roles.contains(hightestRole)){
                            mi.value[subMi] = [:]
 
                            findMore( mi.value[subMi],subMi)
                        }
                })
 
        })
    }
 
    void findMore(mapEntry,mi){
 
        def moreItems = MenuItem.findAllByParent(mi)
 
        if(!moreItems.isEmpty()){
 
            moreItems.each({
                    mapEntry[it] = [:]
                    findMore(mapEntry[it],it) //recursive call
            })
 
        }
    }
 
    def index = {
 
        def hightestRole = null
        def principal = (testUser != null)? testUser : authenticateService.principal();
        def itRole
 
        principal.getAuthorities().each({
 
                itRole = Role.findByAuthority(it.authority)
 
                if(hightestRole == null || ( hightestRole != null && itRole.level < hightestRole.level))
                    hightestRole = itRole
 
        })
 
        populateMenuItems( MenuItem.findAllByTopLevel( true, [sort: 'position', order: 'asc'] ), hightestRole )
 
        return [
                    highestRole:hightestRole,
                    menuItems:menuItems,
                    user: principal
                ]
    }
}

the function populateMenuItems calls the function findMore which calls itself recursively as long as a submenu for some item exists.

here are some integation tests for this:

def miSaveFile = MenuItem.findByLabel('save File')
def miSendAsEmail = MenuItem.findByLabel('send As Email')
 
def user = User.get(2)
assertEquals "bestcook", user.username
 
controller.testUser = user;
def model =  controller.index()
 
assertEquals "bestcook", model["bestcook"]?.username
assertEquals Role.findByAuthority("ROLE_COOK"),model["highestRole"]
assertFalse SearchNestedHash.search(model, miSendAsEmail)
assertTrue SearchNestedHash.search(model, miSaveFile)

the SearchNestedHash class is explained in my previous article.

Categories: algorithms, grails, groovy.

Tags: , , , , , ,

recursive search on a nested (multidimensional) collection for groovy

you can search for any object in a nested list containing hash’s and lists recursively, check out the tests for examples:

here are the tests:

class SearchNestedHashTests extends GrailsUnitTestCase {
    protected void setUp() {
        super.setUp()
    }
    protected void tearDown() {
        super.tearDown()
    }
    void testHashMap(){
 
            def map = [
                            'x' , 'y', 's'
                         ]
 
            assertTrue SearchNestedHash.search(map,'x')
 
            def a = 'a'
            def b = false
            def c = 1
 
            map= [
                        [[a,b] : c] : 'y'
                   ]
 
            assertTrue SearchNestedHash.search(map,a)
            assertTrue SearchNestedHash.search(map,b)
            assertTrue SearchNestedHash.search(map,c)
            assertTrue SearchNestedHash.search(map,false)
 
            map = [
                           'y' : 'null'
                       ]
 
            assertFalse SearchNestedHash.search(map,'x')
 
            map = [
                           'x':'null'
                       ]
 
            assertTrue SearchNestedHash.search(map,'x')
 
            map = [
                        ['n' : 'y'] : 'x'
                    ]
 
            assertFalse SearchNestedHash.search(map,'t')
            assertFalse SearchNestedHash.search(map,null)
            assertTrue SearchNestedHash.search(map,'x')
 
            map= [
                        ['n' : 'x'] : 'y'
                    ]
 
            assertTrue SearchNestedHash.search(map,'x')
 
            map = [
                        ['x' : 'n'] : 'y'
                    ]
 
            assertTrue SearchNestedHash.search(map,'x')
 
            map= [
                        [['x':'z'] : 'n'] : 'y'
                    ]
 
            assertTrue SearchNestedHash.search(map,'x')
 
            map= [
                        [['c':'x'] : 'n'] : null
                    ]
 
            assertTrue SearchNestedHash.search(map,'x')
 
            def x = ['a':'b']
 
            map= [
                        [['y': x ] : null] : 'y'
                    ]
 
            assertTrue SearchNestedHash.search(map,x)
 
            map= [
                        [['y': ['a':'b'] ] : null] : 'y'
                    ]
 
            assertTrue SearchNestedHash.search(map,x)
 
            map= [
                        [['s':'x'] : null] : 'y'
                    ]
 
            assertTrue SearchNestedHash.search(map,'x')
 
            def crazyMap = [
                        [['c':'v'] : [['c':[['c':[[[[['xxsxx':'v'] : [['c':[['c':[[[          'X'            :'v']:'v']:'v']]:'v']]:'v']]:'v']:'v']:'v']]:'v']]:'v']] : 'y'
                    ]
 
            assertFalse SearchNestedHash.search(crazyMap,'q')
            assertFalse SearchNestedHash.search(crazyMap,'x')//only the capital X is inside
            assertTrue  SearchNestedHash.search(crazyMap,'X')//only the capital X is inside
            assertTrue  SearchNestedHash.search(crazyMap,['xxsxx':'v'])
 
     }
}

here is the Class:

class SearchNestedHash {
    static def found = false;
 
    static synchronized search(collection,targetObj){
        found = false;
        return searchRec(collection,targetObj)
    }
 
    static synchronized searchRec(collection, targetObj){
 
        if(collection instanceof List && collection.contains(targetObj))
            return true
 
        collection.each({
 
                if(
                    (
                        collection instanceof Map
                        &&
                        (
                            it.key == targetObj ||
                            it.value == targetObj ||
                            it.key    instanceof List && it.key    .contains(targetObj) ||
                            it.value instanceof List && it.value .contains(targetObj)
                        )
                    )
                        ||
                    (
                        it instanceof List && it == targetObj
                    )
 
                ){found = true;return}
            })
 
        if(!found){
 
            collection.each({
 
                    if(it.key instanceof Map)
                    searchRec(it.key,targetObj)
 
                    if(it.value instanceof Map)
                    searchRec(it.value,targetObj)
 
                    if(it instanceof List)
                    searchRec(it,targetObj)
 
                })
 
        }
        return found
    }
 
}

Categories: Uncategorized, algorithms.

Tags: , , , , , , , , , ,

nice chat with india :)

[11/19/09 5:56:55 AM] nils: hello this is nils
[11/19/09 5:57:47 AM] Sherly: hi nils
[11/19/09 5:57:56 AM] Sherly: this is sherly
[11/19/09 5:58:02 AM] Sherly: how are you doing?
[11/19/09 5:58:23 AM] nils: hey sherly, this is nils how are you doing?
[11/19/09 5:58:41 AM] nils: can i help you?
[11/19/09 5:59:39 AM] Sherly: doing good
[11/19/09 6:00:07 AM] Sherly: i am business manager in a leading software company
[11/19/09 6:00:18 AM] nils: ok
[11/19/09 6:00:25 AM] nils: and i am a programmer
[11/19/09 6:01:11 AM] Sherly: thats great
[11/19/09 6:01:49 AM] nils: can i programm something for you :) ?? in grails and groovy?
[11/19/09 6:01:50 AM] nils: hehe
[11/19/09 6:02:28 AM] Sherly: We are a software company Head quarter in New Jersey, USA with development center located in India. We provide offshore developer(s) working on your instruction in very reasonable prize.
We are a software company located in India. We develop quality software and website in very reasonable prize.
[11/19/09 6:02:54 AM] nils: ah ok i understand
[11/19/09 6:03:22 AM] nils: how did you find me
[11/19/09 6:03:30 AM] Sherly:
We do software development in’ web Design,Photoshop, Corel draw, Flash, Flex, Action script, Silver light,.NET’, PHP’, Java’, Delphi’, FoxPro’, Database Administration’, Website development’, Search engine optimization’, Testing services and more.
[11/19/09 6:04:25 AM] Sherly: i am looking forward software projects
[11/19/09 6:04:47 AM] nils: hehe me too
[11/19/09 6:05:26 AM] Sherly: which company u work for?
[11/19/09 6:05:46 AM] nils: can you answer me this simple captcha? what is 30 + one in numbers?
[11/19/09 6:06:24 AM] Sherly: one moment
[11/19/09 6:06:32 AM] nils: lol
[11/19/09 6:07:12 AM] nils: |-)
[11/19/09 6:07:28 AM] nils: captcha time is up
[11/19/09 6:07:41 AM] nils: you are not authorized to chat anymore
[11/19/09 6:07:50 AM] nils: have a great rest of your day

[11/19/09 5:56:55 AM] nils: hello this is nils

[11/19/09 5:57:47 AM] Sherly: hi nils

[11/19/09 5:57:56 AM] Sherly: this is sherly

[11/19/09 5:58:02 AM] Sherly: how are you doing?

[11/19/09 5:58:23 AM] nils: hey sherly, this is nils how are you doing?

[11/19/09 5:58:41 AM] nils: can i help you?

[11/19/09 5:59:39 AM] Sherly: doing good

[11/19/09 6:00:07 AM] Sherly: i am business manager in a leading software company

[11/19/09 6:00:18 AM] nils: ok

[11/19/09 6:00:25 AM] nils: and i am a programmer

[11/19/09 6:01:11 AM] Sherly: thats great

[11/19/09 6:01:49 AM] nils: can i programm something for you :) ?? in grails and groovy?

[11/19/09 6:01:50 AM] nils: hehe

[11/19/09 6:02:28 AM] Sherly: We are a software company Head quarter in New Jersey, USA with development center located in India. We provide offshore developer(s) working on your instruction in very reasonable prize.

We are a software company located in India. We develop quality software and website in very reasonable prize.

[11/19/09 6:02:54 AM] nils: ah ok i understand

[11/19/09 6:03:22 AM] nils: how did you find me

[11/19/09 6:03:30 AM] Sherly:

We do software development in’ web Design,Photoshop, Corel draw, Flash, Flex, Action script, Silver light,.NET’, PHP’, Java’, Delphi’, FoxPro’, Database Administration’, Website development’, Search engine optimization’, Testing services and more.

[11/19/09 6:04:25 AM] Sherly: i am looking forward software projects

[11/19/09 6:04:47 AM] nils: hehe me too

[11/19/09 6:05:26 AM] Sherly: which company u work for?

[11/19/09 6:05:46 AM] nils: can you answer me this simple captcha? what is 30 + one in numbers?

[11/19/09 6:06:24 AM] Sherly: one moment

[11/19/09 6:06:32 AM] nils: lol

[11/19/09 6:07:12 AM] nils: |-)

[11/19/09 6:07:28 AM] nils: captcha time is up

[11/19/09 6:07:41 AM] nils: you are not authorized to chat anymore

[11/19/09 6:07:50 AM] nils: have a great rest of your day

Categories: Uncategorized.