/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2004 Bull S.A.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * Initial developer: Florent BENOIT
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.deployment.web;

import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.security.jacc.WebResourcePermission;
import javax.security.jacc.WebUserDataPermission;


/**
 * Defines a PatternEntry object for JACC URLPattern handle
 * @author Florent Benoit
 */
public class PatternEntry {

    /**
     * Pattern of this PatternEntry
     */
    private Pattern pattern = null;


    /**
     * This pattern will be unchecked (default pattern added to all others)
     */
    private boolean uncheckedLastEntry = false;

    /**
     * Methods for this pattern
     */
    private MethodsDesc methods = null;

    /**
     * Is this pattern irrelevant while adding qualified pattern
     */
    private boolean irrelevant = false;


    /**
     * Qualifying pattern
     */
    private StringBuffer qualified = null;

    /**
     * Constructor
     * @param pattern used by this PatternEntry object
     */
    public PatternEntry(String pattern) {
        this.pattern = new Pattern(pattern);
        methods = new MethodsDesc();
        qualified = new StringBuffer(pattern);
    }


    /**
     * Add Http methods (Excluded or Unchecked)
     * @param methods array of methods to add
     * @param transportGuarantee Transport Guarantee for these methods
     * @param isExcluded if true add methods as excluded else as unchecked
     */
    public void addMethods(String[] methods, String transportGuarantee, boolean isExcluded) {
        this.methods.addMethods(methods, transportGuarantee, isExcluded);
    }

    /**
     * Add Excluded Http methods
     * @param methods array of methods to add
     * @param transportGuarantee Transport Guarantee for these methods
     */
    public void addExcludedMethods(String[] methods, String transportGuarantee) {
        addMethods(methods, transportGuarantee, true);
    }

    /**
     * Add Unchecked Http methods
     * @param methods array of methods to add
     * @param transportGuarantee Transport Guarantee for these methods
     */
    public void addUncheckedMethods(String[] methods, String transportGuarantee) {
        addMethods(methods, transportGuarantee, false);
    }


    /**
     * Add pattern information for given roles
     * @param methods methods to add to the given role
     * @param roles roles which have the given methods
     * @param transportGuarantee Transport Guarantee for these methods
     */
    public void addMethodsOnRoles(String[] methods, String[] roles, String transportGuarantee) {
        for (int r = 0; r < roles.length; r++) {
            addMethodsOnRole(methods, roles[r], transportGuarantee);
        }
    }


    /**
     * Add pattern information for a given role
     * @param methods methods to add to the given role
     * @param role role which have the given methods
     * @param transportGuarantee Transport Guarantee for these methods
     */
    public void addMethodsOnRole(String[] methods, String role, String transportGuarantee) {
        this.methods.addMethodsOnRole(methods, role, transportGuarantee);
    }

    /**
     * Set the flag which indicate that this entry will be added at the end
     * as unchecked permission
     */
    public void setUncheckedLastEntry() {
        uncheckedLastEntry = true;
    }

    /**
     * Gets the boolean value of the flag which indicate that this entry
     * will be added at the end as unchecked permission
     * @return true if this is the pattern to add at the end
     */
    public boolean isUncheckedLastEntry() {
        return uncheckedLastEntry;
    }




    /**
     * Add to this pattern a qualified pattern
     * @see jacc 3.1.3.1 (Qualifying pattern)
     * @param otherPattern pattern to add for the qualified pattern
     */
    public void addQualifiedPattern(Pattern otherPattern) {
        /*
         * Any pattern, qualified by a pattern that matches it, is overriden
         * and made irrelevant (in the translation) by the qualifying pattern
         */
        if (otherPattern.isMatching(pattern)) {
            irrelevant = true;
        } else {
            qualified.append(":");
            qualified.append(otherPattern);
        }

    }

    /**
     * Gets the permissions for each role.
     * Map between role and permissions
     * @return the Map witth key = role, value = permissions
     */
    public Map getRolesPermissionsMap() {
        Map roleMapActions = methods.getRoleMapActions();
        String roleName = null;
        String actions = null;
        Map rolesPermissionsMap = new HashMap();

        // Need to build WebResource for each role Actions
        for (Iterator it = roleMapActions.keySet().iterator(); it.hasNext();) {
            roleName = (String) it.next();
            actions = (String) roleMapActions.get(roleName);
            if (actions != null) {
                PermissionCollection pc = new Permissions();
                pc.add(new WebResourcePermission(getQualifiedPattern(), actions));
                rolesPermissionsMap.put(roleName, pc);
            }
        }
        return rolesPermissionsMap;
    }


    /**
     * Gets the excluded permissions for this pattern
     * @return the excluded permissions for this pattern
     */
    public PermissionCollection getExcludedPermissions() {
        // Need to build WebResource and WebuserData on these actions
        PermissionCollection pc = new Permissions();
        String actions = methods.getExcludedActions();
        if (!actions.equals("")) {
            pc.add(new WebResourcePermission(getQualifiedPattern(), actions));
            pc.add(new WebUserDataPermission(getQualifiedPattern(), actions));
        }
        return pc;
    }


    /**
     * Gets the unchecked permissions for this pattern
     * @return the unchecked permissions for this pattern
     */
    public PermissionCollection getUncheckedPermissions() {
        // First add unchecked permissions and then the WebUserData
        // permissions of no excluding auth-constraint
        String actions = null;
        List permissions = new ArrayList();

        actions = methods.getUncheckedActions();
        if (actions == null || (!actions.equals(""))) {
            permissions.add(new WebResourcePermission(getQualifiedPattern(), actions));
            permissions.add(new WebUserDataPermission(getQualifiedPattern(), actions));
        }

        // WebUserData (no excluding auth-constraint)
        List actionsList = methods.getUncheckedWebUserDataActionsRoleList();
        for (Iterator it = actionsList.iterator(); it.hasNext();) {
            actions = (String) it.next();
            permissions.add(new WebUserDataPermission(getQualifiedPattern(), actions));
        }

        // Try to merge UserDataPermissions with same transport guarantee
        PermissionCollection pc = new Permissions();
        for (Iterator it = permissions.iterator(); it.hasNext();) {
            Permission p = (Permission) it.next();
            if (p instanceof WebUserDataPermission) {
                WebUserDataPermission wdp = (WebUserDataPermission) p;
                // get actions of this permission
                String wdpName = wdp.getName();
                String wdpActions = wdp.getActions();
                if (wdpActions == null) {
                    // If the permissions got all actions (null = all actions)
                    pc.add(p);
                    continue;
                }
                boolean wasMerged = false;
                // Now, search all permissions with same transport guarantee
                for (Iterator itLoop = permissions.iterator(); itLoop.hasNext();) {
                    Permission loopPerm = (Permission) itLoop.next();
                    if (loopPerm instanceof WebUserDataPermission) {
                        WebUserDataPermission loopWdp = (WebUserDataPermission) loopPerm;
                        // if same permission than our, go on
                        if (loopWdp.equals(wdp)) {
                            continue;
                        }
                        String loopWdpName = loopWdp.getName();
                        String loopWdpActions = loopWdp.getActions();
                        if (loopWdpActions == null) {
                            continue;
                        }
                        boolean wNoTransport = (wdpActions.indexOf(":") == -1);
                        boolean loopNoWTransport = (loopWdpActions.indexOf(":") == -1);

                        // Same name and no transport guarantee
                        if (wdpName.equals(loopWdpName) && wNoTransport && loopNoWTransport) {
                            // merge actions
                            String newActions = wdpActions + "," + loopWdpActions;

                            //Add new actions if it doesn't exists
                            Enumeration existingPermissions = pc.elements();
                            boolean exist = false;
                            Permission permissionToAdd = new WebUserDataPermission(wdpName, newActions);
                            while (existingPermissions.hasMoreElements()) {
                                Permission perm = (Permission) existingPermissions.nextElement();
                                if (perm.equals(permissionToAdd)) {
                                    exist = true;
                                }
                            }
                            if (!exist) {
                                wasMerged = true;
                                pc.add(permissionToAdd);
                            }
                        }
                    }
                }
                // There was no merge for this permission, just add it.
                if (!wasMerged) {
                    pc.add(p);
                }

            } else {
                // Do not merge as it is a WebResourcePermission
                pc.add(p);
            }
        }


        return pc;
    }


    /**
     * Gets the state of the pattern. Irrelevant or not during the qualifying phase
     * @return true if the pattern is irrelevant, false otherwise
     */
    public boolean isIrrelevant() {
        return irrelevant;
    }

    /**
     * Gets the qualified form of the pattern
     * @return qualified pattern
     */
    public String getQualifiedPattern() {
        return qualified.toString();
    }

    /**
     * String representation
     * @return string representation of the pattern
     */
    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("PatternEntry[pattern=");
        sb.append(pattern);
        sb.append(";qualified=");
        sb.append(getQualifiedPattern());
        sb.append(";irrelevant=");
        sb.append(irrelevant);
        sb.append("]");
        return sb.toString();
    }
}
