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

  $Archive: /njcl_v2rmi/src/com/novell/service/file/nw/calls/TrusteePathEnumeratorImpl.java $
  $Revision: 8 $
  $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.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.jncpv2r.net.NetService;

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

import com.novell.service.file.nw.TrusteePathEnumerator;
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
 * This class is the NWCalls implementation of the Trustee Paths
 * Enumerator. Using the NWCalls interface, this class will supply the
 * hasMoreElements and next (and nextElement) methods, implementing an
 * enumerator of trustee paths for a given volume.
 *
 * <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.TrusteePathEnumerator
 * @see com.novell.service.file.nw.naming.StaticAttributeValue
 */
public class TrusteePathEnumeratorImpl
   implements TrusteePathEnumerator, StaticAttributeValue
{
   private FSEnvironment environment;
   private String volumeName;
   private String nameSpace;
   private CallsService callsService;
   private NetService netService;
   private boolean sessionIsNDS;

   // NWScanForTrustees parameters
   private int objID;
   private int volumeNumber;
   private int iterHandle = -1;

   private boolean finished = false;
   private String nextPath = null;

   // user variables
   private String user;
   private boolean userIsSet = false;

   private String pathForSearch = null;

   private TrusteePathEnumeratorImpl attributeValue = null;

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

   /**
    * Main constructor
    *
    * <p>Constructs a TrusteePathEnumeratorImpl 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 TrusteePathEnumerator 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 TrusteePathEnumeratorImpl(FSEnvironment environment, boolean realThing)
      throws NSIException
   {
      this.environment = environment;
      volumeName = environment.getVolumeName();
      nameSpace = environment.getNameSpace();

      int[] volNum = new int[1];

      try
      {
         this.callsService = environment.getCallsService();
         this.netService = environment.getNetService();
         this.sessionIsNDS = environment.sessionIsNDS();

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

      volumeNumber = volNum[0];

      if (realThing)
         finished = !scanNextTrusteePath();
      else
         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 TrusteePathEnumeratorImpl(FSEnvironment environment)
      throws NSIException
   {
      this(environment, false);
   }

/* **************************************************************************
*   TrusteePathEnumerator implementation
****************************************************************************/

   public String getPath()
   {
      return (this.pathForSearch);
   }

   public void setPath(String path)
   {
      this.pathForSearch = path;
   }

   public String getUser()
   {
      return (user);
   }

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

      this.iterHandle = -1;

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

      this.finished = !scanNextTrusteePath();
   }

   /**
    * Enumerator hasMoreElements
    *
    * <p>This method is used to determine if a trustee path is available
    * to be returned in a corresponding nextElement or next method call.
    * </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 scanNextTrusteePath fails
    */
   public boolean hasMoreElements()
   {
      return (!finished);
   }

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

   /**
    * Enumerator nextElement
    *
    * <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 next Trustee path (as a String)
    *
    * @exception                 NoSuchElementException
    */
   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.TrusteePathEnumeratorImpl#hasMoreElements
    * @see com.novell.service.file.nw.Trustee
    */
   public String next()
   {
      if (finished)
         throw new NoSuchElementException();

      String save = nextPath;

      finished = !scanNextTrusteePath();

      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 TrusteePathEnumeratorImpl 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.TrusteePathEnumeratorImpl#hasMoreElements
    * @see com.novell.service.file.nw.calls.TrusteePathEnumeratorImpl#nextElement
    * @see com.novell.service.file.nw.naming.DirEntryDirContext#c_getAttributes
    */
   public Attribute buildAttribute()
      throws NamingException
   {
      TrusteePathEnumeratorImpl tEnum = null;

      try
      {
         tEnum = new TrusteePathEnumeratorImpl(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
   {
      throw new AttributeModificationException();
   }


   /**
    * 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
   {
      throw new AttributeModificationException();
   }

   /**
    * 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
   {
      throw new AttributeModificationException();
   }

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

      TrusteePathEnumeratorImpl paths = null;
      String path = null;
      boolean nameOnly = true;

      if (ssc.operandReplacement())
      {
         paths = (TrusteePathEnumeratorImpl)
               ssc.getReplacementObject();

         nameOnly = false;

         if (path == null)
            throw new IllegalArgumentException(ssc.getOperation());
      }
      else
      {
         path = ssc.getOperand();
      }

      /*
         You need to iterate through all paths and look for the right one
      */

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

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

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

            try
            {
               return paths.equals(value);
            }
            catch(Exception e)
            {
               return false;
            }
         }
         else
         {
            if (!nameOnly)
               path = paths.getPath();

            if (SearchStringComponent.compareSubString(path, value, 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                    TrusteePathEnumerator
    *
    */
   public Object getStaticInterface()
   {
      TrusteePathEnumeratorImpl tEnum =
         new TrusteePathEnumeratorImpl(environment, true);

      return tEnum;
   }

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


   private boolean scanNextTrusteePath()
   {
      int ccode;
      String[] path = new String[1];
      int[] iterHandle = { this.iterHandle };

      if (!userIsSet)
      {
         finished = true;
         return (false);
      }

      try
      {
         callsService.scanObjectTrusteePaths(
               objID,
               volumeNumber,
               NameSpace.nameToNumber(nameSpace),
               iterHandle,
               null, // we don't want the rights back, just the path
               path);
      }
      catch (SessionException e)
      {
         throw new NSIException(e.getMessage(), 0, e);
      }
      catch (RemoteException e)
      {
         throw new NSIException(e.getMessage(), 0, e);
      }

      this.iterHandle = iterHandle[0];
      this.nextPath = path[0];

      return (nextPath.length() > 0);
   }
}
