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

  $Archive: /njcl_v2rmi/src/com/novell/service/jncpv2r/net/NetIterator.java $
  $Revision: 23 $
  $Modtime: 1/11/01 5:35p $

  Copyright (c) 1997 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.jncpv2r.net;

import java.rmi.RemoteException;
import java.io.*;
import java.util.StringTokenizer;

import com.novell.service.jncp.*;
import com.novell.service.nds.NdsIteratorInfo;
import com.novell.service.nds.NdsIteratorControls;
import com.novell.service.session.SessionException;


/** @internal
 * Represents an iterator for NDS operations.
 *
 * <p>NetIterator uses NetService methods to define a logic boundary for
 * operations on iterator handles.
 *
 * An Iterator in NDS is a concept for handling large virtual lists.  It
 * provides List and Search functionality, but can deal with very large
 * result sets, on the order of hundreds of thousands.

 * Conceptually, an Iterator creates a large virtual list on the server
 * containing all the entries which pass the search filter.  This list
 * contains only objects on the particular server in question.  It does
 * not span servers.</p>
 */
public final class NetIterator implements Cloneable
{
   public final static int DS_ITR_INVALID                   = 0;
   private transient boolean isCopy; //set to true if created via serialization

   /*
    * NetIterator error codes
    */

   /* The Iterator pointer is invalid */
   public final static int DS_ITR_INVALID_HANDLE            = -399;
   /* Position out of range 0 - 1000 */
   public final static int DS_ITR_INVALID_POSITION          = -398;
   /* Unexpected or corrupt data in search results */
   public final static int DS_ITR_INVALID_SEARCH_DATA       = -397;
   /* DS_SEARCH_SUBTREE is not allowed */
   public final static int DS_ITR_INVALID_SCOPE             = -395;

   /* Iterator reached beg of data */
   public final static int ERR_BOF_HIT                      = -764;
   /* Iterator reached end of data */
   public final static int ERR_EOF_HIT                      = -765;


   private NetService service;
   private int handle;


   /**
    * Constructs an NetIterator using a NetService.
    *
    * @return                    An uninitialized NetIterator.
    *
    * @exception NSIException    Generic, source-unknown exception.
    */
   public NetIterator (
         NetService service,
         String baseObjectName,
         int filter,
         boolean allAttrs,
         int attrNames)
      throws NSIException
   {
      this (
               service,
               baseObjectName,
               NetJNI.DS_SEARCH_SUBORDINATES,
               true,                            // searchAliases
               filter,
               0,                               // timeFilter
               NetJNI.DS_ATTRIBUTE_VALUES,
               allAttrs,
               attrNames,
               "",                              // indexSelect
               "",                              // sortKey
               NdsIteratorControls.DS_ITR_PREFER_SCALABLE,
               0);                              // timeout

   } /* NetIterator () */


   /**
    * Constructs an NetIterator using an NetContext and a size.
    *
    * <p>This constructor does not initialize the buffer.
    * </p>
    *
    * @return                    An uninitialized NetIterator.
    *
    * @exception NSIException    Generic, source-unknown exception.
    */
   public NetIterator (
         NetService service,
         String baseObjectName,
         int scope,
         boolean searchAliases,
         int filter,
         int timeFilter,
         int infoType,
         boolean allAttrs,
         int attrNames,
         String indexSelect,
         String sortKey,
         int scalability,
         int timeout)
      throws NSIException
   {
      this.service = service;

      try
      {
         int[] handle = new int[1];

         service.itrCreateSearch (
                              baseObjectName,
                              scope,
                              searchAliases,
                              filter,
                              timeFilter,
                              infoType,
                              allAttrs,
                              attrNames,
                              indexSelect,
                              sortKey,
                              scalability,
                              timeout,
                              handle);

         this.handle = handle[0];
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   } /* NetIterator () */


   /**
    * Constructs a NetIterator using NWDSItrCreateList
    *
    * @exception NSIException    Generic, source-unknown exception.
    */
   public NetIterator (
         NetService service,
         String objectName,
         String className,
         String subordinateName,
         int scalability,
         int timeout)
      throws NSIException
   {
      this.service = service;

      try
      {
         int[] handle = new int[1];

         service.itrCreateList (
                              objectName,
                              className,
                              subordinateName,
                              scalability,
                              timeout,
                              handle);

         this.handle = handle[0];
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   } /* NetIterator () */

   /**
    *
    */
   public void finalize ()
   {
      try
      {

         if(!isCopy && handle != DS_ITR_INVALID)
         {
            service.itrDestroy (handle);
            handle = DS_ITR_INVALID;
         }

      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   }


   // ******************** NetIterator Class ********************

   /**
    * Closes the Iterator and frees all associated memory.
    */
   public void destroy ()
      throws NSIException
   {
      try
      {
         service.itrDestroy (handle);

         handle = DS_ITR_INVALID;

      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   } /* destroy () */


   /**
    *
    */
   public boolean atFirst ()
      throws NSIException
   {
      try
      {
         return (service.itrAtFirst (handle));
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   } /* atFirst () */

   /**
    *
    */
   public boolean atEOF ()
      throws NSIException
   {
      try
      {
         return (service.itrAtEOF (handle));
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   } /* atEOF () */

   /**
    *
    */
   public NdsIteratorInfo getInfo ()
      throws NSIException
   {
      int ccode;
      boolean[] isScalable = new boolean[1];
      boolean[] isPositionable = new boolean[1];

      try
      {
         service.itrGetInfo (
                                 handle,
                                 isScalable,
                                 isPositionable);

         return (new Info (
                              isScalable[0],
                              isPositionable[0]));
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   } /* itrGetInfo () */


   /**
    * Get the Iterator's current logical position.
    */
   public int getPosition (
         int timeout)
      throws NSIException
   {
      try
      {
         int[] position = new int[1];

         service.itrGetPosition (handle, position, timeout);

         return (position[0]);
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   } /* getPosition () */

   /**
    * position according to a logical value.
    */
   public void setPosition (
         int position,
         int timeout)
      throws NSIException
   {
      try
      {
         service.itrSetPosition (handle, position, timeout);
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   } /* setPosition () */

   /**
    * current position to the source Iterator's position.
    */
   public void setPosition (
         NetIterator srcIterator,
         int timeout)
      throws NSIException
   {
      try
      {
         service.itrSetPositionFromIterator (handle, srcIterator.handle, timeout);
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   } /* setPosition () */

   /**
    * position according to the specified attribute  and value.
    *
    * Implements "typedown" (e.g., If you have a list sorted by surname, you
    * can specify "D" and it will position to the first name starting with
    * D.  If you specify "DA", it positions to the first name starting with
    * DA, etc.  If there are no entries matching the value string,
    * it positions to the first one which is greater than the specified
    * value.
    */
   public void setPosition (
         String attribute,
         String value,
         int timeout)
      throws NSIException
   {
      try
      {
         service.itrTypeDown (
                                 handle,
                                 attribute,
                                 value,
                                 timeout);
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   } /* setPosition () */

   /**
    * Skip entries, either forward or backward.
    */
   public int skip (
         int numToSkip,
         int timeout)
      throws NSIException
   {
      try
      {
         int[] skipped = new int[1];

         service.itrSkip (handle, numToSkip, timeout, skipped);

         return (skipped[0]);
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   } /* skip () */

   /**
    * Get the next n entries, including the current entry.  The current
    * position is left pointing to the next unread entry after the last one
    * retrieved.  If the end of the list is reached, ERR_EOF_HIT is returned
    * and the current position is left at EOL, an imaginary position just
    * after the last entry.
    */
   public void getNext (
         int numEntries,
         int timeout,
         NetIterationHandle iterationHandle,
         NetBuffer data)
      throws NSIException
   {
      try
      {
         service.itrGetNext (
                                 handle,
                                 numEntries,
                                 timeout,
                                 iterationHandle,
                                 data.getHandle ());
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   } /* getNext () */

   /**
    * Get the previous n entries, NOT including the current entry.
    * The entries are returned in REVERSE sort order.
    * The current position is left pointing to the last entry returned,
    * (the one closest to the top of the list).
    * If an attempt is made to read beyond the top of the list, ERR_BOF_HIT
    * is returned and the current position is left at the first entry.
    */
   public void getPrev (
         int numEntries,
         int timeout,
         NetIterationHandle iterationHandle,
         NetBuffer data)
      throws NSIException
   {
      try
      {
         service.itrGetPrev (
                                 handle,
                                 numEntries,
                                 timeout,
                                 iterationHandle,
                                 data.getHandle ());
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   } /* getPrev () */

   /**
    * Get the entry at the current position.  The current position is left
    * unchanged.
    */
   public void getCurrent (
         NetIterationHandle iterationHandle,
         NetBuffer data)
      throws NSIException
   {
      try
      {
         service.itrGetCurrent (
                                    handle,
                                    iterationHandle,
                                    data.getHandle ());
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   } /* getCurrent () */

   /**
    * Get an estimate of the number of entries in this iterator.
    *
    * The function will scan the list until:
    *    a) The time limit expires, or
    *    b) maxCount entries have been counted.
    *    c) The end of the list is reached.
    *
    * Entries are scanned starting from the current position.
    */
   public int count (
         int timeout,
         int maxCount,
         boolean updatePosition)
      throws NSIException
   {
      try
      {
         int[] count = new int[1];

         service.itrCount (handle, timeout, maxCount, updatePosition, count);

         return (count[0]);
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   } /* count () */


   // ******************** Object Class ********************

   /**
    * Creates a new object of the same class as this object. It
    * then initializes each of the new object's fields by assigning
    * them the same value as the corresponding fields in this object.
    * No constructor is called.
    *
    * @return A clone of this object instance containing the cloned
    *         syntax.
    *
    * @exception CloneNotSupportedException The object's class does
    *            not support the Cloneable interface.
    */
   public Object clone ()
   {
      try
      {
         int[] handle = new int[1];
         NetIterator iterator = (NetIterator) super.clone ();

         service.itrClone (this.handle, handle);

         iterator.service = service;
         iterator.handle = handle[0];

         return (iterator);
      }
      catch (CloneNotSupportedException e)
      {
         // this shouldn't happen, since we are Cloneable
         throw (new InternalError ());
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }
      catch(RemoteException e)
      {
         throw NSIExceptionBuilder.build(e);
      }

   } /* clone () */

   private void readObject(ObjectInputStream in)
   throws IOException, ClassNotFoundException
   {
      in.defaultReadObject();
      // If we get deserialized, dont run finalizer
      isCopy = true;
   }

   /**
    *
    */
   class Info implements NdsIteratorInfo
   {
      boolean scalable;
      boolean positionable;

      /**
       *
       */
      Info (
            boolean isScalable,
            boolean isPositionable)
      {
         this.scalable = isScalable;
         this.positionable = isPositionable;
      }

      /**
       *
       */
      public boolean isScalable ()
      {
         return (scalable);
      }

      /**
       *
       */
      public boolean isPositionable ()
      {
         return (positionable);
      }

   } /* Info */

} /* NetIterator */


