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

  $Archive: /njcl_v2rmi/src/com/novell/service/file/nw/calls/VolumeRestrictionEnumeratorImpl.java $
  $Revision: 21 $
  $Modtime: 1/05/01 2:43p $
 
  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.calls;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.NoSuchElementException;

import java.io.ByteArrayOutputStream;

import java.rmi.RemoteException;
import javax.naming.NamingException;

import javax.naming.directory.Attribute;
import javax.naming.directory.AttributeModificationException;
import javax.naming.directory.InvalidSearchFilterException;

import com.novell.service.jncp.NSIException;

import com.novell.utility.naming.directory.StaticAttributeValue;

import com.novell.service.session.SessionException;
import com.novell.service.session.xplat.CallsService;
import com.novell.service.jncpv2r.net.NetService;

import com.novell.service.file.nw.VolumeRestriction;
import com.novell.service.file.nw.VolumeRestrictionEnumerator;
import com.novell.service.file.nw.NameSpace;

import com.novell.service.file.nw.naming.FSAttribute;
import com.novell.service.file.nw.naming.SchemaAttrDef;
import com.novell.service.file.nw.naming.SchemaSyntaxDef;
import com.novell.service.file.nw.naming.FSEnvironment;

import com.novell.service.rfc1960.SearchStringComponent;

import com.novell.service.session.xplat.CallsServiceRemote.*;
/** @internal
 * VolumeRestriction Enumerator Implementation
 *
 * <p>This class is the NWCalls implementation of the VolumeRestriction
 * Enumerator.  Using the NWCalls interface, this class will supply the
 * hasMoreElements and next (and nextElement) methods, implementing an
 * enumerator of VolumeRestriction for a given node.
 * </p>
 * <p>This class also implements the StaticAttributeValue interface which is
 * an interface used by jndi DSContexts for obtaining, modifing and searching
 * for attribute values.
 * </p>
 * @see com.novell.service.file.nw.VolumeRestrictionEnumerator
 * @see com.novell.service.file.nw.naming.StaticAttributeValue
 * @see com.novell.service.file.nw.naming.DirEntryDirContext
 */

public class VolumeRestrictionEnumeratorImpl
   implements VolumeRestrictionEnumerator, StaticAttributeValue
{
   private FSEnvironment environment;
   private String fullNodeName;
   private String nameSpace;
   private String volumeName;
   private NetService netService;
   private CallsService callsService;
   private boolean sessionIsNDS;

   private boolean userIsSet = false;
   private String user;

   // NWScanVolDiskRestrictions2 parameters
   private int runningIteration = 0;   // initially zero
   private int iterHandle;
   private int entryCount = 0;
   private boolean nextOk = false;

   // native NWVOL_RESTRICTIONS structure fields

   private int numberOfEntries;
   private VolumeRestriction[] vRestrictions = new VolumeRestriction[16];

   private int volumeNumber = -1;

   private boolean realThing = false;
   private boolean startedEnum = false;
   private boolean finished = false;
   private int entryPos = 0;
   private VolumeRestriction nextRestriction = null;

   private VolumeRestrictionEnumeratorImpl attributeValue = null;

/* **************************************************************************
*   Public Constructor
****************************************************************************/

   /**
    * Main constructor
    *
    * <p>Constructs a VolumeRestrictionEnumeratorImpl object that is in one
    * of two states.  If the realThing parameter is false, the state of the
    * object is setup to support the StaticAttributeValue interface.  If the
    * realThing parameter is true, the state of the object is setup to
    * support the VolumeRestriction Enumerator functionality.
    * </p>
    *
    * @param environment         The JNDI DSContext's environment associated
    *                            with this attribute and this static
    *                            attribute value.
    * @param realThing           If true, setup as an enumerator of Volume
    *                            Restriction values.  If false, setup as a
    *                            StaticAttributeValue interface.
    *
    * @exception NSIException    If an error occurs.
    */

   public VolumeRestrictionEnumeratorImpl(
      FSEnvironment environment,
      boolean realThing)
      throws NSIException
   {
      this.environment = environment;
      fullNodeName = environment.getFullNodeName();
      nameSpace = environment.getNameSpace();
      volumeName = environment.getVolumeName();

      try
      {
         this.callsService = environment.getCallsService();
         this.netService = environment.getNetService();
         this.sessionIsNDS = environment.sessionIsNDS();
      } catch (NamingException e)
      {
         throw new NSIException("" + e, e);
      }

      this.realThing = realThing;

      if (!realThing)
         finished = true;
   }

   /**
    * jndi DSContext constructor
    *
    * <p>This constructor is used by the jndi DSContexts to instantiate a
    * StaticAttributeValue interface.
    * </p>
    *
    * @param environment         The JNDI DSContext's environment associated
    *                            with this attribute and this static
    *                            attribute value.
    *
    * @see com.novell.service.file.nw.naming.DirEntryDirContext#DirEntryDirContext
    */

   public VolumeRestrictionEnumeratorImpl(FSEnvironment environment)
      throws NSIException
   {
      this(environment, false);
   }

/* **************************************************************************
*   TrusteeEnumerator implementation
****************************************************************************/

   /**
    * Enumerator hasMoreElements
    *
    * <p>This method is used to determine if a VolumeRestriction is available
    * to be returned in a corrisponding nextElement or next method call.
    * This method must be called before the nextElement (or next) method is
    * called.  However, this method must not be called a second time until
    * the nextElement (or next) method has been called.
    * </p>
    * <p>The StaticAttributeValue interface also declares this method for
    * lazy evaluation of jndi attribute values.  The buildAttribute method
    * will return a FSAttribute which will have a reference to this object.
    * The FSAttribute.getValues method will return an enumerator which will
    * forward the hasMoreElements and nextElement calls back into this
    * object.
    * </p>
    *
    * @return                    true if nextElement or next will return a
    *                            valid value. false if there are no values
    *                            available.
    *
    * @exception                 NSIException if filling object fails
    *
    * @see com.novell.service.file.nw.naming.StaticAttributeValue
    * @see com.novell.service.file.nw.naming.FSAttribute
    * @see #next
    * @see #nextElement
    * @see #buildAttribute
    */

   public boolean hasMoreElements()
   {
      if (finished)
         return (false);

      // return true if userIsSet; by this point finished is false
      if (userIsSet)
         return (true);

      if (nextOk)
         return (true);

      // if entryPos < entryCount, that means that there is a restriction
      //   still available from the last scan
      if (entryPos < entryCount)
      {
         nextRestriction = vRestrictions[entryPos++];

         // the restriction object ids come back swapped
         nextRestriction.setObjectId(ObjectNameImpl.swap32(
               nextRestriction.getObjectId()));

         nextOk = true;

         return (true);
      }
      else
      {
         finished = !scanNextRestrictionGroup();

         if (!finished)
         {
            nextRestriction = vRestrictions[entryPos++];

            // the restriction object ids come back swapped
            nextRestriction.setObjectId(ObjectNameImpl.swap32(
                  nextRestriction.getObjectId()));

            nextOk = true;

            return (true);
         }
      }

      return (false);
   }

   public boolean hasMore()
   {
      return hasMoreElements();
   }

   /**
    * Enumerator nextElement
    *
    * <p>This method is used to return the value obtained in the
    * hasMoreElements call.  This method must be called after the
    * hasMoreElements method is called.
    * </p>
    * <p>The StaticAttributeValue interface also declares this method for
    * lazy evaluation of jndi attribute values.  The buildAttribute method
    * will return a FSAttribute which will have a reference to this object.
    * The FSAttribute.getValues method will return an enumerator which will
    * forward the hasMoreElements and nextElement calls back into this
    * object.
    * </p>
    *
    * @return                    The VolumeRestriction that the last
    *                            call to hasMoreElements obtained.
    *
    * @exception                 NoSuchElementException
    *
    * @see com.novell.service.file.nw.naming.StaticAttributeValue
    * @see com.novell.service.file.nw.naming.FSAttribute
    * @see #hasMoreElements
    * @see #next
    * @see #buildAttribute
    * @see com.novell.service.file.nw.VolumeRestriction
    */

   public Object nextElement()
   {
      return next();
   }

   /**
    * Enumerator next (typed nextElement)
    *
    * <p>This method is used to return the value obtained in the
    * hasMoreElements call.  This method must be called after the
    * hasMoreElements method is called.
    * </p>
    *
    * @return                    The VolumeRestriction that the last call to
    *                            hasMoreElements obtained.
    *
    * @exception                 NoSuchElementException
    * @exception                 NSIException
    *
    * @see #hasMoreElements
    * @see com.novell.service.file.nw.VolumeRestriction
    */

   public VolumeRestriction next()
   {
      // start the scan if not started already
      if (!startedEnum)
         hasMoreElements();

      if (finished)
         throw new NoSuchElementException();

      if (userIsSet)
      {
         finished = true;

         int objID = ObjectNameImpl.nameToObjectId(
               user,
               callsService,
               netService,
               sessionIsNDS);

         nextRestriction = new VolumeRestriction();

         nextRestriction.setName(user);
         nextRestriction.setObjectId(objID);
      }
      else
      {
         if (!nextOk)
            throw new NoSuchElementException();

         /*
         The scanNextRestrictionGroup has set up the array of 16 restrictions
         by calling the NWScanVolDiskRestrictions2 nwcalls method.  This
         method sets up the objectId and the restriction field of the
         VolumeRestriction object.  The NWGetObjDiskRestriction will be
         called here to get the inUse field.
         */

         ObjectName on = ObjectNameImpl.objectIdToObjectName(
               nextRestriction.getObjectId(),
               callsService,
               netService,
               sessionIsNDS);

         nextRestriction.setName(on.getName());
      }

      try
      {
         callsService.getObjDiskRestrictions(
               getVolumeNumber(),
               nextRestriction.getObjectId(),
               nextRestriction);
      }
      catch (SessionException e)
      {
         throw new NSIException(e.getMessage(), 0, e);
      }
      catch (RemoteException e)
      {
         throw new NSIException(e.getMessage(), 0, e);
      }

      VolumeRestriction save = nextRestriction;

      // force a wire hit to get the next restriction
      nextOk = false;
      hasMoreElements();

      return (save);
   }

   public void setUser(String user)
   {
      this.user = user;
      this.userIsSet = true;

      if (realThing)
         this.finished = false;
   }

/* **************************************************************************
   StaticAttributeValue implementation
****************************************************************************/

   /**
    * Builds and returns a lazy evaluation Attribute object
    *
    * <p>This method returns a FSAttribute object which has a reference to
    * a VolumeRestrictionEnumeratorImpl that has been instantiated in the
    * enumerator state (verses the StaticAttributeValue interface state).
    * The FSAttribute extends javax.naming.directory.Attribute and overrides
    * the getValues method.  It's getValues method returns an enumerator that
    * simply forwards the hasMoreElements and nextElement calls back into the
    * VolumeRestrictionEnumeratorImpl reference that the FSAttribute contains.
    * </p>
    *
    * @return                    A FSAttribute object that provides for lazy
    *                            evaluation of all possible Trustee's
    *                            associated with the file this object is
    *                            associated with.
    *
    * @exception NamingException
    *
    * @see com.novell.service.file.nw.naming.FSAttribute
    * @see com.novell.service.file.nw.Trustee
    * @see com.novell.service.file.nw.naming.StaticAttributeValue
    * @see #hasMoreElements
    * @see #nextElement
    * @see com.novell.service.file.nw.naming.DirEntryDirContext#c_getAttributes
    */

   public Attribute buildAttribute()
      throws NamingException
   {
      VolumeRestrictionEnumeratorImpl vrEnum = null;
      try
      {
         vrEnum = new VolumeRestrictionEnumeratorImpl(environment, true);
      }
      catch (NSIException nsie)
      {
         NamingException ne = new NamingException();
         ne.setRootCause(nsie);
         throw ne;
      }

      return new 
         FSAttribute(
            ATTRIBUTE_ID,
            vrEnum,
            new SchemaAttrDef(SchemaAttrDef.VR_ATTRDEF, environment),
            new SchemaSyntaxDef(SchemaAttrDef.VR_ATTRDEF, environment));
   }

   /**
    * Adds an Attribute to the Attribute ID's values.
    *
    * <p>There are some dynamic aspects of the static attribute values.
    * examples of these are in the file system name space; Extended
    * Attributes, Trustee lists, and volume restrictions.  All three of these
    * are dynamic lists of information that will be found under a single
    * attribute ID.  The addAttribute will be used by attribute values that
    * have this dynamic aspect to them.  All others will throw an
    * AttributeModificationException.
    * </p>
    *
    * @param                   The attribute to be added.
    *
    * @exception AttributeModificationException
    *
    * @see com.novell.service.file.nw.naming.StaticAttributeValue
    * @see com.novell.service.file.nw.naming.DirEntryDirContext#c_modifyAttributes
    */

   public void addAttribute(Attribute attr)
      throws NamingException
   {
      Enumeration enum = attr.getAll();
      while (enum.hasMoreElements())
      {
         VolumeRestriction vr = (VolumeRestriction) enum.nextElement();
         synchronized(vr)
         {
            int objectId;
            try
            {
               objectId = ObjectNameImpl.nameToObjectId(
                                             vr.getName(), 
                                             callsService,
                                             netService,
                                             sessionIsNDS);
            }
            catch(NSIException nsie)
            {
               AttributeModificationException ame =
                  new AttributeModificationException(nsie.getMessage());
               ame.setRootCause(nsie);
               throw ame;
            }

            try
            {
               callsService.setObjectVolSpaceLimit(
                              getVolumeNumber(),
                              objectId,
                              vr.getRestriction());

            }
            catch (NSIException nsie)
            {
               AttributeModificationException ame =
                  new AttributeModificationException(nsie.getMessage());
               ame.setRootCause(nsie);
               throw ame;
            }
            catch (SessionException e)
            {
               AttributeModificationException ame =
                  new AttributeModificationException(e.getMessage());
               ame.setRootCause(e);
               throw ame;
            }
            catch (RemoteException e)
            {
               AttributeModificationException ame =
                  new AttributeModificationException(e.getMessage());
               ame.setRootCause(e);
               throw ame;
            }
         }
      }
   }


   /**
    * Deletes an Attribute from the Attribute ID's values.
    *
    * <p>There are some dynamic aspects of the static attribute values.
    * examples of these are in the file system name space; Extended
    * Attributes, Trustee lists, and volume restriction.  All three of these
    * are dynamic lists of information that will be found under a single
    * attribute ID.  The delAttribute will be used by attribute values that
    * have this dynamic aspect to them.  All others will throw an
    * AttributeModificationException.
    * </p>
    *
    * @param                   The attribute to be removed.
    *
    * @exception AttributeModificationException
    *
    * @see com.novell.service.file.nw.naming.StaticAttributeValue
    * @see com.novell.service.file.nw.naming.DirEntryDirContext#c_modifyAttributes
    */

   public void deleteAttribute(Attribute attr)
      throws NamingException
   {
      Enumeration enum = attr.getAll();
      while (enum.hasMoreElements())
      {
         VolumeRestriction vr = (VolumeRestriction) enum.nextElement();
         synchronized(vr)
         {
            int objectId;
            try
            {
               objectId = ObjectNameImpl.nameToObjectId(
                                             vr.getName(), 
                                             callsService,
                                             netService,
                                             sessionIsNDS);
            }
            catch(NSIException nsie)
            {
               AttributeModificationException ame =
                  new AttributeModificationException(nsie.getMessage());
               ame.setRootCause(nsie);
               throw ame;
            }

            try
            {
               callsService.removeObjectDiskRestrictions(
                              getVolumeNumber(),
                              objectId);

            }
            catch (NSIException nsie)
            {
               AttributeModificationException ame =
                  new AttributeModificationException(nsie.getMessage());
               ame.setRootCause(nsie);
               throw ame;
            }
            catch (SessionException e)
            {
               AttributeModificationException ame =
                  new AttributeModificationException(e.getMessage());
               ame.setRootCause(e);
               throw ame;
            }
            catch (RemoteException e)
            {
               AttributeModificationException ame =
                  new AttributeModificationException(e.getMessage());
               ame.setRootCause(e);
               throw ame;
            }
         }
      }
   }

   /**
    * Sets the current state to the Attribute values.
    *
    * <p>If this object is multi-valued (compound Attribute), It is possible
    * for some of the values to be read-only.  Any read-only fields of the
    * attribute value being passed in must match the current state of this
    * object or an AttributeModificationException will be thrown.
    * </p>
    *
    * @exception AttributeModificationException
    *
    * @see com.novell.service.file.nw.naming.StaticAttributeValue
    * @see com.novell.service.file.nw.naming.DirEntryDirContext#c_modifyAttributes
    */

   public void modifyAttribute(Attribute attr)
      throws NamingException
   {
      addAttribute(attr);
   }

   /**
    * Get the Attribute Name.
    *
    * <p>Returns the Attribute name (ATTRIBUTE_ID) of the VolumeRestriction
    * implementation object.
    * </p>
    *
    * @return                    The Attribute name (ATTRIBUTE_ID).
    */

   public String getID()
   {
      return(ATTRIBUTE_ID);
   }

   /**
    * Does the specified compare operation and returns the result
    *
    * <p>This method will compare the value object against the attributes
    * current value, in the manner specified in the SearchStringComponent,
    * and return the result of this compare.
    * </p>
    *
    * @param ssc                 The SearchStringComponent to use for the
    *                            compare.
    *
    * @return                    true if the operation compares true, false
    *                            otherwise.
    *
    * @exception InvalidSearchFilterException
    * @exception NSIException
    *
    * @see com.novell.service.file.nw.naming.StaticAttributeValue
    */

   public boolean compare(SearchStringComponent ssc)
      throws NamingException
   {
      try
      {
         return equals(ssc);
      }
      catch (IllegalArgumentException e)
      {
         InvalidSearchFilterException isfe =
            new InvalidSearchFilterException();

         isfe.setRootCause(e);
         throw isfe;
      }
   }

   /**
    * Does the specified compare operation and returns the result
    *
    * <p>This method will compare the value object against the attributes
    * current value, in the manner specified in the SearchStringComponent,
    * and return the result of this compare.
    * </p>
    *
    * @param ssc                 The SearchStringComponent to use for the
    *                            compare.
    *
    * @return                    true if the operation compares true, false
    *                            otherwise.
    *
    * @exception IllegalArgumentException
    * @exception NSIException
    */

   public boolean equals(SearchStringComponent ssc)
   {
      if (ssc == null)
         return false;

      int type = ssc.getOperationType();

      if (type == ssc.PRESENT)
         return true;      // static attribute values are always present

      if (type != ssc.EQUALS && type != ssc.SUBSTRING)
         return false;

      VolumeRestriction vr = null;
      boolean nameOnly = true;
      if (ssc.operandReplacement())
      {
         vr = (VolumeRestriction)ssc.getReplacementObject();
         nameOnly = false;
         if (vr == null)
            throw new IllegalArgumentException(ssc.getOperation());
      }
      else
      {
         // just match the extended attribute name, ignore the data
         vr = new VolumeRestriction(ssc.getOperand());
      }

      /*
         You need to iterate through all ea's and look for the right
         trustee
      */

      VolumeRestrictionEnumeratorImpl vrei = null;
      vrei = new VolumeRestrictionEnumeratorImpl(environment, true);

      while (vrei.hasMoreElements())
      {
         VolumeRestriction value = vrei.next();

         if (type == ssc.EQUALS)
         {
            synchronized(vr)
            {
               if (nameOnly)
               {
                  if (value.getName().
                        equalsIgnoreCase(vr.getName()))
                     return false;
               }
               try
               {
                  if (value.equals(vr))
                     return true;
               }
               catch (Exception e)
               {
                  return false;
               }
            }
         }
         else
         {
            if (type == ssc.SUBSTRING)
            {
               synchronized(vr)
               {
                  if (SearchStringComponent.compareSubString(
                                             vr.getName(),
                                             value.getName(),
                                             true))
                     return true;
               }
            }
            else
               return false;
         }
      }

      return false;
   }

   /**
    * Returns the static interface object.
    *
    * <p>This method returns the appropriate object for the static interface
    * to this attribute value.
    * </p>
    *
    * @return                    VolumeRestrictionEnumerator
    *
    */
   public Object getStaticInterface()
   {
      VolumeRestrictionEnumeratorImpl vrEnum =
         new VolumeRestrictionEnumeratorImpl(environment, true);
      return vrEnum;
   }

/* **************************************************************************
* Private methods
****************************************************************************/


   private boolean scanNextRestrictionGroup()
   {
      int ccode;
      int volNum;

      startedEnum = true;
      volNum = getVolumeNumber();

      /*
         This native call will update the following members:
         entryCount, vRestrictions
      */

      iterHandle = runningIteration;

      for (int i = 0; i < vRestrictions.length; i++)
         vRestrictions[i] = new VolumeRestriction();

      try
      {
         callsService.scanVolDiskRestrictions2(volNum, this);
      }
      catch (SessionException e)
      {
         throw new NSIException(e.getMessage(), 0, e);
      }
      catch (RemoteException e)
      {
         throw new NSIException(e.getMessage(), 0, e);
      }

      if (entryCount == 0)
      {
         return (false);
      }
      else
      {
         entryPos = 0;
         runningIteration += entryCount;

         return (true);
      }
   }

   private int getVolumeNumber()
      throws NSIException
   {
      if (this.volumeNumber != -1)
         return (this.volumeNumber);

      int[] volNum = new int[1];

      try
      {
         callsService.getVolumeNumber(volumeName, volNum);
      }
      catch (SessionException e)
      {
         throw new NSIException(e.getMessage(), 0, e);
      }
      catch (RemoteException e)
      {
         throw new NSIException(e.getMessage(), 0, e);
      }

      this.volumeNumber = volNum[0];

      return (volNum[0]);
   }

   // Read in from CallsService structure
   public void in(VolumeRestrictionEnumeratorStruct x)
   {
      iterHandle = x.iterHandle;
      entryCount = x.entryCount;
      for (int i = 0; i < 16; i++)
      {
         vRestrictions[i].copySubset(x.vRestrictions[i]);
      }
   }

   // Return iteration handle for CallsService call
   public int out()
   {
      return iterHandle;
   }
}
