/* **************************************************************************

  $Archive: /njcl_v2/src/com/novell/service/file/nw/Trustee.java $
  $Revision: 14 $
  $Modtime: 10/02/00 12:49p $
 
  Copyright (c) 1998 Novell, Inc.  All Rights Reserved.

  THIS WORK IS  SUBJECT  TO  U.S.  AND  INTERNATIONAL  COPYRIGHT  LAWS  AND
  TREATIES.   NO  PART  OF  THIS  WORK MAY BE  USED,  PRACTICED,  PERFORMED
  COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED,  ABRIDGED, CONDENSED,
  EXPANDED,  COLLECTED,  COMPILED,  LINKED,  RECAST, TRANSFORMED OR ADAPTED
  WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL, INC. ANY USE OR EXPLOITATION
  OF THIS WORK WITHOUT AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO
  CRIMINAL AND CIVIL LIABILITY.

****************************************************************************/

package com.novell.service.file.nw;

import com.novell.service.rfc1960.SearchStringComponent;
import com.novell.service.rfc1960.Rfc1960Parser;

/** 
 * Provides variables, constructors, and methods for the support of a
 * Trustee. Trustees can be associated with a given directory entry
 * (file or directory). The given directory entry can have multiple
 * trustees, which can be enumerated using the TrusteeEnumerator
 * interface.
 *
 * <p>A Trustee is composed of a name identifying the trustee, the 
 * rights associated with the named trustee, and a compare string. The
 * compare string uses the RFC1960 (LDAP) search string format and is
 * used to allow individual compares on names and rights.
 * 
 * <p>Warning:  This class results in a mutable object. For versatility, 
 * code block synchronization has been implemented in the various jndi
 * search and modify methods of this class's implementation to provide
 * for thread safe operation.
 * 
 * <p>If you have a multi-threaded application and a code segment of that
 * application uses get methods of this class, and there is a potential 
 * of another thread randomly using corresponding set methods of this
 * class, you should enclose the calls to the get methods of this class
 * in a synchronized code block.
 * 
 * <p>If you have a multi-threaded application and a code segment of that
 * application uses set methods of this class, and there is a potential 
 * of another thread randomly using corresponding get methods of this class,
 * you should enclose the calls to the set methods of this class in a 
 * synchronized code block.
 * </p>
 *
 * @see TrusteeEnumerator
 */
 
public class Trustee implements Cloneable
{
/* *************************************************************************
* Constants and data members
***************************************************************************/

   /**
    * Attribute ID of Trustee.
    *
    * <p>(ATTRIBUTE_ID = "Trustee")
    * </p>
    */

   public static final String ATTRIBUTE_ID = "Trustee";

   /**
    * Schema Name of Trustee.
    *
    * <p>(SCHEMA_BINDING_NAME = ATTRIBUTE_ID + " Definition")
    * </p>
    */

   public static final String SCHEMA_BINDING_NAME = 
         ATTRIBUTE_ID + " Definition";

   /**
    * Schema Syntax of Trustee.
    *
    * <p>(COMPARE_SYNTAX_STRING = 
    *       "(" +
    *       SearchStringComponent.EQUALS_STRING + "," +
    *       SearchStringComponent.PRESENT_STRING + "," +
    *       SearchStringComponent.SUBSTRING_STRING + 
    *       ")")
    * </p>
    */

   public static final String COMPARE_SYNTAX_STRING = 
         new String(
               "(" +
               SearchStringComponent.EQUALS_STRING + "," +
               SearchStringComponent.PRESENT_STRING + "," +
               SearchStringComponent.SUBSTRING_STRING + 
               ")");

/* **************************************************************************
   compareString field name defines
****************************************************************************/

   /**
    * Compare field for names.
    *
    * <p>(NAME_FIELD = "Name")
    * </p>
    * @see #equals(Object)
    */

   public static final String NAME_FIELD = new String("Name");

   /**
    * Compare field for rights.
    *
    * <p>(RIGHTS_FIELD = "Rights")
    * </p>
    * @see #equals(Object)
    */

   public static final String RIGHTS_FIELD = new String("Rights");

   /**
    * Read rights.
    *
    * <p>(TA_READ = 0x1)
    * </p>
    * @see #getReadRights()
    * @see #setReadRights(boolean)
    */

   public static final int TA_READ        =  0x1;

   /**
    * Write rights.
    *
    * <p>(TA_WRITE = 0x2)
    * </p>
    * @see #getWriteRights()
    * @see #setWriteRights(boolean)
    */

   public static final int TA_WRITE       =  0x2;

   /**
    * Create rights.
    *
    * <p>(TA_CREATE = 0x8)
    * </p>
    * @see #getCreateRights()
    * @see #setCreateRights(boolean)
    */

   public static final int TA_CREATE      =  0x8;

   /**
    * Delete rights.
    *
    * <p>(TA_DELETE = 0x10)
    * </p>
    * @see #getDeleteRights()
    * @see #setDeleteRights(boolean)
    */

   public static final int TA_DELETE      = 0x10;

   /**
    * Ownership rights.
    *
    * <p>(TA_OWNERSHIP = 0x20)
    * </p>
    * @see #getOwnershipRights()
    * @see #setOwnershipRights(boolean)
    */

   public static final int TA_OWNERSHIP   = 0x20;

   /**
    * Search rights.
    *
    * <p>(TA_SEARCH = 0x40)
    * </p>
    * @see com.novell.service.file.nw.Trustee#getSearchRights()
    * @see com.novell.service.file.nw.Trustee#setSearchRights(boolean)
    */

   public static final int TA_SEARCH      = 0x40;

   /**
    * Modify rights.
    *
    * <p>(TA_MODIFY = 0x80)
    * </p>
    * @see #getModifyRights()
    * @see #setModifyRights(boolean)
    */

   public static final int TA_MODIFY      = 0x80;

   /**
    * Supervisor rights.
    * <p>(TA_SUPERVISOR = 0x0100)
    * </p>
    */
   public static final int TA_SUPERVISOR = 0x0100;

   private String name;
   private int objectRights;
   private String compareString;
   private Rfc1960Parser ssParser = null;
   private boolean firstParser = true;

/* *************************************************************************
* constructors
***************************************************************************/

   /**
    * Creates the default constructor with all data members set
    * to NULL or zero.
    */
    
   public Trustee()
   {
      this(null, 0);
   }

   /**
    * Creates a constructor with the name known and all 
    * other data members set to NULL or zero.
    *
    * @param name The trustee name. 
    */
    
   public Trustee(String name)
   {
      this(name, 0);
   }

   /**
    * Creates a constructor with the rights known and all 
    * other data members set to NULL or zero.
    *
    * @param rights The trustee rights. 
    */
    
   public Trustee(int objectRights)
   {
      this(null, objectRights);
   }

   /**
    * Creates a constructor with all data members based on
    * the input parameters.
    *
    * @param name         The value of the name data member.
    * @param objectRights The value of the objectRights data member.
    *
    * @exception IllegalArgumentException if an illegal arguement
    *            is passed in.
    */
    
   public Trustee(
      String name, 
      int objectRights)
   {
      this.name = name;
      this.objectRights = objectRights;
   }

/* *************************************************************************
* public accessors
***************************************************************************/

   /**
    * Returns this object's name field.
    * 
    * @return The current name.
    */
    
   public String getName()
   {
      return name;
   }

   /**
    * Sets the name field of the attribute.
    *
    * @param name The name to be assigned the attribute.
    */
    
   public void setName(String name)
   {
      this.name = name;
   }

   /**
    * Returns the value of the objectRights data member.
    *
    * @return The current objectRights mask value.
    */
    
   public int getRights()
   {
      return objectRights;
   }

   /**
    * Sets the objectRights data member of this object to the
    * specified value.
    *
    * @param value The value to be set in the ojbectRights mask.
    */
    
   public void setRights(int value)
   {
      objectRights = value;
   }

/* *************************************************************************
* public compareString support methods
***************************************************************************/

   /**
    * Sets the compare string value to allow individual compares on the
    * name and rights fields. The compare string uses the RFC1960 (LDAP)
    * search string format. 
    *
    * <p>The names of these fields takes the names of their corresponding
    * methods minus the get or set prefix. Given the following string:
    *
    * <p>"(&(Name=*printer*)(ObjectId>=2)(Rights>=128))"
    *
    * The equals() method will return TRUE if the Name contains the
    * substring printer, the ObjectId  is not 2, and Rights is 
    * >= 128.
    * 
    * If the approximate operator type is used on the Rights field the 
    * various bits of the operand value are checked and if any of them are
    * set, equals will return TRUE. For example: "(Rights~=3) returns TRUE
    * if either the TA_READ or TA_WRITE bits are set, regardless of what 
    * other bits might be set.
    * 
    * <p>If the string passed in is not a valid RFC1960 formatted string, an
    * IllegalArgumentException will be thrown. If the compareString 
    * value is NULL, RFC1960 formatted compares will be disabled.</p>
    *
    * @param compareString The RFC1960 formatted search string. NULL 
    *                      disables this compare functionality.
    *
    * @exception IllegalArgumentException if the string passed in is
    *            not a valid RFC1960 formatted string.
    *
    * @see #equals
    * @see #NAME_FIELD
    * @see #RIGHTS_FIELD
    */
    
   public void setCompareString(String compareString)
   {
      firstParser = true;
      if (compareString != null)
      {
         this.compareString = new String(compareString);
         ssParser = new Rfc1960Parser(compareString);
      }else
      {
         this.compareString = null;
         ssParser = null;
      }
   }

   /**
    * Returns the current value of compareString.
    * 
    * @return The current value of compareString.
    *
    * @see #setCompareString
    */
    
   public String getCompareString()
   {
      return compareString;
   }


   /**
    * Returns the RFC1960 search string parser for the compare string.
    * 
    * <p>Returns a Rfc1960Parser object that was instantiated with the
    * compareString last set by the setCompareString method.</p>
    *
    * @return The Rfc1960Parser for the current search String.
    *         Returns NULL if the search string has not been set.
    */
    
   public Rfc1960Parser getSearchStringParser()
   {
      if (firstParser)
      {
         firstParser = false;
         return ssParser;
      }
      else
      {
         if (compareString == null)
            return null;
         return new Rfc1960Parser(compareString);
      }
   }

/* *************************************************************************
* public Rights Mask helper methods
***************************************************************************/

   /**
    * Sets the Read bit of the objectRights mask based on the 
    * boolean input value.
    *
    * @param value A boolean set to TRUE if the Read bit is to be set;
    *              otherwise set to FALSE if the bit is to be cleared.
    *
    * @see #getRights
    * @see #setRights
    */
    
   public void setReadRights(boolean value)
   {
      if (value)
         objectRights |= TA_READ;
      else
         objectRights &= ~TA_READ;
   }

   /**
    * Returns a boolean value reflecting the state of the object rights
    * mask Read bit.
    * 
    * <p>Returns the state of the corresponding 
    * bit in the rights mask. TRUE is returned if the corresponding bit
    * is set, otherwise FALSE is returned.</p>
    *
    * @return A boolean set to TRUE if the rights mask bit is set;
    * otherwise FALSE is returned.
    *
    * @see #getRights
    * @see #setRights
    */
    
   public boolean getReadRights()
   {
      if ((objectRights & TA_READ) != 0)
         return true;
      return false;
   }

   /**
    * Sets the Write bit of the object rights mask based on the 
    * boolean input value.
    *
    * @param value A boolean set to TRUE if bit is to be set, otherwise
    *              set to FALSE if the bit is to be cleared.
    *
    * @see #getRights
    * @see #setRights
    */
    
   public void setWriteRights(boolean value)
   {
      if (value)
         objectRights |= TA_WRITE;
      else
         objectRights &= ~TA_WRITE;
   }

   /**
    * Returns a boolean value reflecting the state of the Write bit
    * of the object rights mask.
    *
    * @return A boolean set to TRUE if the rights mask bit is set;
    * otherwise FALSE is returned.
    *
    * @see #getRights
    * @see #setRights
    */
    
   public boolean getWriteRights()
   {
      if ((objectRights & TA_WRITE) != 0)
         return true;
      return false;
   }

   /**
    * Sets the Create bit of the object rights mask based on the 
    * boolean input value.
    *
    * @param value A boolean set to TRUE if bit is to be set; otherwise
    *              set to FALSE if the bit is to be cleared.
    *
    * @see #getRights
    * @see #setRights
    */
    
   public void setCreateRights(boolean value)
   {
      if (value)
         objectRights |= TA_CREATE;
      else
         objectRights &= ~TA_CREATE;
   }

   /**
    * Returns a boolean value reflecting the state of the Create bit of
    * the object rights mask.
    * 
    * @return A boolean set to TRUE if the corresponding rights mask bit
    *         is set; otherwise FALSE is returned.
    *
    * @see #getRights
    * @see #setRights
    */
    
   public boolean getCreateRights()
   {
      if ((objectRights & TA_CREATE) != 0)
         return true;
      return false;
   }

   /**
    * Sets the Delete bit of the object rights mask based on the 
    * boolean input value.
    *
    * @param value  A boolean set to TRUE if bit is to be set; otherwise
    *               FALSE is returned if the bit is to be cleared.
    *
    * @see #getRights
    * @see #setRights
    */
    
   public void setDeleteRights(boolean value)
   {
      if (value)
         objectRights |= TA_DELETE;
      else
         objectRights &= ~TA_DELETE;
   }

   /**
    * Returns the state of the Delete bit of the object rights mask as
    * a boolean value.
    *
    * @return A boolean value set to TRUE if the rights mask bit
    *         is set; otherwise FALSE is returned.
    *
    * @see #getRights
    * @see #setRights
    */
    
   public boolean getDeleteRights()
   {
      if ((objectRights & TA_DELETE) != 0)
         return true;
      return false;
   }

   /**
    * Sets the Ownership bit of the object rights mask based on the 
    * boolean input value.
    *
    * @param value A boolean value set to TRUE if bit is to be set;
    *              otherwise set to FALSE if the bit is to be cleared.
    *
    * @see #getRights
    * @see #setRights
    */
    
   public void setOwnershipRights(boolean value)
   {
      if (value)
         objectRights |= TA_OWNERSHIP;
      else
         objectRights &= ~TA_OWNERSHIP;
   }

   /**
    * Returns a boolean value reflecting the state of the Ownership
    * bit of the object rights mask.
    *
    * @return A boolean set to TRUE if the ownership rights mask bit
    *         is set; otherwise FALSE is returned.
    *
    * @see com.novell.service.file.nw.Trustee#getRights
    * @see com.novell.service.file.nw.Trustee#setRights
    */
    
   public boolean getOwnershipRights()
   {
      if ((objectRights & TA_OWNERSHIP) != 0)
         return true;
      return false;
   }

   /**
    * Sets the Search bit of the object rights mask based on the 
    * boolean input value.
    *
    * @param value A boolean value set to TRUE if bit is to be set;
    *              otherwise set to FALSE if the bit is to be cleared.
    *
    * @see #getRights
    * @see #setRights
    */
    
   public void setSearchRights(boolean value)
   {
      if (value)
         objectRights |= TA_SEARCH;
      else
         objectRights &= ~TA_SEARCH;
   }

   /**
    * Returns a boolean value reflecting the state of the Search bit
    * of the object rights mask.
    * 
    * @return A boolean set to TRUE if the rights mask bit is set;
    *         otherwise FALSE is returned.
    *
    * @see #getRights
    * @see #setRights
    */
    
   public boolean getSearchRights()
   {
      if ((objectRights & TA_SEARCH) != 0)
         return true;
      return false;
   }

   /**
    * Sets the Modify bit of the object rights mask based on the 
    * boolean input value.
    * 
    * @param value A boolean set to TRUE if bit is to be set;
    *              otherwise set to FALSE if the bit is to be cleared.
    *
    * @see #getRights
    * @see #setRights
    */
    
   public void setModifyRights(boolean value)
   {
      if (value)
         objectRights |= TA_MODIFY;
      else
         objectRights &= ~TA_MODIFY;
   }

   /**
    * Returns a boolean value reflecting the state of the Modify bit
    * of the object rights mask.
    *
    * @return A boolean set to TRUE if the rights mask bit is set; 
    * otherwise FALSE is returned.
    *
    * @see #getRights
    * @see #setRights
    */
    
   public boolean getModifyRights()
   {
      if ((objectRights & TA_MODIFY) != 0)
         return true;
      return false;
   }

   /**
    * Sets the Supervisor bit of the object rights mask based on the 
    * boolean input value.
    * 
    * @param value A boolean set to TRUE if bit is to be set;
    *              otherwise set to FALSE if the bit is to be cleared.
    *
    * @see #getRights
    * @see #setRights
    */
    
   public void setSupervisorRights(boolean value)
   {
      if (value)
         objectRights |= TA_SUPERVISOR;
      else
         objectRights &= ~TA_SUPERVISOR;
   }

   /**
    * Returns a boolean value reflecting the state of the Supervisor bit
    * of the object rights mask.
    *
    * @return A boolean set to TRUE if the rights mask bit is set; 
    * otherwise FALSE is returned.
    *
    * @see #getRights
    * @see #setRights
    */
    
   public boolean getSupervisorRights()
   {
      if ((objectRights & TA_SUPERVISOR) != 0)
         return true;
      return false;
   }

/* *************************************************************************
* overridden Object methods
***************************************************************************/

   /**
    * Compares the input object against this object for equality.
    * 
    * <p>If the input object has a RFC1960 compare string, the 
    * comparision will be based on the commands in the compare string.
    * If the operationType is illegal for the data type being compared,
    * an IllegalArmumentException will be thrown.</p>
    *
    * @param obj The object to compare.
    *
    * @return  A boolean set to TRUE if equals; otherwise FALSE
    *          is returned.
    *
    * @exception IllegalArgumentException If the operationType is
    *            illegal for the data type being compared.
    *
    * @see #setCompareString
    * @see #NAME_FIELD
    * @see #RIGHTS_FIELD
    */
    
   public boolean equals(Object obj)
   {
      if(obj == null || !(obj instanceof Trustee))
         return false;
      Trustee trustee = (Trustee)obj;

      Rfc1960Parser ssp = trustee.getSearchStringParser();
      if (ssp == null)
      {
         // do a simple compare

         if (obj == this)
            return true;

         if (!SearchStringComponent.compareStringsEqual(trustee.getName(), name))
            return false;

         if (trustee.getRights() != objectRights)
            return false;

         return true;      
      }

      while (ssp.hasMoreElements())
      {
         SearchStringComponent ssc = ssp.next();
         String name = ssc.getAttributeId();
         boolean compared = false;

         if (name.equals(NAME_FIELD))
         {
            compared =ssc.compareString(
                             ssc.getOperationType(), 
                             ssc.operandReplacement() ?
                                trustee.getName() :
                                ssc.getOperand(), 
                             this.name,
                             true);
         }
         else
         {
            if (name.equals(RIGHTS_FIELD))
               compared = compareRights(ssc, trustee);
            else
               compared = false;
         }
         ssp.setCompareResult(ssc, compared);
      }
      return ssp.compared();
   }   

   /**
    * Returns a String representation of this object. The string
    * is returned in the following format: "name: n, objectRights: r,
    * compareString: s".</p>
    *
    * @return A String representation of this object.
    */
    
   public String toString()
   {
      return new String(
                     "name: " + name +  
                     ", objectRights: " + objectRights +
                     ", compareString: " + compareString);
   }

   /**
    * Instantiates a new object of this type. The new object has all
    * new references but with the same values within those references.
    *
    * @return A new Trustee object with the same values.
    */
    
   public Object clone()
   {
      Trustee obj = null;
      try
      {
         obj = (Trustee) super.clone();
      }
      catch (CloneNotSupportedException e)
      {
         // this shouldn't happen, since we are Cloneable
         throw (new InternalError());
      }

      obj.name = name;
      obj.objectRights = objectRights;
      obj.compareString = compareString;
      obj.ssParser = ssParser;
      obj.firstParser = firstParser;
      obj.setCompareString(compareString);
      return obj;
   }


/* *************************************************************************
* private methods
***************************************************************************/

   private boolean compareRights(SearchStringComponent ssc, Trustee trustee)
   {
      int operandValue;
      int againstValue = getRights();
      int type = ssc.getOperationType();

      if (ssc.operandReplacement())
         operandValue = trustee.getRights();
      else
         operandValue = Integer.parseInt(ssc.getOperand());


      if (type == ssc.APPROXIMATE)
      {
         int value = operandValue & againstValue;
         if (value != 0)    
            return true;
         else
            return false;
      }

      return ssc.compareInt(type, operandValue, againstValue);
   }
}
