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

  $Archive: /njcl_v2/src/com/novell/service/file/nw/DirectorySpaceInformation.java $
  $Revision: 12 $
  $Modtime: 5/11/00 10:27a $
 
  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 support for NetWare file system directory attribute values
 * and operations.
 *
 * <p>For the get and set methods that deal with blocks of volume
 * data, the blocksize of a Volume now defaults to 64K when the
 * Volume is created; however, this blocksize may be changed at create
 * time because it is now a variable value. Previously the blocksize
 * was set to be 4K, and was not a variable value.
 * 
 * <p>This class results in a mutable object. For versitility, code
 * block synchronization has been implemented in the various JNDI
 * search and modify methods to provide for thread safe operation.
 * 
 * <p>For multi-threaded applications, if 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>Also, 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 DirectorySpaceInformation implements Cloneable
{
/* **************************************************************************
   jndi naming interface defines
****************************************************************************/

   /**
    * Attribute ID of DirectorySpaceInformation.
    *
    * <p>(ATTRIBUTE_ID = "Directory Space Information")
    * </p>
    */

   public static final String ATTRIBUTE_ID = "Directory Space Information";

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

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

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

   /**
    * TotalBlocks compare field.
    *
    * <p>(TOTALBLOCKS_FIELD = "TotalBlocks")
    * </p>
    * @see #setCompareString
    * @see #equals
    */

   public static final String TOTALBLOCKS_FIELD = new String("TotalBlocks");

   /**
    * AvailableBlocks compare field.
    *
    * <p>(AVAILABLEBLOCKS_FIELD = "AvailableBlocks")
    * </p>
    * @see #setCompareString
    * @see #equals
    */

   public static final String AVAILABLEBLOCKS_FIELD = 
      new String("AvailableBlocks");

   /**
    * PurgeableBlocks compare field.
    *
    * <p>(PURGEABLEBLOCKS_FIELD = "PurgeableBlocks")
    * </p>
    * @see #setCompareString
    * @see #equals
    */

   public static final String PURGEABLEBLOCKS_FIELD = 
      new String("PurgeableBlocks");

   /**
    * NotYetPurgeableBlocks compare field.
    *
    * <p>(NOTYETPURGEABLEBLOCKS_FIELD = "NotYetPurgeableBlocks")
    * </p>
    * @see #setCompareString
    * @see #equals
    */

   public static final String NOTYETPURGEABLEBLOCKS_FIELD = 
      new String("NotYetPurgeableBlocks");

   /**
    * TotalDirEntries compare field.
    *
    * <p>(TOTALDIRENTRIES_FIELD = "TotalDirEntries")
    * </p>
    * @see #setCompareString
    * @see #equals
    */

   public static final String TOTALDIRENTRIES_FIELD = 
      new String("TotalDirEntries");

   /**
    * AvailableDirEntries compare field.
    *
    * <p>(AVAILABLEDIRENTRIES_FIELD = "AvailableDirEntries")
    * </p>
    * @see #setCompareString
    * @see #equals
    */

   public static final String AVAILABLEDIRENTRIES_FIELD = 
      new String("AvailableDirEntries");

   /**
    * Reserved compare field.
    *
    * <p>(RESERVED_FIELD = "Reserved")
    * </p>
    * @see #setCompareString
    * @see #equals
    */

   public static final String RESERVED_FIELD = new String("Reserved");

   /**
    * SectorsPerBlock compare field.
    *
    * <p>(SECTORSPERBLOCK_FIELD = "SectorsPerBlock")
    * </p>
    * @see #setCompareString
    * @see #equals
    */

   public static final String SECTORSPERBLOCK_FIELD = 
      new String("SectorsPerBlock");

   /**
    * VolName compare field.
    *
    * <p>(VOLNAME_FIELD = "VolName")
    * </p>
    * @see #setCompareString
    * @see #equals
    */

   public static final String VOLNAME_FIELD = new String("VolName");

   /**
    * MaxAvailable compare field.
    *
    * <p>(MAXAVAILABLE_FIELD = "MaxAvailable")
    * </p>
    * @see #setCompareString
    * @see #equals
    */

   public static final String MAXAVAILABLE_FIELD = 
      new String("MaxAvailable");

   /**
    * CurrentUse compare field.
    *
    * <p>(CURRENTUSE_FIELD = "CurrentUse")
    * </p>
    * @see #setCompareString
    * @see #equals
    */

   public static final String CURRENTUSE_FIELD = new String("CurrentUse");


/* **************************************************************************
   DirectorySpaceInformation state data members
****************************************************************************/

   // native DIR_SPACE_INFO structure fields
   private int totalBlocks;
   private int availableBlocks;
   private int purgeableBlocks;         // not returned with dirHandle
   private int notYetPurgeableBlocks;   // not returned with dirHandle
   private int totalDirEntries;
   private int availableDirEntries;
   private int reserved;
   private int sectorsPerBlock;
   private String volName;

   // see NWGetDirSpaceLimitList
   private int maxAvailable;  // number of 4096 blocks
   private int currentUse;    // number of 4096 blocks

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

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

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

   public DirectorySpaceInformation()
   {
   }
       
   /**
    * Constructs a DirectorySpaceInformation object with all possible
    * state parameters and initializes the object to the values of
    * these parameters.
    *
    * @param totalBlocks           The totalBlocks data members value.
    * @param availableBlocks       The availableBlocks data members value.
    * @param purgeableBlocks       The purgeableBlocks data members value.
    * @param notYetPurgeableBlocks The notYetPurgeableBlocks data members
    *                              value.
    * @param totalDirEntries       The totalDirEntries data members value.
    * @param availableDirEntries   The availableDirEntries data members value.
    * @param reserved              The reserved data members value.
    * @param sectorsPerBlock       The sectorsPerBlock data members value.
    * @param maxAvailable          The maxAvailable data members value.
    * @param currentUse            The currentUse data members value.
    * @param volName               The volName data members value.
    */

   public DirectorySpaceInformation(
      int totalBlocks,
      int availableBlocks,
      int purgeableBlocks,
      int notYetPurgeableBlocks,
      int totalDirEntries,
      int availableDirEntries,
      int reserved,
      int sectorsPerBlock,
      int maxAvailable,
      int currentUse,
      String volName)
   {
      this.totalBlocks = totalBlocks;
      this.availableBlocks = availableBlocks;
      this.purgeableBlocks = purgeableBlocks;
      this.notYetPurgeableBlocks = notYetPurgeableBlocks;
      this.totalDirEntries = totalDirEntries;
      this.availableDirEntries = availableDirEntries;
      this.reserved = reserved;
      this.sectorsPerBlock = sectorsPerBlock;
      this.volName = volName;
      this.maxAvailable = maxAvailable;
      this.currentUse = currentUse;
   }
       
/* **************************************************************************
   Public Accessor methods
****************************************************************************/

   /**
    * Returns the totalBlocks field of this object.
    *
    * @return The current totalBlocks value in blocksize.
    *
    * @see #TOTALBLOCKS_FIELD
    */

   public int getTotalBlocks()
   {
      return(totalBlocks);
   }

   /**
    * Sets the totalBlocks field of this object.
    *
    * @param value The new totalBlocks value in blocksize.
    *
    * @see #TOTALBLOCKS_FIELD
    */

   public void setTotalBlocks(int value)
   {
      totalBlocks = value;
   }

   /**
    * Returns the availableBlocks field of this object.
    *
    * <p>The availableBlocks value is either the total available blocks
    * on the Volume or the currently available amount in the directory
    * if directory restrictions are in effect.
    *
    * @returns The current availableBlocks value in blocksize.
    *
    * @see #AVAILABLEBLOCKS_FIELD
    */

   public int getAvailableBlocks()
   {
      return(availableBlocks);
   }

   /**
    * Sets the availableBlocks field of this object.
    *
    * @param value The new availableBlocks value in blocksize.
    *
    * @see #getAvailableBlocks
    * @see #AVAILABLEBLOCKS_FIELD
    */

   public void setAvailableBlocks(int value)
   {
      availableBlocks = value;
   }

   /**
    * Returns the purgeableBlocks field of this object.
    *
    * @return The current purgeableBlocks value in blocksize.
    *
    * @see #PURGEABLEBLOCKS_FIELD
    */

   public int getPurgeableBlocks()
   {
      return(purgeableBlocks);
   }

   /**
    * Sets the purgeableBlocks field of this object.
    *
    * @param value The new purgeableBlocks value in blocksize.
    *
    * @see #PURGEABLEBLOCKS_FIELD
    */

   public void setPurgeableBlocks(int value)
   {
      purgeableBlocks = value;
   }

   /**
    * Returns the notYetPurgeableBlocks field of this object.
    *
    * @return The current notYetPurgeableBlocks value in blocksize.
    *
    * @see #NOTYETPURGEABLEBLOCKS_FIELD
    */

   public int getNotYetPurgeableBlocks()
   {
      return(notYetPurgeableBlocks);
   }

   /**
    * Sets the notYetPurgeableBlocks field of this object.
    *
    * @param value The new notYetPurgeableBlocks value in blocksize.
    *
    * @see #NOTYETPURGEABLEBLOCKS_FIELD
    */

   public void setNotYetPurgeableBlocks(int value)
   {
      notYetPurgeableBlocks = value;
   }

   /**
    * Gets the totalDirEntries field of this object.
    *
    * @return The current totalDirEntries value.
    *
    * @see #TOTALDIRENTRIES_FIELD
    */

   public int getTotalDirEntries()
   {
      return(totalDirEntries);
   }

   /**
    * Sets the totalDirEntries field of this object.
    *
    * @param value The new totalDirEntries value.
    *
    * @see #TOTALDIRENTRIES_FIELD
    */

   public void setTotalDirEntries(int value)
   {
      totalDirEntries = value;
   }

   /**
    * Returns the availableDirEntries field of this object.
    *
    * @return The current availableDirEntries value.
    *
    * @see #AVAILABLEDIRENTRIES_FIELD
    */

   public int getAvailableDirEntries()
   {
      return(availableDirEntries);
   }

   /**
    * Sets the availableDirEntries field of this object.
    *
    * @param value The new availableDirEntries value.
    *
    * @see #AVAILABLEDIRENTRIES_FIELD
    */

   public void setAvailableDirEntries(int value)
   {
      availableDirEntries = value;
   }

   /**
    * Returns the reserved field of this object.
    *
    * @return The current reserved value.
    *
    * @see #RESERVED_FIELD
    */

   public int getReserved()
   {
      return(reserved);
   }

   /**
    * Sets the reserved field of this object.
    *
    * @param value The new reserved value.
    *
    * @see #RESERVED_FIELD
    */

   public void setReserved(int value)
   {
      reserved = value;
   }

   /**
    * Returns the sectorsPerBlock field of this object.
    *
    * @return The current sectorsPerBlock value.
    *
    * @see #SECTORSPERBLOCK_FIELD
    */

   public int getSectorsPerBlock()
   {
      return(sectorsPerBlock);
   }

   /**
    * Sets the sectorsPerBlock field of this object.
    *
    * @param value The new sectorsPerBlock value.
    *
    * @see #SECTORSPERBLOCK_FIELD
    */

   public void setSectorsPerBlock(int value)
   {
      sectorsPerBlock = value;
   }

   /**
    * Returns the volNam field of this object.
    *
    * @return The current volName value.
    *
    * @see #VOLNAME_FIELD
    */

   public String getVolName()
   {
      return(volName);
   }

   /**
    * Sets the volName field of this object.
    *
    * @param value The new volName value.
    *
    * @see #VOLNAME_FIELD
    */

   public void setVolName(String value)
   {
      volName = value;
   }

   /**
    * Returns the maxAvailable field of this object.
    *
    * <p>The maxAvailable value is set to 0x7FFFFFFF if no directory
    * restrictions are in place. If restrictions are in place, then
    * this is the amount of restriction in 4K pieces. This value is
    * not the same as availableBlocks; it represents the maximum
    * amount of space assigned to a directory and not the amount of 
    * space currently available in the directory.
    *
    * @return The current maxAvailable value in 4K pieces.
    *
    * @see #MAXAVAILABLE_FIELD
    */

   public int getMaxAvailable()
   {
      return(maxAvailable);
   }

   /**
    * Sets the maxAvailable field of this object.
    *
    * @param value The new maxAvailable value in 4K pieces.
    *
    * @see #MAXAVAILABLE_FIELD
    */

   public void setMaxAvailable(int value)
   {
      maxAvailable = value;
   }

   /**
    * Returns the currentUse field of this object.
    *
    * @return The current currentUse value in 4K pieces.
    *
    * @see #CURRENTUSE_FIELD
    */

   public int getCurrentUse()
   {
      return(currentUse);
   }

   /**
    * Sets the currentUse field of this object.
    *
    * @param value The new currentUse value in 4K pieces.
    *
    * @see #CURRENTUSE_FIELD
    */

   public void setCurrentUse(int value)
   {
      currentUse = 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 compare
    * fields.
    *
    * <p>If the string passed in is not a valid RFC1960 formated string,
    * this method will throw an IllegalArgumentException. If the compareString 
    * value is null, RFC1960 formated compares will be disabled.
    *
    * <p>The names of these fields have taken 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 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 formated search string. NULL 
    *                      disables this compare functionality.
    *
    * @exception IllegalArgumentException if the string passed in is not
    *            a valid RFC1960 formatted string.
    *
    * @see #equals
    * @see #TOTALBLOCKS_FIELD
    * @see #AVAILABLEBLOCKS_FIELD
    * @see #PURGEABLEBLOCKS_FIELD
    * @see #NOTYETPURGEABLEBLOCKS_FIELD
    * @see #TOTALDIRENTRIES_FIELD
    * @see #AVAILABLEDIRENTRIES_FIELD
    * @see #RESERVED_FIELD
    * @see #SECTORSPERBLOCK_FIELD
    * @see #VOLNAME_FIELD
    * @see #MAXAVAILABLE_FIELD
    * @see #CURRENTUSE_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 Trustee#setCompareString
    */
    
   public String getCompareString()
   {
      return compareString;
   }


   /**
    * Returns the RFC1960 search string parser for the compare string.
    * 
    * <p>The Rfc1960Parser object is returned that was instantiated with
    * the compareString last set by the setCompareString method.</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);
      }
   }

/* **************************************************************************
* overriden 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 two objects are equal,
    *            otherwise FALSE is returned. 
    *
    * @exception IllegalArgumentException if the operationType is illegal
    *            for the data type being compared.
    *
    * @see #setCompareString
    * @see #TOTALBLOCKS_FIELD
    * @see #AVAILABLEBLOCKS_FIELD
    * @see #PURGEABLEBLOCKS_FIELD
    * @see #NOTYETPURGEABLEBLOCKS_FIELD
    * @see #TOTALDIRENTRIES_FIELD
    * @see #AVAILABLEDIRENTRIES_FIELD
    * @see #RESERVED_FIELD
    * @see #SECTORSPERBLOCK_FIELD
    * @see #VOLNAME_FIELD
    * @see #MAXAVAILABLE_FIELD
    * @see #CURRENTUSE_FIELD
    */
    
   public boolean equals(Object obj)
   {
      if (!(obj instanceof DirectorySpaceInformation))
         return false;

      DirectorySpaceInformation dsi = (DirectorySpaceInformation)obj;

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

         if (obj == this)
            return true;

         if (dsi.getTotalBlocks() != totalBlocks)
            return false;

         if (dsi.getAvailableBlocks() != availableBlocks)
            return false;

         if (dsi.getPurgeableBlocks() != purgeableBlocks)
            return false;

         if (dsi.getNotYetPurgeableBlocks() != notYetPurgeableBlocks)
            return false;

         if (dsi.getTotalDirEntries() != totalDirEntries)
            return false;

         if (dsi.getAvailableDirEntries() != availableDirEntries)
            return false;

         if (dsi.getReserved() != reserved)
            return false;

         if (dsi.getSectorsPerBlock() != sectorsPerBlock)
            return false;

         if (!dsi.getVolName().equalsIgnoreCase(volName))
            return false;

         if (dsi.getMaxAvailable() != maxAvailable)
            return false;

         if (dsi.getCurrentUse() != currentUse)
            return false;

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

         return true;      
      }

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

         if (name.equals(TOTALBLOCKS_FIELD))
         {
            compared =ssc.compareInt(
                              ssc.getOperationType(), 
                              ssc.operandReplacement() ?
                                 dsi.getTotalBlocks() :
                                 Integer.parseInt(ssc.getOperand()),
                              totalBlocks);
         }else
         {
         if (name.equals(AVAILABLEBLOCKS_FIELD))
         {
            compared = ssc.compareInt(
                              ssc.getOperationType(), 
                              ssc.operandReplacement() ?
                                 dsi.getAvailableBlocks() :
                                 Integer.parseInt(ssc.getOperand()),
                              availableBlocks);
         }else
         {
         if (name.equals(PURGEABLEBLOCKS_FIELD))
         {
            compared = ssc.compareInt(
                              ssc.getOperationType(), 
                              ssc.operandReplacement() ?
                                 dsi.getPurgeableBlocks() :
                                 Integer.parseInt(ssc.getOperand()),
                              purgeableBlocks);
         }else
         {
         if (name.equals(NOTYETPURGEABLEBLOCKS_FIELD))
         {
            compared = ssc.compareInt(
                              ssc.getOperationType(), 
                              ssc.operandReplacement() ?
                                 dsi.getNotYetPurgeableBlocks() :
                                 Integer.parseInt(ssc.getOperand()),
                              notYetPurgeableBlocks);
         }else
         {
         if (name.equals(TOTALDIRENTRIES_FIELD))
         {
            compared = ssc.compareInt(
                              ssc.getOperationType(), 
                              ssc.operandReplacement() ?
                                 dsi.getTotalDirEntries() :
                                 Integer.parseInt(ssc.getOperand()),
                              totalDirEntries);
         }else
         {
         if (name.equals(AVAILABLEDIRENTRIES_FIELD))
         {
            compared = ssc.compareInt(
                              ssc.getOperationType(), 
                              ssc.operandReplacement() ?
                                 dsi.getAvailableDirEntries() :
                                 Integer.parseInt(ssc.getOperand()),
                              availableDirEntries);
         }else
         {
         if (name.equals(RESERVED_FIELD))
         {
            compared = ssc.compareInt(
                              ssc.getOperationType(), 
                              ssc.operandReplacement() ?
                                 dsi.getReserved() :
                                 Integer.parseInt(ssc.getOperand()),
                              reserved);
         }else
         {
         if (name.equals(SECTORSPERBLOCK_FIELD))
         {
            compared = ssc.compareInt(
                              ssc.getOperationType(), 
                              ssc.operandReplacement() ?
                                 dsi.getSectorsPerBlock() :
                                 Integer.parseInt(ssc.getOperand()),
                              sectorsPerBlock);
         }else
         {
         if (name.equals(VOLNAME_FIELD))
         {
            compared = ssc.compareString(
                              ssc.getOperationType(), 
                              ssc.operandReplacement() ?
                                 dsi.getVolName() :
                                 ssc.getOperand(),
                              volName,
                              true);
         }else
         {
         if (name.equals(MAXAVAILABLE_FIELD))
         {
            compared = ssc.compareInt(
                              ssc.getOperationType(), 
                              ssc.operandReplacement() ?
                                 dsi.getMaxAvailable() :
                                 Integer.parseInt(ssc.getOperand()),
                              maxAvailable);
         }else
         {
         if (name.equals(CURRENTUSE_FIELD))
         {
            compared = ssc.compareInt(
                              ssc.getOperationType(), 
                              ssc.operandReplacement() ?
                                 dsi.getCurrentUse() :
                                 Integer.parseInt(ssc.getOperand()),
                              currentUse);
         }else
            compared = false;

         }}}}}}}}}}
         ssp.setCompareResult(ssc, compared);
      }
      return ssp.compared();
   }   

   /**
    * Returns a String representation of this object.
    * 
    * <p>The string returned is in the following format:
    * "totalBlocks: n, availableBlocks: n, purgeableBlocks: n, 
    * notYetPurgeableBlocks: n, totalDirEntries: n, availableDirEntries: n,
    * reserved: n, sectorsPerBlock: n, maxAvailable: n, currentUse n, 
    * volName: s, compareString: s".
    * </p>
    *
    * @return The String representation of this object.
    */
    
   public String toString()
   {
      return new String(
                     "totalBlocks: " + totalBlocks +
                     ", availableBlocks: " + availableBlocks +
                     ", purgeableBlocks: " + purgeableBlocks +
                     ", notYetPurgeableBlocks: " + notYetPurgeableBlocks +
                     ", totalDirEntries: " + totalDirEntries +
                     ", availableDirEntries: " + availableDirEntries +
                     ", reserved: " + reserved +
                     ", sectorsPerBlock: " + sectorsPerBlock +
                     ", maxAvailable: " + maxAvailable +
                     ", currentUse: " + currentUse +
                     ", volName: " + volName +
                     ", compareString: " + compareString);
   }

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

      obj.totalBlocks = totalBlocks;
      obj.availableBlocks = availableBlocks;
      obj.purgeableBlocks = purgeableBlocks;
      obj.notYetPurgeableBlocks = notYetPurgeableBlocks;
      obj.totalDirEntries = totalDirEntries;
      obj.availableDirEntries = availableDirEntries;
      obj.reserved = reserved;
      obj.sectorsPerBlock = sectorsPerBlock;
      obj.volName = volName;
      obj.maxAvailable = maxAvailable;
      obj.currentUse = currentUse;
      obj.compareString = compareString;
      obj.ssParser = ssParser;
      obj.firstParser = firstParser;
      obj.setCompareString(compareString);
      return obj;
   }
}
