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

  $Archive: /njcl_v2rmi/src/com/novell/service/file/nw/VolumeUtilization.java $
  $Revision: 15 $
  $Modtime: 1/12/01 4:11p $
 
  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 the attribute value for volume utilization.
 * 
 * <p>This attribute value is unique from all other attribute values
 * in that it requires the objectName be set by the user before it
 * can obtain the values. Because of this feature the attribute value
 * can not be setup for searching (only the default constructor is
 * available). The returned jndi attribute value will actually be an
 * object that will obtain all values when the setName() or setObjectId()
 * methods are called. Calling any methods, other than Objectname(),
 * previous to this time will cause an IllegalStateException to be thrown.
 * </p>
 * <p>This class results in a mutable object. For versitility, 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>
 * <p>If a code segment of a multi-threaded 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>
 * <p>If a code segment of a multi-threaded 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>
 */
 
public class VolumeUtilization implements Cloneable, java.io.Serializable
{
/* **************************************************************************
   jndi naming interface defines
****************************************************************************/

   /**
    * Attribute ID of VolumeUtilization.
    *
    * <p>(ATTRIBUTE_ID = "Volume Utilizations")
    * </p>
    */

   public static final String ATTRIBUTE_ID = "Volume Utilization";

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

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

   /**
    * Schema Syntax of VolumeUtilization.
    *
    * <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
****************************************************************************/

   /**
    * The UsedDirectories compare field.
    *
    * <p>(USEDDIRECTORIES_FIELD = "UsedDirectories")
    * </p>
    * @see #setCompareString
    * @see #equals
    */

   public static final String USEDDIRECTORIES_FIELD = 
      new String("UsedDirectories");

   /**
    * The UsedFiles compare field.
    *
    * <p>(USEDFILES_FIELD = "UsedFiles")
    * </p>
    * @see #setCompareString
    * @see #equals
    */

   public static final String USEDFILES_FIELD = new String("UsedFiles");

   /**
    * The UsedBlocks compare field.
    *
    * <p>(USEDBLOCKS_FIELD = "UsedBlocks")
    * </p>
    * @see #setCompareString
    * @see #equals
    */

   public static final String USEDBLOCKS_FIELD = new String("UsedBlocks");

   /**
    * The Name compare field.
    *
    * <p>(NAME_FIELD = "Name")
    * </p>
    * @see #setCompareString
    * @see #equals
    */

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


/* **************************************************************************
   VolumeRestriction state data members
****************************************************************************/

   private int usedDirectories;
   private int usedFiles;
   private int usedBlocks;
   private String name;
   protected boolean objectIdSet = false;

   private String compareString;
   private Rfc1960Parser ssParser = null;
   private boolean firstParser = true;

/* **************************************************************************
* Constructors
****************************************************************************/

   /**
    * Constructs a default VolumeUtilization object with no parameters.
    */

   public VolumeUtilization()
   {
   }


   /**
    * Constructs a VolumeUtilization object with only a name parameter.
    */
   public VolumeUtilization(String Name)
   {
      this.name = name;
   }

   // this constructor is for clone only
   private VolumeUtilization(       
            int usedDirectories,
            int usedFiles,
            int usedBlocks,
            String name,
            boolean objectIdSet)
   {
      this.usedDirectories = usedDirectories;
      this.usedFiles = usedFiles;
      this.usedBlocks = usedBlocks;
      this.name = name;
      this.objectIdSet = objectIdSet;
   }

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

   /**
    * Returns the usedDirectories field of this Object.
    *
    * <p>The setName() method must be called first before this method
    * can be called, otherwise an IllegalStateException will be thrown.
    * </p>
    *
    * @return The current value of the usedDirectories field.
    *
    * @exception IllegalStateException if setName() is not called first.
    *
    * @see #setName
    */

   public int getUsedDirectories()
   {
      if (!objectIdSet)
         throw new IllegalStateException();
      return(usedDirectories);
   }

   /**
    * Sets the usedDirectories field of this Object.
    *
    * <p>The setName() or setObjectId() methods must be called first
    * before this method can be called, otherwise an IllegalStateException
    * will be thrown.</p>
    *
    * @param value The new value of the usedDirectories field.
    *
    * @exception IllegalStateException if setName() or setObjectId()
    *            is not called first.
    *
    * @see #setName
    */

   public void setUsedDirectories(int value)
   {
      if (!objectIdSet)
         throw new IllegalStateException();
      usedDirectories = value;
   }

   /**
    * Returns the usedFiles field of this Object.
    *
    * <p>The setName() method must be called first before this
    * method can be called, otherwise an IllegalStateException
    * will be thrown.</p>
    *
    * @return The current value of the usedFiles field.
    *
    * @exception IllegalStateException if setName() is not called first.
    *
    * @see #setName
    */

   public int getUsedFiles()
   {
      if (!objectIdSet)
         throw new IllegalStateException();
      return(usedFiles);
   }

   /**
    * Sets the usedFiles field of this Object.
    *
    * <p>The setName() or setObjectId() methods must be called first
    * before this method can be called, otherwise an IllegalStateException
    * will be thrown.</p>
    *
    * @param value The new value of the usedFiles field.
    *
    * @exception IllegalStateException if setName() or setObjectId() is
    *            not called first.
    *
    * @see #setName
    */

   public void setUsedFiles(int value)
   {
      if (!objectIdSet)
         throw new IllegalStateException();
      usedFiles = value;
   }

   /**
    * Returns the usedBlocks field of this Object.
    *
    * <p>The setName() method must be called first before this method
    * can be called, otherwise an IllegalStateException will be thrown.</p>
    *
    * @return The current value of the usedBlocks field.
    *
    * @exception IllegalStateException if setName() is not called first.
    *
    * @see #setName
    */

   public int getUsedBlocks()
   {
      if (!objectIdSet)
         throw new IllegalStateException();
      return(usedBlocks);
   }

   /**
    * Sets the usedBlocks field of this Object.
    *
    * <p>The setName() or setObjectId() methods must be called first
    * before this method can be called, otherwise an IllegalStateException
    * will be thrown.</p>
    *
    * @param value The new value of the usedBlocks field.
    *
    * @exception IllegalStateException if setName() or setObjectId() is
    *            not called first.
    *
    * @see #setName
    */

   public void setUsedBlocks(int value)
   {
      if (!objectIdSet)
         throw new IllegalStateException();
      usedBlocks = value;
   }

   /**
    * Returns the name field of this Object.
    *
    * @return The current value of the name field.
    */

   public String getName()
   {
      return(name);
   }

   /**
    * Sets the name field of this Object.
    *
    * <p>When the user sets the object name the implementation can then
    * calculate the objectId and obtain the state information. After
    * this call is made, all other methods will then return their
    * corresponding data, based on the last update, caused by calling
    * this method. Each time this method is called (with the same, or
    * a different name) the state is updated.</p>
    *
    * @param value The new value of the name field.
    */

   public void setName(String value)
   {
      name = value;
   }

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

   /**
    * Sets the compare string value for the compare fields.
    * 
    * <p>The compare string uses the RFC1960 (LDAP) search string format, 
    * and is used to allow individual compares on the usedDirectories,
    * usedFiles, usedBlocks, and name fields. If the search string passed
    * in is not a valid RFC1960 formatted string, this method will throw
    * an IllegalArgumentException. If the compareString value is NULL,
    * RFC1960 formatted compares will be disabled.
    *
    * <p>The names of these fields takes the names of their 
    * corresponding methods minus the get or set prefix. Given the
    * following  string:
    *
    * <p><i>   "(&(Name=*printer*)(ObjectId>=2)(Rights>=128))"</i>
    *
    * <p>The equals method will return TRUE if the name contains the
    * substring printer, the objectId is not 2, and the rights are set
    * to be able to modify (TA_MODIFY). 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>
    *
    * @param compareString The RFC1960 formatted search string. NULL 
    *                      disables this compare functionality.
    *
    * @exception IllegalArgumentException If the search string
    *            passed in is not a valid RFC1960 formatted string.
    *
    * @see #equals
    * @see #USEDDIRECTORIES_FIELD
    * @see #USEDFILES_FIELD
    * @see #USEDBLOCKS_FIELD
    * @see #NAME_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 the 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>A Rfc1960Parser object that was instantiated with the compareString
    * last set by the setCompareString method is returned.</p>
    *
    * @return  The Rfc1960Parser object for the current compare String.
    *          NULL is returned if the compare string has not been set.
    */
    
   public Rfc1960Parser getSearchStringParser()
   {
      if (firstParser)
      {
         firstParser = false;
         return ssParser;
      }else
      {
         if (compareString == null)
            return null;
         return new Rfc1960Parser(compareString);
      }
   }

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

   /**
    * Compares the input object against this object for equality.
    * 
    * <p>If the input object has a RFC1960 compare string, this method
    * will do the comparision 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 the objects compared are equal;
    *          otherwise FALSE is returned.  
    *
    * @exception IllegalArgumentException if the operationType is
    *            illegal for the data type being compared.
    *
    * @see #setCompareString
    * @see #USEDDIRECTORIES_FIELD
    * @see #USEDFILES_FIELD
    * @see #USEDBLOCKS_FIELD
    * @see #NAME_FIELD
    */
    
   public boolean equals(Object obj)
   {
      if (!(obj instanceof VolumeUtilization))
         return false;

      VolumeUtilization vu = (VolumeUtilization)obj;

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

         if (obj == this)
            return true;

         if (vu.getUsedDirectories() != usedDirectories)
               return false;

         if (vu.getUsedFiles() != usedFiles)
               return false;

         if (vu.getUsedBlocks() != usedBlocks)
               return false;

         if (!vu.getName().equalsIgnoreCase(name))
            return false;

         if (!SearchStringComponent.compareStringsEqual(
                                    compareString, 
                                    vu.getCompareString()))
            return false;

         return true;      
      }

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

         if (name.equals(USEDDIRECTORIES_FIELD))
         {
            compared =ssc.compareInt(
                              ssc.getOperationType(), 
                              ssc.operandReplacement() ?
                                 vu.getUsedDirectories() :
                                 Integer.parseInt(ssc.getOperand()),
                              usedDirectories);
         }else
         {
            if (name.equals(USEDFILES_FIELD))
            {
               compared = ssc.compareInt(
                                 ssc.getOperationType(), 
                                 ssc.operandReplacement() ?
                                    vu.getUsedFiles() :
                                    Integer.parseInt(ssc.getOperand()),
                                 usedFiles);
            }else
            {
               if (name.equals(USEDBLOCKS_FIELD))
               {
                  compared = ssc.compareInt(
                                    ssc.getOperationType(), 
                                    ssc.operandReplacement() ?
                                       vu.getUsedBlocks() :
                                       Integer.parseInt(ssc.getOperand()),
                                    usedBlocks);
               }else
               {
                  if (name.equals(NAME_FIELD))
                  {
                     compared = ssc.compareString(
                                       ssc.getOperationType(), 
                                       ssc.operandReplacement() ?
                                          vu.getName() :
                                          ssc.getOperand(),
                                       this.name,
                                       true);
                  }else
                     compared = false;
         }}}
         ssp.setCompareResult(ssc, compared);
      }
      return ssp.compared();
   }   

   /**
    * Returns a String representation of the VolumeUtilization object
    * in the format: "name: s, usedDirectories: n, usedFiles: n,
    * usedBlocks: n".
    *
    * @return A String representation of this object.
    */
    
   public String toString()
   {
      return new String(
                     "name: " + name +
                     "usedDirectories: " + usedDirectories +  
                     ", usedFiles: " + usedFiles +
                     ", usedBlocks: " + usedBlocks);
   }

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

      obj.usedDirectories = usedDirectories;
      obj.usedFiles = usedFiles;
      obj.usedBlocks = usedBlocks;
      obj.name = name;
      obj.objectIdSet = objectIdSet;
      obj.compareString = compareString;
      obj.ssParser = ssParser;
      obj.firstParser = firstParser;
      obj.setCompareString(compareString);
      return obj;
   }

   /**
    * Copies the CallsServiceRemote from object into this object.
    */
   public void copy(VolumeUtilization from)
   {
      this.usedDirectories = from.usedDirectories;
      this.usedFiles = from.usedFiles;
      this.usedBlocks = from.usedBlocks;
      this.name = from.name;
      this.objectIdSet = from.objectIdSet;
      this.compareString = from.compareString;
      this.ssParser = from.ssParser;
      this.firstParser = from.firstParser;
      this.setCompareString(from.compareString);
   }
}
