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

  $Archive: /njcl_v2/src/com/novell/service/file/nw/naming/FileSystemInitialDirContext.java $
  $Revision: 24 $
  $Modtime: 2/22/00 5:15p $
 
  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.naming;


import java.util.*;

//import java.rmi.RemoteException;

import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.spi.ResolveResult;

import com.sun.jndi.toolkit.ctx.ComponentDirContext;
import com.sun.jndi.toolkit.ctx.Continuation;

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

import com.novell.service.session.SessionException;
import com.novell.service.session.xplat.CallsService;
import com.novell.service.session.xplat.Version;
import com.novell.service.session.xplat.Xplat;

import com.novell.utility.naming.directory.NAttributes;
import com.novell.utility.naming.directory.StaticSchemaContext;

import com.novell.service.jncp.NSIException;
import com.novell.service.jncp.ServerErrors;


/** @internal
 * This is the initial context in the NetWare file system naming system.
 * From this context, volumes are resolved, and then file/dir names.
 * Many methods from the Context and DSContext interfaces throw
 * OperationNotSupportedException.  However, lookup() and list() work.
 * All this context needs to exist is a session to a NetWare server.
 *
 * <p>When looking up a volume, a name space can be specified along
 * with the volume name.  For example: when looking up the SYS volume,
 * and the DOS name space is desired, you can force names to be resolved
 * in the DOS name space, by first resolving "SYS+DOS".  The name space tags
 * are as follows: DOS, MAC, NFS, FTAM, LONG.  You may also just look up
 * "SYS" and it will be found.  In that case, the optimal supported name
 * space will be used, based on the JVM that you are running under and
 * which name spaces are supported on the volume which you are looking up.
 *
 * <p>The FileSystemInitialDirContext class is Referenceable, and calls
 * 'ReferenceFactoryImpl.createReference()' to create the needed Reference.
 * </p>
 */
public class FileSystemInitialDirContext extends ComponentDirContext
      implements Referenceable
{
   private String[] volNames = null;      // see scanVolumes ()
   private int[] volNumbers = null;       // see scanVolumes ()
   private int[][] volNameSpaces = null;  // [volumeNumber][NameSpace]
   private int[] defaultNameSpaces = null;   // index is volumeNumber
   private String serverName;
   private String selectedNameSpace;
   private String selectedVolumeName;
   private CallsService callsService;
   private FSEnvironment environment = null;

   /**
    * Constructs an initial context into the NetWare file system naming
    * system.
    *
    * @param environment         The File system environment object
    * @exception NamingException When an error occurs.
    */
   public FileSystemInitialDirContext(
      String serverName, 
      Hashtable environment)
      throws NamingException
   {
      this.serverName = serverName;
      this.environment = new FSEnvironment(environment, serverName);

      callsService = this.environment.getCallsService();
   }


   // ******************** Context Interface ********************

   /** @internal
    *
    */
   public String getNameInNamespace ()
      throws NamingException
   {
      return ("[Root]");
   }


   // ******************** ComponentContext Interface ********************

   /**
    * Resolves the given single-component name.
    *
    * @param      name           The single-component name to be resolved.
    *                            If 'name' is empty, 'lookup()' returns a
    *                            'this'.  The name must contain the name
    *                            of a volume on the server to which this
    *                            context has a session.
    * @param      cont           The continuation information.
    * @return                    VolumeDirContext bound to this context under
    *                            'name'.
    * @exception NamingException When 'name' is null or an error occurs
    *                            resolving the name.
    */
   protected Object c_lookup (Name name, Continuation cont)
      throws NamingException
   {
      if (name.isEmpty())
      {
         cont.setSuccess();
         return (this);
      }

      // findVolume sets up environment.volumeName and environment.nameSpace
      // on successful resolution of 'name'
      findVolume(name, cont);
      environment = 
         environment.setNSandVolume(selectedNameSpace, selectedVolumeName);
   
      VolumeDirContext ctx = (VolumeDirContext)
         ContextFactoryImpl.getContextInstance(
                                 null,
                                 null,
                                 environment,
                                 ContextFactoryImpl.CREATE_VOLUME_CONTEXT);
      cont.setSuccess();
      return (ctx);
   }

   /**
    * Resolves to link specified by the given single-component name.
    *
    * <p>Currently, this is just the same as calling lookup().
    * </p>
    *
    * @param      name           The single-component name specifying
    *                            the link to which to resolve.
    * @param      cont           The continuation information.
    * @return                    The object bound to the name by not
    *                            following the terminal link (if any).
    * @exception NamingException When 'name' is null or an error occurs
    *                            resolving the name.
    */
   protected Object c_lookupLink (Name name, Continuation cont)
      throws NamingException
   {
      return (c_lookup (name, cont));
   }

   /**
    * Lists all volume names bound to this context.
    *
    * @param      name           The name which should be empty coming
    *                            from the toolkit.
    * @param      cont           The continuation information.
    * @return                    NameClassEnumeration with all NameClassPairs
    *                            in it.
    * @exception NamingException When an error occurs getting the volume
    *                            names.
    */
   protected NamingEnumeration c_list (Name name, Continuation cont)
      throws NamingException
   {
      if (!name.isEmpty ())
      {
         resolveNext(name, cont);
         return null;
      }

      scanVolumes (name, cont);   // update volume listing

      cont.setSuccess ();

      return new VolumeNCEnumerator (volNames, volNameSpaces);
   }

   /**
    * Lists all volumes bound to this context.
    *
    * @param      name           The name which should be empty coming
    *                            from the toolkit.
    * @param      cont           The continuation information.
    * @return                    BindingEnumeration with all Bindings
    *                            in it.
    * @exception NamingException When an error occurs getting the volume
    *                            names.
    */
   protected NamingEnumeration c_listBindings (Name name, Continuation cont)
      throws NamingException
   {
      if (!name.isEmpty ())
      {
         resolveNext(name, cont);
         return (null);
      }

      scanVolumes (name, cont);   // update volume listing

      cont.setSuccess ();

      return new VolumeBindingEnumerator (
                     volNames, 
                     volNameSpaces,
                     defaultNameSpaces, 
                     serverName,
                     environment.getEnvironment(false));
   }

   /**
    * Method not supported in FileSystemInitialDirContext.
    *
    * @exception NamingException Always thrown.
    */
   protected void c_bind (Name name, Object obj, Continuation cont)
      throws NamingException
   {
      cont.setError (this, name);
      throw cont.fillInException (new OperationNotSupportedException ());
   }

   /**
    * Method not supported in FileSystemInitialDirContext.
    *
    * @exception NamingException Always thrown.
    */
   protected void c_rebind (Name name, Object obj, Continuation cont)
      throws NamingException
   {
      cont.setError (this, name);
      throw cont.fillInException (new OperationNotSupportedException ());
   }

   /**
    * Method not supported in FileSystemInitialDirContext.
    *
    * @exception NamingException Always thrown.
    */
   protected void c_unbind (Name name, Continuation cont)
      throws NamingException
   {
      cont.setError (this, name);
      throw cont.fillInException (new OperationNotSupportedException ());
   }

   /**
    * Tries to mount the volume specified in the name.
    *
    * <p>This operation requires that you have console operator rights
    * at the minimum.
    * </p>
    *
    * @param      name           The name of the unmounted volume.
    * @param      cont           The continuation information.
    * @exception NamingException When an error occurs unmounting the volume.
    */
   protected Context c_createSubcontext (Name name, Continuation cont)
      throws NamingException
   {
      if (name.isEmpty ())
      {
         cont.setError (this, name);
         throw cont.fillInException (new InvalidNameException ());
      }

      try
      {
         callsService.smMountVolume (
            name.toString().toUpperCase(),
            null);   // don't care about return vol number

      }
      catch (NSIException nsie)
      {
         NamingException ne = new NamingException (nsie.getMessage());
         ne.setRootCause (nsie);
         cont.setError (this, name);
         throw cont.fillInException (ne);
      }
      catch (SessionException e)
      {
         NamingException ne = new NamingException (e.getMessage());
         ne.setRootCause (e);
         cont.setError (this, name);
         throw cont.fillInException (ne);
      }

      return ((Context) c_lookup (name, cont));
   }

   /**
    * Tries to unmount the volume specified in the name.
    *
    * <p>This operation requires that you have console operator rights
    * at the minimum.
    * </p>
    *
    * @param      name           The name of the mounted volume.
    * @param      cont           The continuation information.
    * @exception NamingException When an error occurs unmounting the volume.
    */
   protected void c_destroySubcontext (Name name, Continuation cont)
      throws NamingException
   {
      if (name.isEmpty ())
      {
         cont.setError (this, name);
         throw cont.fillInException (new InvalidNameException ());
      }

      try
      {
         callsService.smDismountVolumeByName(name.toString().toUpperCase());

      }
      catch (NSIException nsie)
      {
         NamingException ne = new NamingException (nsie.getMessage());
         ne.setRootCause (nsie);
         cont.setError (this, name);
         throw cont.fillInException (ne);
      }
      catch (SessionException e)
      {
         NamingException ne = new NamingException (e.getMessage());
         ne.setRootCause (e);
         cont.setError (this, name);
         throw cont.fillInException (ne);
      }
   }

   /**
    * Method not supported in FileSystemInitialDirContext.
    *
    * @exception NamingException Always thrown.
    */
   protected void c_rename (Name name, Name newname, Continuation cont)
      throws NamingException
   {
      cont.setError (this, name);
      throw cont.fillInException (new OperationNotSupportedException ());
   }

   /**
    * Returns the name parser that can be used to parse names
    * in the NetWare file sytem naming system.
    *
    * @param      name           Should be empty coming from the tookit.
    * @param      cont           The continuation information.
    * @return                    Name parser for the NetWare file system
    *                            naming system.
    * @exception NamingException Never thrown.
    */
   protected NameParser c_getNameParser (Name name, Continuation cont)
      throws NamingException
   {
      if (!name.isEmpty ())
      {
         resolveNext(name, cont);
         return null;
      }

      cont.setSuccess ();

      return (new FileSystemNameParser (getServerName (), false));
   }

   /**
    * Retrieves environment for this context.
    *
    * @return                    The environment of this context.
    */
   public Hashtable getEnvironment()
      throws NamingException
   {
      return environment.getEnvironment(true);
   }

   public Object addToEnvironment(String propName, Object propVal)
      throws NamingException
   {
      return (environment.addToEnvironment(propName, propVal));
   }

   public Object removeFromEnvironment(String propName)
      throws NamingException
   {
      return (environment.removeFromEnvironment(propName));
   }

   public void close() throws NamingException
   {
      //no action necessary
   }

/* **************************************************************************
   Implement ComponentDSContext
****************************************************************************/

/* *************************************************************************/
   // needed by ReferenceFactoryImpl - which is local to this package

   String getServerName()
   {
      return environment.getServerName();   
   }

   /**
    * Get attributes.
    *
    * <p>Retrieves all the attributes associated with named object.
    * </p>
    *
    * @param name                The string name of the object for which
    *                            to retrieve the attributes.  If name is the
    *                            empty string, retrieves the attributes of
    *                            this context object.
    * @param attrIds             The list of attribute ID's to be included
    *                            in the returned AttributeSet.
    * @param cont                The continuation object to be maintained
    *                            for downstream federation.
    *
    * @return                    The attribute set associated with 'name' and
    *                            requested by the itemized attrIds list.
    * @exception NamingException Never thrown.
    *
    */
   protected Attributes c_getAttributes(
      Name name,
      String[] attrIds,
      Continuation cont)
      throws NamingException
   {
      if (!name.isEmpty())
      {
         resolveNext(name, cont);
         return null;
      }

      /*
         there are no Attributes associated with this DSContext,
         return an empty set
      */
      cont.setSuccess();
      return(new NAttributes(true));
   }

   /**
    * Modify attributes.
    *
    * <p>Modifies the attributes associated with named object.
    * </p>
    *
    * @param name                The string name of the object for which
    *                            to modify the attributes.  If name is the
    *                            empty string, modifies the attributes of
    *                            this context object.
    * @param mod_op              Type of modification to do to with the
    *                            attrs given.  Possible values are:
    *                            DSContext.ADD_ATTRIBUTE,
    *                            DSContext.REPLACE_ATTRIBUTE, and
    *                            DSContext.DELETE_ATTRIBUTE
    *                            in the returned AttributeSet.
    * @param attrs               The AttributeSet to be applied, in the
    *                            manner specified in mod_op.
    * @param cont                Continuation object to be maintained
    *                            for downstream federation.
    * @exception AttributeModification
    *
    */
   protected void c_modifyAttributes(
      Name name,
      int mod_op,
      Attributes attrs,
      Continuation cont)
      throws NamingException
   {
      if (!name.isEmpty())
      {
         resolveNext(name, cont);
         return;
      }

      cont.setError(this, name);
      throw cont.fillInException(new AttributeModificationException());
   }

   /**
    * Modify attributes.
    *
    * <p>Modifies the attributes associated with named object.
    * </p>
    *
    * @param name                The string name of the object for which
    *                            to modify the attributes.  If name is the
    *                            empty string, modifies the attributes of
    *                            this context object.
    * @param mod_op              Type of modification to do to with the
    *                            attrs given.  Possible values are:
    *                            DSContext.ADD_ATTRIBUTE,
    *                            DSContext.REPLACE_ATTRIBUTE, and
    *                            DSContext.DELETE_ATTRIBUTE
    *                            in the returned AttributeSet.
    * @param attrs               The AttributeSet to be applied, in the
    *                            manner specified in mod_op.
    * @param cont                Continuation object to be maintained
    *                            for downstream federation.
    * @exception NamingException
    *
    */
   protected void c_modifyAttributes(
      Name name,
      ModificationItem[] mods,
      Continuation cont)
      throws NamingException
   {
      if (!name.isEmpty())
      {
         resolveNext(name, cont);
         return;
      }

      cont.setError(this, name);
      throw cont.fillInException(new AttributeModificationException());
   }

   protected void c_bind(
      Name name,
      Object obj,
      Attributes attrs,
      Continuation cont)
      throws NamingException
   {
      cont.setError(this, name);
      throw cont.fillInException(new OperationNotSupportedException());
   }

   protected void c_rebind(
      Name name,
      Object obj,
      Attributes attrs,
      Continuation cont)
      throws NamingException
   {
      cont.setError(this, name);
      throw cont.fillInException(new OperationNotSupportedException());
   }

   protected DirContext c_createSubcontext(
      Name name,
      Attributes attrs,
      Continuation cont)
      throws NamingException
   {
      cont.setError(this, name);
      throw cont.fillInException(new OperationNotSupportedException());
   }

   protected NamingEnumeration c_search(
      Name name,
      Attributes matchingAttributes,
      String[] attributesToReturn,
      Continuation cont)
      throws NamingException
   {
      if (!name.isEmpty())
      {
         resolveNext(name, cont);
         return null;
      }

      cont.setError(this, name);
      throw cont.fillInException(new OperationNotSupportedException());
   }

   protected NamingEnumeration c_search(
      Name name,
      String filter,
      SearchControls cons,
      Continuation cont)
      throws NamingException
   {
      if (!name.isEmpty())
      {
         resolveNext(name, cont);
         return null;
      }

      cont.setError(this, name);
      throw cont.fillInException(new OperationNotSupportedException());
   }

   protected NamingEnumeration c_search(
      Name name,
      String filterExpr,
      Object [] filterArgs,
      SearchControls cons,
      Continuation cont)
      throws NamingException
   {
      if (!name.isEmpty())
      {
         resolveNext(name, cont);
         return null;
      }

      cont.setError(this, name);
      throw cont.fillInException(new OperationNotSupportedException());
   }

   /**
    * Get the file system's root schema context.
    *
    * @param      name           The name that should be empty coming from
    *                            the toolkit.
    * @param      cont           The continuation information from the
    *                            toolkit.
    * @return                    The root schema context of the file system
    *                            naming system.
    * @exception NamingException When an error occurs getting the root
    *                            file system schema context.
    *
    * @see com.novell.service.file.nw.naming.Schema
    */
   protected DirContext c_getSchema(Name name, Continuation cont)
      throws NamingException
   {
      if (!name.isEmpty ())
      {
         resolveNext(name, cont);
         return null;
      }

      cont.setSuccess();
      return(new Schema(environment));
   }

   /**
    * Get the schema class definition context.
    *
    * <p>Returns the schema class definition for this file system context.
    * </p>
    *
    * @param      name           The name that should be empty coming from
    *                            the toolkit.
    * @param      cont           The continuation information from the
    *                            toolkit.
    * @return                    The schema class definition context
    *                            for this file system context.
    * @exception NamingException When an error occurs getting the file
    *                            system schema class definition context.
    *
    * @see com.novell.service.file.nw.naming.SchemaClassDef
    */
   protected DirContext c_getSchemaClassDefinition (
         Name name,
         Continuation cont)
      throws NamingException
   {
      if (!name.isEmpty ())
      {
         resolveNext (name, cont);
         return null;
      }

      Binding[] bindings = { 
         new Binding (
                        "Initial",
                        SchemaClassDef.class.getName (),
                        new SchemaClassDef (this, environment)) };

      DirContext root = new StaticSchemaContext ("[Root]", bindings);

      cont.setSuccess ();
      return (root);

   } /* c_getSchemaClassDefinition() */

/* **************************************************************************
   implement Referanceable
****************************************************************************/

   /**
    * Construct a Reference for this context.
    *
    * @return                    A JNDI reference that contains all
    *                            information on how to locate this context
    *                            again.
    * @see ReferenceFactoryImpl#createReference(FileSystemInitial
    */
   public Reference getReference ()
      throws NamingException
   {
      return (new ReferenceFactoryImpl ().createReference (this));
   }

   /**
    * Updates an internal list of volume names and available name spaces
    * on each of theses volumes.
    *
    * <p>After this method has successfully scanned all volumes, the
    * following variables are filled out:
    * <ul>
    * <li>volNames - contains array of all volumes
    * <li>volNameSpaces - contains array of all supported NS #'s for each vol
    * <li>defaultNameSpaces - contains array of default NS for each vol
    * </ul>
    * </p>
    *
    * @param      name           The name passed from different context
    *                            methods.  It is passed so that we can
    *                            throw a NamingException exception in
    *                            this method.
    * @param      cont           The continuation information passed from
    *                            different context methods.  It is passed
    *                            so that we can throw a NamingException
    *                            exception in this method.
    * @exception NamingException When an error occurs scanning the volumes.
    */
   private void scanVolumes (Name name, Continuation cont)
      throws NamingException
   {
      int ccode = 0, i;
      Vector v = new Vector();
      int volumeCount = 0;
      int majorVersion;
      String attrID = Xplat.CONN_SERVER_VERSION_ATTR_ID;

      try
      {
         Version ver = (Version) environment.getSession().
               getAttributes(new String[] {attrID}).getValue(attrID);

         majorVersion = ver.majorVersion;
      }
      catch (SessionException e)
      {
         NamingException ne = new NamingException(e.getMessage());
         ne.setRootCause(e);
         cont.setError(this, name);
         throw cont.fillInException(ne);
      }

      if (majorVersion <= 3)
      {
         String[] nameArr = new String[1];
         NSIException nsie = null;
   
         i = 0;

         // Get the Volume names on this server (session)
         //   A server can have volumes with discontiguous volume numbers
         //   For example, if there are three volumes: 'v1', 'v2', and 'v3' on
         //   server 'srv', and 'v2' is NOT mounted, then we'd have to
         //   scan volume numbers: 0, 1, and 2 to get all the mounted volumes.
         // This goes from volume number 0 on up until an error is returned.
         // We're assuming here, that if the volume number is too high,
         //   that the 0x8998 error will come back.
         while (ccode == 0)
         {
            try
            {
               callsService.getVolumeName(i, nameArr);
            } catch (NSIException e)
            {
               ccode = e.getCCode();
               nsie = e;
               break;  // shortcuts trying every one of 64 vol numbers
            }
            catch (SessionException se)
            {
               throw new NSIException(se.getMessage(), 0, se);
            }
   
            if (nameArr[0].length() > 0)
            {
               v.addElement(nameArr[0]);
               v.addElement(new Integer(i));
               volumeCount++;
            }
   
            i++;
         }
   
         // on the last volume name, the ccode will still be zero,
         //   the API is just like that.  Even if you specify an invalid
         //   volume number, it will still return 0, it will just
         //   return an empty string for the volume name.
         if (ccode != 0 && ccode != ServerErrors.NWE_VOL_INVALID)
         {
            NamingException ne = new NamingException(nsie.getMessage());
            ne.setRootCause(nsie);
            cont.setError(this, name);
            throw cont.fillInException(ne);
         }
      }
      else
      {
         int[][] numbers = new int[1][];
         String[][] names = new String[1][];
         int[] nextNumber = new int[1];
         int volNumber = 0;

         for (;;)
         {
            try
            {
               callsService.scanMountedVolumes(volNumber, numbers,
                     names, nextNumber);
            }
            catch (SessionException se)
            {
               throw new NSIException(se.getMessage(), 0, se);
            }

            for (i = 0; i < numbers[0].length; i++)
            {
               v.addElement(names[0][i]);
               v.addElement(new Integer(numbers[0][i]));

               volumeCount++;
            }

            volNumber = nextNumber[0];

            if (volNumber == 0)
               break;
         }
      }

      // store the volume names in the private member String[] volNames
      volNames = new String[volumeCount];
      volNumbers = new int[volumeCount];

      i = 0;

      for (int j = 0; j < v.size (); i++)
      {
         volNames[i] = (String) v.elementAt(j++);
         volNumbers[i] = ((Integer) v.elementAt(j++)).intValue ();
      }
      // get the loaded name spaces for each volume found in above step
      int[][] NSLoadedList = new int[1][];

      v.removeAllElements();

      for (i = 0; i < volNames.length; i++)
      {
         try
         {
            callsService.getNSLoadedList(volNumbers[i], NSLoadedList);
         }
         catch (NSIException nsi)
         {
            NamingException ne = new NamingException(nsi.getMessage());
            ne.setRootCause(nsi);
            cont.setError(this, name);
            throw cont.fillInException(ne);
         }
         catch (SessionException e)
         {
            NamingException ne = new NamingException(e.getMessage());
            ne.setRootCause(e);
            cont.setError(this, name);
            throw cont.fillInException(ne);
         }

         v.addElement(NSLoadedList[0]);
      }

      // store the name spaces in the private member int[][] volNameSpaces,
      //   where the first dimension is the volume Number and the second
      //   dimension is the loaded name space list associated with the
      //   volume number (first dimension)
      volNameSpaces = new int[volumeCount][]; //[volumeNumber][NameSpace]

      for (int j = 0; j < volumeCount; j++)
         volNameSpaces[j] = (int[]) v.elementAt(j);

      // find the default name space for each volume, based on the following:
      //   1. find the default name space of the client
      //   2. see if the client's name space is found on the volume.
      //     a. if so, set the volumes appropriate defaultNameSpaces element
      //        to the clients default name space.
      //     b. if not, set the volumes appropriate defaultNameSpaces element
      //        to the DOS name space.

      // find the default name space of the client
      int clientNS = NameSpace.getPlatformDefaultNameSpace();
      Object defaultNS = environment.getDefaultNameSpace();

      if (defaultNS instanceof String)  // null defaultNS will not enter 'if'
      {
         String defaultNSStr = (String) defaultNS;
         int temp = NameSpace.nameToNumber(defaultNSStr.toUpperCase());

         if (temp != -1)
            clientNS = temp;
      }

      defaultNameSpaces = new int[volumeCount];

      for (i = 0; i < volumeCount; i++)
      {
         boolean found = false;

         for (int j = 0; j < volNameSpaces[i].length; j++)
         {
            if (volNameSpaces[i][j] == clientNS)
            {
               defaultNameSpaces[i] = clientNS;
               found = true;
               break;
            }
         }

         if (!found)
            defaultNameSpaces[i] = NameSpace.DOS_INT;
      }
   }

   /**
    * Finds a volume name in an internal list of volume names and available
    * name spaces on each of these volumes.
    *
    * @param      name           The name of the volume to find.
    * @param      cont           The continuation information is passed so
    *                            that we can throw a NamingException
    *                            in this method.
    * @exception NamingException When the volume is not found.
    */
   private void findVolume(Name name, Continuation cont)
      throws NamingException
   {
      scanVolumes (name, cont);   // update volume listing

      // in the DOS name space, some natives require the volume name to
      //   be upper case
      String volume = name.toString ().toUpperCase ();
      boolean usingDefault = true;

      // see if they have specified a name space with the volume
      int offset = volume.indexOf('+');
      if (offset != -1)
      {
         selectedVolumeName = volume.substring(0, offset);
         selectedNameSpace = volume.substring(offset + 1);
         usingDefault = false;
      }
      else
         selectedVolumeName = volume;

      // look for the given volume name amoung those available
      for (int i=0; i < volNames.length; i++)
      {
         if (volNames[i].equals(selectedVolumeName))
         {
            // found the volume name, see if the name space is ok
            if (usingDefault)
            {
               selectedNameSpace = NameSpace.numberToName(
                                    defaultNameSpaces[i]);
               return;
            }

            // A name space was specified, see if we have the requested
            //   one for this volume
            int nsNumber = NameSpace.nameToNumber(selectedNameSpace);

            for (int j=0; j < volNameSpaces[i].length; j++)
            {
               if (volNameSpaces[i][j] == nsNumber)
               {
                  selectedNameSpace = NameSpace.numberToName(
                                          volNameSpaces[i][j]);
                  return;
               }
            }

            // bad news, did not find the requested name space
            cont.setError(this, name);
            throw cont.fillInException(new NameNotFoundException());
         } // if the volume name was found
      } // for all possible volumes

      // bad news, did not find the requested volume
      cont.setError(this, name);
      throw cont.fillInException(new NameNotFoundException());
   } // end of findVolume

   /**
    * This is called by methods that need to perform name resolution.
    * The name is looked up and the Continuation object is told to continue.
    */
   private void resolveNext(
         Name name,
         Continuation cont)
      throws NamingException
   {
      Object obj = c_lookup(name, cont);
      if (null != obj)
      {
         cont.setContinue (obj, name, this);
      }
   }
}


/**
 * Enumerates all available volumes as NameClassPairs.
 */
class VolumeNCEnumerator implements NamingEnumeration
{
   private static String className = "com.novell.service.file.nw.naming.VolumeDirContext";

   private String[] volNames;
   private int[][] volNameSpaces;
   private int counter = 0;
   private int counterNS = 0;
   private boolean doneNoNSScan = false;
   private boolean done;

   public VolumeNCEnumerator(
            String[] volNames,
            int[][] volNameSpaces)
      throws NamingException
   {
      this.volNames = volNames;
      this.volNameSpaces = volNameSpaces;

      done = (volNames.length == 0);
   }

   public boolean hasMoreElements()
   {
      return !done;
   }

   public boolean hasMore()
      throws NamingException
   {
      return !done;
   }

   public Object nextElement()
   {
      try
      {
         return (next());
      }
      catch (NamingException ne)
      {
         Throwable t = ne.getRootCause();

         if (t instanceof NSIException)
            throw (NSIException) t;
         else
            throw new RuntimeException(ne.getMessage());
      }
   }

   public Object next()
      throws NamingException
   {
      if (done)
         throw new NoSuchElementException();

      String name;

      // list the simple volume names first (no NS tags)
      if (!doneNoNSScan)
         name = volNames[counter++];
      // list the complex volume names last (with NS tags)
      else
      {
         name = volNames[counter] + "+" +
               NameSpace.numberToName(volNameSpaces[counter][counterNS++]);

         if (counterNS >= volNameSpaces[counter].length)
         {
            counterNS = 0;
            counter++;
         }
      }

      if (counter >= volNames.length)
      {
         if (!doneNoNSScan)
         {
            doneNoNSScan = true;
            counter = 0;
         }
         else
            done = true;
      }

      return new NameClassPair(name, className, true);
   }

   public void close() throws NamingException
   {
      //no action necessary
   }
} /* VolumeNCEnumerator */


/**
 * Enumerates all available volumes as Bindings.
 */
class VolumeBindingEnumerator implements NamingEnumeration
{
   private String[] volNames;
   private int[][] volNameSpaces;
   private int[] defaultNameSpaces;
   private String serverName;
   private Hashtable env;
   private int counter = 0;
   private int counterNS = 0;
   private boolean doneNoNSScan = false;
   private boolean done;

   public VolumeBindingEnumerator(
               String[] volNames,
               int[][] volNameSpaces,
               int[] defaultNameSpaces,
               String serverName,
               Hashtable env)
   {
      this.volNames = volNames;
      this.volNameSpaces = volNameSpaces;
      this.defaultNameSpaces = defaultNameSpaces;
      this.serverName = serverName;
      this.env = env;

      done = (volNames.length == 0);
   }

   public boolean hasMoreElements()
   {
      return !done;
   }

   public boolean hasMore()
      throws NamingException
   {
      return !done;
   }

   public Object nextElement()
   {
      try
      {
         return (next());
      }
      catch (NamingException ne)
      {
         Throwable t = ne.getRootCause();

         if (t instanceof NSIException)
            throw (NSIException) t;
         else
            throw new RuntimeException(ne.getMessage());
      }
   }

   public Object next()
      throws NamingException
   {
      if (done)
         throw new NoSuchElementException ();

      String name;
      String plainName = volNames[counter];
      String nameSpace;

      // list the simple volume names first (no NS tags)
      if (!doneNoNSScan)
      {
         nameSpace = NameSpace.numberToName(defaultNameSpaces[counter++]);

         name = plainName;
      }
      // list the complex volume names last (with NS tags)
      else
      {
         nameSpace = NameSpace.numberToName(
               volNameSpaces[counter][counterNS++]);

         name = plainName + "+" + nameSpace;

         if (counterNS >= volNameSpaces[counter].length)
         {
            counterNS = 0;
            counter++;
         }
      }

      if (counter >= volNames.length)
      {
         if (!doneNoNSScan)
         {
            doneNoNSScan = true;
            counter = 0;
         }
         else
            done = true;
      }

      FSEnvironment environment = new FSEnvironment(env, serverName);
      environment.setNSandVolume(nameSpace, plainName);

      VolumeDirContext ctx = (VolumeDirContext)
         ContextFactoryImpl.getContextInstance(
                                 null,
                                 null,
                                 environment,
                                 ContextFactoryImpl.CREATE_VOLUME_CONTEXT);
                               
      return new Binding(name, ctx, true);
   }

   public void close() throws NamingException
   {
      //no action necessary
   }
} /* VolumeBindingEnumerator */
