/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2002 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.dynamo.security;

import java.security.Principal;
import java.security.acl.Permission;
import java.util.*;
import com.silverwrist.dynamo.db.NamespaceCache;
import com.silverwrist.dynamo.db.UserProxyManagement;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.util.*;

class AceObject implements DynamoAce
{
  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private AceOperations m_ops;                   // ACE operations object
  private NamespaceCache m_ns_cache;             // namespace cache object
  private UserProxyManagement m_proxy;           // for generating user and group proxies
  private RegisterNewAce m_reg;                  // how we register a new ACE
  private int m_aceid;                           // ID of this ACE
  private PrincipalID m_pr;                      // principal associated with this object
  private boolean m_negative;                    // negative permissions on this ACL?
  private HashSet m_perms;                       // collection of permissions

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  AceObject(AceOperations ops, NamespaceCache ns_cache, UserProxyManagement proxy, RegisterNewAce reg,
	    Map data)
  {
    m_ops = ops;
    m_ns_cache = ns_cache;
    m_proxy = proxy;
    m_reg = reg;
    m_aceid = ((Integer)(data.get(SRMOperations.HMKEY_ACEID))).intValue();
    m_pr = (PrincipalID)(data.get(SRMOperations.HMKEY_PRINCIPAL));
    m_negative = ((Boolean)(data.get(SRMOperations.HMKEY_NEGATIVE))).booleanValue();
    m_perms = new HashSet();

  } // end constructor

  AceObject(AceOperations ops, NamespaceCache ns_cache, UserProxyManagement proxy, RegisterNewAce reg, int id)
  {
    m_ops = ops;
    m_ns_cache = ns_cache;
    m_proxy = proxy;
    m_reg = reg;
    m_aceid = id;
    m_pr = null;
    m_negative = false;
    m_perms = new HashSet();

  } // end constructor

  protected AceObject(AceObject other, int new_id)
  {
    m_ops = other.m_ops;
    m_ns_cache = other.m_ns_cache;
    m_proxy = other.m_proxy;
    m_reg = other.m_reg;
    m_aceid = new_id;
    m_pr = other.m_pr;
    m_negative = other.m_negative;
    m_perms = (HashSet)(other.m_perms.clone());
    m_reg.registerNewAce(this);

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Internal operations
   *--------------------------------------------------------------------------------
   */

  private final PropertyKey getPKey(Permission perm) throws DatabaseException
  {
    if (perm instanceof PermObject)
      return ((PermObject)perm).getPKey();
    if (perm instanceof DynamoPermission)
    { // convert to a PropertyKey
      DynamoPermission dp = (DynamoPermission)perm;
      return new PropertyKey(m_ns_cache.namespaceNameToId(dp.getNamespace()),dp.getName());

    } // end if

    throw new IllegalArgumentException("expected DynamoPermission as argument");

  } // end getPKey

  /*--------------------------------------------------------------------------------
   * Implementations from interface AclEntry
   *--------------------------------------------------------------------------------
   */

  public synchronized boolean setPrincipal(Principal user)
  {
    try
    { // if principal already set, don't do it
      if (m_pr!=null)
	return false;
      PrincipalID new_prid = new PrincipalID(user);
      m_ops.setPrincipal(m_aceid,new_prid);
      m_pr = new_prid;
      return true;

    } // end try
    catch (DatabaseException e)
    { // can't throw DatabaseException, so throw this instead
      throw new SecurityRuntimeException(e);

    } // end catch
    
  } // end setPrincipal

  public synchronized Principal getPrincipal()
  {
    if (m_pr==null)
      return null;
    if (m_pr.isGroup())
      return m_proxy.getGroupProxy(m_pr.getID());
    else
      return m_proxy.getUserProxy(m_pr.getID());

  } // end getPrincipal

  public synchronized void setNegativePermissions()
  {
    if (m_negative)
      return;

    try
    { // call through to the database
      m_ops.setNegative(m_aceid);
      m_negative = true;

    } // end try
    catch (DatabaseException e)
    { // can't throw DatabaseException, so throw this instead
      throw new SecurityRuntimeException(e);

    } // end catch
    
  } // end setNegativePermissions

  public boolean isNegative()
  {
    return m_negative;

  } // end isNegative

  public synchronized boolean addPermission(Permission permission)
  {
    try
    { // get a PropertyKey for the permission
      PropertyKey pkey = getPKey(permission);
      PermObject pobj = new PermObject(pkey,m_ns_cache);
      if (m_perms.contains(pobj))
	return false;
      m_ops.addPermission(m_aceid,pkey);
      m_perms.add(pobj);
      return true;

    } // end try
    catch (DatabaseException e)
    { // can't throw DatabaseException, so throw this instead
      throw new SecurityRuntimeException(e);

    } // end catch
    
  } // end addPermission

  public synchronized boolean removePermission(Permission permission)
  {
    try
    { // get a PropertyKey for the permission
      PropertyKey pkey = getPKey(permission);
      PermObject pobj = new PermObject(pkey,m_ns_cache);
      if (!(m_perms.contains(pobj)))
	return false;
      m_ops.removePermission(m_aceid,pkey);
      m_perms.remove(pobj);
      return true;

    } // end try
    catch (DatabaseException e)
    { // can't throw DatabaseException, so throw this instead
      throw new SecurityRuntimeException(e);

    } // end catch
    
  } // end removePermission

  public synchronized boolean checkPermission(Permission permission)
  {
    try
    { // get a PropertyKey for the permission
      PermObject pobj = null;
      if (permission instanceof PermObject)
	pobj = (PermObject)permission;
      else
      { // convert to a PermObject
	PropertyKey pkey = getPKey(permission);
	pobj = new PermObject(pkey,m_ns_cache);

      } // end else

      return m_perms.contains(pobj);

    } // end try
    catch (DatabaseException e)
    { // can't throw DatabaseException, so throw this instead
      throw new SecurityRuntimeException(e);

    } // end catch
    
  } // end try

  public synchronized Enumeration permissions()
  {
    ArrayList rc = new ArrayList(m_perms);
    return Collections.enumeration(rc);

  } // end permissions

  public String toString()
  {
    ArrayList tmp = null;
    synchronized (this)
    { // create list of permissions that's "frozen"
      tmp = new ArrayList(m_perms);

    } // end synchronized block

    StringBuffer rc = new StringBuffer("DynamoAce[");
    rc.append(m_aceid).append("]: ");
    Principal p = getPrincipal();
    if (p==null)
      rc.append("(null principal), ");
    else if (p instanceof DynamoUser)
      rc.append("user #").append(((DynamoUser)p).getUID()).append(", ");
    else if (p instanceof DynamoGroup)
      rc.append("group #").append(((DynamoGroup)p).getGID()).append(", ");
    if (m_negative)
      rc.append("negative");
    else
      rc.append("positive");
    Iterator it = tmp.iterator();
    while (it.hasNext())
      rc.append(", ").append(((Permission)(it.next())).toString());
    tmp.clear();
    return rc.toString();

  } // end toString

  public synchronized Object clone()
  {
    try
    { // get PropertyKeys for all our Permissions
      ArrayList tmp = new ArrayList(m_perms.size());
      Iterator it = m_perms.iterator();
      while (it.hasNext())
	tmp.add(((PermObject)(it.next())).getPKey());

      // call down to the database to create the clone
      int new_id = m_ops.duplicateAce(m_pr,m_negative,tmp);
      tmp.clear();

      // and use a constructor to duplicate this object (this ctor also registers the ACE with the cache)
      return new AceObject(this,new_id);

    } // end try
    catch (DatabaseException e)
    { // can't throw DatabaseException, so throw this instead
      throw new SecurityRuntimeException(e);

    } // end catch

  } // end clone

  /*--------------------------------------------------------------------------------
   * Implementations from interface DynamoAce
   *--------------------------------------------------------------------------------
   */

  public int getAceID()
  {
    return m_aceid;

  } // end getAceID

  /*--------------------------------------------------------------------------------
   * External operations
   *--------------------------------------------------------------------------------
   */

  synchronized void loadPermissions() throws DatabaseException
  {
    Collection pkeys = m_ops.loadPermissions(m_aceid);
    Iterator it = pkeys.iterator();
    while (it.hasNext())
    { // cycle through all the PropertyKeys and wrap them in PermObjects
      PropertyKey pk = (PropertyKey)(it.next());
      m_perms.add(new PermObject(pk,m_ns_cache));

    } // end while

    pkeys.clear();

  } // end loadPermissions

} // end class AceObject
