/*
 * JBoss, the OpenSource J2EE webOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */

package org.jboss.test.security.test;

import java.security.Permission;
import java.security.PermissionCollection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

/** The PermissionCollection object for NamespacePermissions.

@author Scott.Stark@jboss.org
@version $Revision: 1.5 $
*/
public class NamespacePermissionCollection extends PermissionCollection
{
    private TreeMap namespacePerms = new TreeMap();
    private TreeMap namespaceKeys = new TreeMap(new PermissionName.NameLengthComparator());

    /** Creates new NamespacePermission */
    public NamespacePermissionCollection()
    {
    }

    public void add(Permission permission)
    {
        if( this.isReadOnly() )
            throw new SecurityException("Cannot add permission to read-only collection");
        if( (permission instanceof NamespacePermission) == false )
            throw new IllegalArgumentException("Only NamespacePermission can be added, invalid="+permission);
        NamespacePermission np = (NamespacePermission) permission;
        PermissionName key = np.getFullName();
        ArrayList tmp = (ArrayList) namespacePerms.get(key);
        if( tmp == null )
        {
            tmp = new ArrayList();
            namespacePerms.put(key, tmp);
            namespaceKeys.put(key, key);
        }
        tmp.add(np);
    }

    /** Locate the closest permissions assigned to the namespace. This is based
     *on the viewing the permission name as a heirarchical PermissionName and
     */
    public boolean implies(Permission permission)
    {
        boolean implies = false;
        if( namespacePerms.isEmpty() == true )
            return false;

        NamespacePermission np = (NamespacePermission) permission;
        // See if there is an exact permission for the name
        PermissionName key = np.getFullName();
        ArrayList tmp = (ArrayList) namespacePerms.get(key);
        if( tmp == null )
        {   // Find the closest parent position.
            SortedMap headMap = namespacePerms.headMap(key);
            try
            {
                PermissionName lastKey = (PermissionName) headMap.lastKey();
                if( lastKey.isParent(key) == true )
                    tmp = (ArrayList) namespacePerms.get(lastKey);
                else
                {
                    PermissionName[] keys = {};
                    keys = (PermissionName[]) headMap.keySet().toArray(keys);
                    for(int k = keys.length-1; k >= 0; k --)
                    {
                        lastKey = keys[k];
                        if( lastKey.isParent(key) == true )
                        {
                            tmp = (ArrayList) namespacePerms.get(lastKey);
                            break;
                        }
                    }
                }
            }
            catch(NoSuchElementException e)
            {   /* Assign the first permission
                Object firstKey = namespacePerms.firstKey();
                tmp = (ArrayList) namespacePerms.get(firstKey);
		*/
            }
        }

        // See if the permission is implied by any we found
        if( tmp != null )
            implies = isImplied(tmp, np);
//log.debug("NPC["+this+"].implies("+np+") -> "+implies);
        return implies;
    }

    public Enumeration elements()
    {
        Set s = namespaceKeys.keySet();
        final Iterator iter = s.iterator();
        Enumeration elements = new Enumeration()
        {
            ArrayList activeEntry;
            int index;
            public boolean hasMoreElements()
            {
                boolean hasMoreElements = true;
                if( activeEntry == null || index >= activeEntry.size() )
                {
                    hasMoreElements = iter.hasNext();
                    activeEntry = null;
                }
                return hasMoreElements;
            }
            public Object nextElement()
            {
                Object next = null;
                if( activeEntry == null )
                {
                    Object key = iter.next();
                    activeEntry = (ArrayList) namespacePerms.get(key);
                    index = 0;
                    next = activeEntry.get(index ++);
                }
                else
                {
                    next = activeEntry.get(index ++);
                }
                return next;
            }
        };
        return elements;
    }


    private boolean isImplied(ArrayList permissions, NamespacePermission np)
    {
        boolean isImplied = false;
        for(int p = 0; p < permissions.size(); p ++)
        {
            Permission perm = (Permission) permissions.get(p);
            isImplied |= perm.implies(np);
            if( isImplied == true )
                break;
        }
        return isImplied;
    }
}
