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

  $Archive: /njcl_v2/src/com/novell/service/file/nw/calls/TrusteeEnumeratorImpl.java $
  $Revision: 18 $
  $Modtime: 1/28/04 4:21p $
 
  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.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.service.session.SessionException;
import com.novell.service.session.xplat.CallsService;
import com.novell.service.jncpv2.net.NetService;

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

import com.novell.service.file.nw.Trustee;
import com.novell.service.file.nw.TrusteeEnumerator;
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;

/** @internal
 * Trustee Enumerator Implementation
 *
 * <p>This class is the NWCalls implementation of the Trustee Enumerator.
 * Using the NWCalls interface, this class will supply the hasMoreElements
 * and next (and nextElement) methods, implementing an enumerator of Trustee
 * 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.TrusteeEnumerator
 * @see com.novell.service.file.nw.naming.StaticAttributeValue
 * @see com.novell.service.file.nw.naming.DirEntryDirContext
 */

public class TrusteeEnumeratorImpl
   implements TrusteeEnumerator, StaticAttributeValue
{
   private FSEnvironment environment;
   private String fullNodeName;
   private String nameSpace;
   private CallsService callsService;
   private NetService netService;
   private boolean sessionIsNDS;

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

   // native NWET_INFO_EXT structure fields
   private int ownerID;
   private Trustee[] trusteeList = new Trustee[100];  // NWCalls uses 100 trustees
   private int[] objectIds = new int[100];

   private boolean startedEnum = false;

   private boolean finished = false;
   private int entryPos = 0;
   private Trustee nextTrustee = null;
   private int nextObjectId = -1;

   private TrusteeEnumeratorImpl attributeValue = null;

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

   /**
    * Main constructor
    *
    * <p>Constructs a TrusteeEnumeratorImpl 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
    * Trustee 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 Trustee
    *                            values.  If false, setup as a
    *                            StaticAttributeValue interface.
    *
    * @exception NSIException    If error occurs.
    */

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

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

      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 TrusteeEnumeratorImpl(FSEnvironment environment)
      throws NSIException
   {
      this(environment, false);
   }

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

   /**
    * Enumerator hasMoreElements
    *
    * <p>This method is used to determine if a Trustee 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 scanNextTrusteeGroup fails
    *
    * @see com.novell.service.file.nw.naming.StaticAttributeValue
    * @see com.novell.service.file.nw.naming.FSAttribute
    * @see com.novell.service.file.nw.calls.TrusteeEnumeratorImpl#next
    * @see com.novell.service.file.nw.calls.TrusteeEnumeratorImpl#nextElement
    * @see com.novell.service.file.nw.calls.TrusteeEnumeratorImpl#buildAttribute
    */

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

      if (nextOk)
         return (true);

      // first skip all zero obj IDs from the last native scan
      while (entryPos < entryCount && objectIds[entryPos] == 0)
         entryPos++;

      // if entryPos < entryCount, that means that there was a non-zero
      //   obj ID still available from the last scan
      if (entryPos < entryCount)
      {
         nextTrustee = trusteeList[entryPos];
         nextObjectId = objectIds[entryPos];
         
         entryPos++;

         nextOk = true;

         return (true);
      }

      while (entryPos >= entryCount && !finished)
      {
         finished = !scanNextTrusteeGroup();

         // skip all zero obj IDs from the last native scan
         while (entryPos < entryCount)
         {
            if (objectIds[entryPos] == 0)
            {
               entryPos++;
               continue;
            }

            // if entryPos < entryCount, and the obj ID is non-zero, that
            //   means that we've found a valid obj ID
            if (entryPos < entryCount && objectIds[entryPos] != 0)
            {
               nextTrustee = trusteeList[entryPos];
               nextObjectId = objectIds[entryPos];
               
               entryPos++;

               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 Trustee (as an Object) 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 com.novell.service.file.nw.calls.TrusteeEnumeratorImpl#hasMoreElements
    * @see com.novell.service.file.nw.calls.TrusteeEnumeratorImpl#next
    * @see com.novell.service.file.nw.calls.TrusteeEnumeratorImpl#buildAttribute
    * @see com.novell.service.file.nw.Trustee
    */

   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 Trustee that the last call to
    *                            hasMoreElements obtained.
    *
    * @exception                 NoSuchElementException
    * @exception                 NSIException
    *
    * @see com.novell.service.file.nw.calls.TrusteeEnumeratorImpl#hasMoreElements
    * @see com.novell.service.file.nw.Trustee
    */

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

      if (!nextOk || finished)
         throw new NoSuchElementException();

      /*
         The scan puts the objectId in the nextObjectId data member, and the
         objectRightsMask in the Trustee.objectRights data member grab the
         nextObjectId and convert it to name and type before returning
      */

      ObjectName on = null;
      try
      {
         on = ObjectNameImpl.
                  objectIdToObjectName(
                     nextObjectId,
                     callsService,
                     netService,
                     sessionIsNDS);
      } catch (NSIException nsie)
      {
         throw new NSIException(
                        nsie.getMessage() + " " +
                        nextObjectId);
      }

      nextTrustee.setName(on.getName());

      Trustee save = nextTrustee;

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

      return (save);
   }

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

   /**
    * Builds and returns a lazy evaluation Attribute object
    *
    * <p>This method returns a FSAttribute object which has a reference to
    * a TrusteeEnumeratorImpl 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
    * EAEnumeratorImpl 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 com.novell.service.file.nw.calls.TrusteeEnumeratorImpl#hasMoreElements
    * @see com.novell.service.file.nw.calls.TrusteeEnumeratorImpl#nextElement
    * @see com.novell.service.file.nw.naming.DirEntryDirContext#c_getAttributes
    */

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

      return new 
         FSAttribute(
            ATTRIBUTE_ID,
            tEnum,
            new SchemaAttrDef(SchemaAttrDef.TL_ATTRDEF, environment),
            new SchemaSyntaxDef(SchemaAttrDef.TL_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())
      {
         Trustee trustee = (Trustee) enum.nextElement();

         synchronized(trustee)
         {
            int objectId;

            // first see if the trustee name is in the name+type format

            ObjectName on = null;
            String user = trustee.getName();
            if (BinderyName.isNameType(user))
               on = new BinderyName(user, true);
            else
               on = new ObjectName(user);

            try
            {
               objectId = ObjectNameImpl.nameToObjectId(
                                             on, 
                                             callsService,
                                             netService,
                                             sessionIsNDS);
            }catch(NSIException nsie)
            {
               AttributeModificationException ame =
                  new AttributeModificationException(nsie.getMessage());
               ame.setRootCause(nsie);
               throw ame;
            }
            try
            {
               callsService.addTrustee(
                              0,
                              fullNodeName,
                              NameSpace.nameToNumber(nameSpace),
                              objectId,
                              trustee.getRights());

            }
            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;
            }
         }
      }
   }


   /**
    * 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 restrictions.  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())
      {
         Trustee trustee = (Trustee) enum.nextElement();

         ObjectName on = null;
         synchronized(trustee)
         {
            // first see if the trustee name is in the name+type format
            String user = trustee.getName();
            if (BinderyName.isNameType(user))
               on = new BinderyName(user, true);
            else
               on = new ObjectName(user);
         }

         int objectId;
         try
         {
            objectId = ObjectNameImpl.nameToObjectId(
                                          on, 
                                          callsService,
                                          netService,
                                          sessionIsNDS);
         }catch(NSIException nsie)
         {
            AttributeModificationException ame =
               new AttributeModificationException(nsie.getMessage());
            ame.setRootCause(nsie);
            throw ame;
         }

         try
         {
            callsService.deleteTrustee(
                           0,
                           fullNodeName,
                           NameSpace.nameToNumber(nameSpace),
                           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;
         }
      }
   }

   /**
    * 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 AttributeInformation
    * 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;

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

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

      TrusteeEnumeratorImpl tei = null;
      tei = new TrusteeEnumeratorImpl(environment, true);

      while (tei.hasMoreElements())
      {
         Trustee value = tei.next();

         if (type == ssc.EQUALS)
         {
            if (nameOnly)
            {
               synchronized(trustee)
               {
                  if (value.getName().equalsIgnoreCase(trustee.getName()))
                     return true;
               }
            }

            try
            {
               synchronized(trustee)
               {
                  return value.equals(trustee);
               }
            }catch(Exception e)
            {
               return false;
            }
         }else
         {
            synchronized(trustee)
            {
               if (SearchStringComponent.compareSubString(
                                             trustee.getName(),
                                             value.getName(),
                                             true))
                  return true;
            }
         }
      }
      return false;
   }

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

      return tEnum;
   }

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


   private boolean scanNextTrusteeGroup()
   {
      int ccode;

      startedEnum = true;

      /*
         This native call will update the following members:
         iterHandle, entryCount, trusteeList, and objectIds
      */
      for (int i = 0; i < trusteeList.length; i++)
         trusteeList[i] = new Trustee();

      try
      {
         callsService.scanForTrustees(
            0,    // no dir handle
            fullNodeName,
            NameSpace.nameToNumber(nameSpace),
            this);

      }
      catch (NSIException nsie)
      {
         ccode = nsie.getCCode();
         if (ccode == 0x899c)
            return (false);

         throw nsie;
      }
      catch (SessionException e)
      {
         throw new NSIException(e.getMessage(), 0, e);
      }

      entryPos = 0;

      return (true);
   }
}
