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

  $Archive: /njcl_v2rmi/src/com/novell/service/file/nw/naming/DirEntryDirContext.java $
  $Revision: 41 $
  $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.naming;

import java.io.InputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Properties;

import java.rmi.RemoteException;

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

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

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

import com.novell.utility.naming.directory.SearchFilterFactory;
import com.novell.utility.naming.directory.SearchEnumerator;

import com.novell.service.file.nw.DirectoryEntryInformation;
import com.novell.service.file.nw.EAEnumerator;
import com.novell.service.file.nw.ExtendedAttribute;
import com.novell.service.file.nw.NameSpace;
//import com.novell.service.file.nw.NSInfoEnumerator;
import com.novell.service.file.nw.NFileInputStream;
import com.novell.service.file.nw.DataAccessableParameters;
import com.novell.service.file.nw.NFileOutputStream;
import com.novell.service.file.nw.TrusteeEnumerator;
import com.novell.service.file.nw.Trustee;
import com.novell.service.file.nw.EffectiveRights;
import com.novell.service.file.nw.naming.ExtendedAttributeLock;

import com.novell.service.file.nw.calls.DirectoryEntryInfoImpl;
import com.novell.service.file.nw.calls.EAAccessor;
import com.novell.service.file.nw.calls.EAEnumeratorImpl;
import com.novell.service.file.nw.calls.TrusteeEnumeratorImpl;
import com.novell.service.file.nw.calls.EffectiveRightsImpl;

import com.novell.service.jncp.NSIException;
import com.novell.service.jncp.ServerErrors;
//import com.novell.service.jncpv2r.cal.CalJNI;

import com.novell.service.rfc1960.Rfc1960Parser;
import com.novell.service.rfc1960.SearchStringComponent;

import com.novell.service.session.Session;
import com.novell.service.session.SessionException;
import com.novell.service.session.xplat.CallsService;

import com.novell.java.io.spi.DataAccessor;
import com.novell.java.io.DataAccessable;
import com.novell.java.io.RandomAccessNotSupportedException;

import com.novell.utility.naming.Environment;

/** @internal
 * Represents an abstract directory entry in the NetWare file system.
 *
 * <p>The DirEntryDirContext class extends ComponentDSContext.  It supports
 * weakly-separated naming.  It is a DataAccessable.  'DirEntryDirContext.
 * openStream(...)' returns an EAAccessor, if the selector passed in
 * is 0xFF.  This EAAccessor is used to access the contents
 * of an extended attribute.  This is a special case
 * to support explicit binding.
 *
 * <p>Explicit binding, as supported by this context, is being able to
 * call 'bind()' with an object that implements the Referenceable
 * interface, or is a Reference, which contains no other RefAddr class
 * types than BinaryRefAddr and StringRefAddr.  The named References are
 * streamed out to an extended attribute named '__JNDIReferences'.
 *
 * <p>Each DirEntryDirContext has a parent.  A full native dir entry name
 * is constructed by calling each parent and appending all names together
 * with backslashes, with a colon between the volume and the first directory.
 * This is accomplished in the 'Directory.addPath()' method.
 *
 * <p>Because everything is all appended together with backslashes, if any
 * of the components themselves contain backslashes, then it works, because
 * the NWCalls library will recognize the slashes as separators, even though
 * JNDI treats that component as a single component.
 *
 * <p>The DirEntryDirContext class is Referenceable, and calls
 * 'ReferenceFactoryImpl.createReference()' to create the needed Reference.
 * When a Reference is created for a DirEntryDirContext, the name space
 * is stored explicitly in the Reference address.  This forces the resolver
 * of the stored reference to retrieve the referenced context, and never
 * a context in another NetWare file name space.
 *
 * <p>DirectoryDirContext, and FileDirContext have their own set of
 * attribute values that are not shared with each other.  They also
 * need to include in their set of attribute values, those in
 * DirEntryDirContext.  They first operate on their own attributeValues
 * array, and then operate on their super's (this class's) through the
 * attrValues parameter.
 * </p>
 */
public abstract class DirEntryDirContext extends ComponentDirContext
      implements DataAccessable, Referenceable
{
   // name of the extended attribute in which named explicit bindings are
   //   stored (References)
   public static final String JNDIEAName = "__JNDIReferences";
   protected static final int NWE_NO_SUCH_EA = 0xC9;

   protected CallsService callsService;
   protected Session session;

   protected FSEnvironment environment = null;

   private static final int DEI_SAV = 0;
   private static final int EA_SAV  = 1;
   private static final int TRU_SAV = 2;
   private static final int EFR_SAV = 3;
//   private static final int NSI_SAV = 3;

   private DirectoryDirContext dir;
   private String atomicName;
   private String fullNodeName;
   private String fullUrlName;

   protected StaticAttributeValue[] attributeValues = null;

   /**
    * Constructs a DirEntryDirContext given a connection, a parent directory,
    * and a dir entry name.
    *
    * <p>This constructor pulls the server name out of the connection,
    * sets up the full native dir entry name, sets up attribute value info.
    * </p>
    *
    * @param dir                 The parent directory.  
    * @param atomicName          The atomic name of the dir entry in the
    *                            name space of the parent dir.
    * @param environment         The File system environment object
    * @exception NamingException When an error occurs getting the server
    *                            name from the connection or when any
    *                            other error occurs.
    */
   public DirEntryDirContext (
            DirectoryDirContext dir,
            String atomicName,
            FSEnvironment environment)
      throws NamingException
   {
      this.dir = dir;
      this.atomicName = atomicName;
      this.environment = new 
         FSEnvironment(environment);

      if (dir != null)
      {
         this.fullNodeName = dir.addPath(atomicName);
         fullUrlName = dir.getFullUrlName() + "/" + atomicName;
         
      }else
      {
         this.fullNodeName = atomicName;
         fullUrlName = atomicName;
      }

      this.environment.setFullNames(fullNodeName, fullUrlName);

      this.session = environment.getSession ();
      callsService = environment.getCallsService();
   }

/* *************************************************************************
* com.novell.java.io.NFile implementation
****************************************************************************/

   public long lastModified()
   {
      DirectoryEntryInformation dei = getDirectoryEntryInformation();
      long dateTime = dei.getModifyDate();
      dateTime <<= 16;
      dateTime |= dei.getModifyTime();
      return dateTime;
   }

   public long length()
   {
      DirectoryEntryInformation dei = getDirectoryEntryInformation();
      return dei.getSpaceAlloc();
   }

/* *************************************************************************
* com.novell.java.io.NFile deprecated method implementations
****************************************************************************/
/*
   public boolean exists()
   {
      return true;
   }

   public boolean canWrite()
   {
      return true;
   }

   public boolean canRead()
   {
      return true;
   }

   public boolean isDirectory()
   {
      if (this instanceof DirectoryDirContext)
         return true;
      else
         return false;
   }

   public boolean isFile()
   {
      if (this instanceof FileDirContext)
         return true;
      else
         return false;
   }

   public String[] list()
   {
      NamingEnumeration nce = null;
      Vector strings = new Vector();
      try
      {
         nce = list("");
         while (nce.hasMore())
         {
            NameClassPair ncp = (NameClassPair)nce.next();
            strings.addElement(ncp.getName());
         }
      } catch (NamingException ne)
      {
         return new String[0];
      }
      int count = strings.size();
      String[] rvalue = new String[count];

      for (int i=0; i < count; i++)
         rvalue[i] = (String) strings.elementAt(i);

      return rvalue;
   }

   public String[] list(java.io.FilenameFilter filter)
   {
      String[] all = list();
      if (all.length == 0)
         return all;

      Vector strings = new Vector();
      for (int i=0; i < all.length; i++)
      {
FilenameFilter takes a directory path (java.io.File) and can not be
implemented.
      }

      int count = strings.size();
      String[] rvalue = new String[count];

      for (int i=0; i < count; i++)
         rvalue[i] = strings.elementAt(i);

      return rvalue;
   }

*/

/* *************************************************************************
* File, Directory and Volume common interface implementation
****************************************************************************/

   protected void setStaticInterface(
      StaticAttributeValue sav, 
      int mod_op,
      String attrId,
      Object attrValue)
   {
      try
      {
         Attribute attr = new BasicAttribute(attrId, attrValue);
         switch (mod_op)
         {
            case DirContext.ADD_ATTRIBUTE:
               sav.addAttribute(attr);
               break;
            case DirContext.REMOVE_ATTRIBUTE:
               sav.deleteAttribute(attr);
               break;
            case DirContext.REPLACE_ATTRIBUTE:
               sav.modifyAttribute(attr);
               break;
            default:
               throw new NSIException("mod_op: " + mod_op);
         }
      }
      catch (NamingException ne)
      {
         throw new NSIException(ne.getMessage(), ne);
      }
   }

   /**
    * Returns the DirectoryEntryInformation object associated with this file.
    *
    * @return                    The DirectoryEntryInformation object.
    */
   public DirectoryEntryInformation getDirectoryEntryInformation()
   {
      return (DirectoryEntryInformation)
         attributeValues[DEI_SAV].getStaticInterface();
   }

   /**
    * Modifies the Backend DirectoryEntryInformation object associated with 
    * this directory entry.
    *
    * @param      dei            The DirectoryEntryInformation modify value.
    * @param      mod_op         The modification to preform if the
    *                            attribute is part of the set, valid values
    *                            are:
    *                            DirContext.REPLACE_ATTRIBUTE.
    */
   public void setDirectoryEntryInformation(
      DirectoryEntryInformation dei,
      int mod_op)
   {
      setStaticInterface(
         attributeValues[DEI_SAV], 
         mod_op, 
         attributeValues[DEI_SAV].getID(),
         dei);
   }


   /**
    * Returns the EAEnumerator object associated with this file.
    *
    * @return                    The EAEnumerator object.
    */
   public EAEnumerator getEAEnumerator()
   {
      return (EAEnumerator)
         attributeValues[EA_SAV].getStaticInterface();
   }

   /**
    * Modifies the Backend Extended Attribute object associated with this
    * directory entry.
    *
    * @param      ea             The Extended Attribute modify value.
    * @param      mod_op         The modification to preform if the
    *                            attribute is part of the set, valid values
    *                            are:
    *                            DirContext.ADD_ATTRIBUTE,
    *                            DirContext.REPLACE_ATTRIBUTE.
    */
   public void setExtendedAttribute(
      ExtendedAttribute ea,
      int mod_op)
   {
      setStaticInterface(
         attributeValues[EA_SAV], 
         mod_op, 
         attributeValues[EA_SAV].getID(),
         ea);
   }


   /*
    * Returns the NSInfoEnumerator object associated with this file.
    *
    * @return                    The NSInfoEnumerator object.
   public NSInfoEnumerator getNSInfoEnumerator()
   {
      return (NSInfoEnumerator)
         attributeValues[NSI_SAV].getStaticInterface();
   }
   */

   /**
    * Returns the TrusteeEnumerator object associated with this file.
    *
    * @return                    The TrusteeEnumerator object.
    */
   public TrusteeEnumerator getTrusteeEnumerator()
   {
      return (TrusteeEnumerator)
         attributeValues[TRU_SAV].getStaticInterface();
   }

   /**
    * Modifies the Backend Trustee Attribute object associated with this 
    * directory entry..
    *
    * @param      trustee        The Trustee Attribute modify value.
    * @param      mod_op         The modification to preform if the
    *                            attribute is part of the set, valid values
    *                            are:
    *                            DirContext.ADD_ATTRIBUTE,
    *                            DirContext.DELETE_ATTRIBUTE, and
    *                            DirContext.REPLACE_ATTRIBUTE.
    */
   public void setTrustee(
      Trustee trustee,
      int mod_op)
   {
      setStaticInterface(
         attributeValues[TRU_SAV], 
         mod_op, 
         attributeValues[TRU_SAV].getID(),
         trustee);
   }

   /**
    * Returns the EffectiveRights object associated with this file.
    *
    * @return                    The EffectiveRights object.
    */
   public EffectiveRights getEffectiveRights()
   {
      return (EffectiveRights)
         attributeValues[EFR_SAV].getStaticInterface();
   }

/* **************************************************************************
   DataAccessable interface implementation
****************************************************************************/

   /**
    * Opens an EAAccessor for use with NInputStream and NOutputStream.
    *
    * <p>This is the DataAccessable custom data interface method that opens a
    * subordinate stream.  This method, at this level (DirEntryDirContext) is
    * only used for opening a EA stream.  RandomAccess is not allowed, and
    * will throw a RandomAccessNotSupportedException (which is an extension
    * of IOException).
    *
    * <p>This method returns null if the data selector is not set for
    * Extended attributes.  This method must be overridden in extending
    * classes, in which they will check for the null and throw the
    * appropriate IOException.
    * <p/>
    *
    * @param name                The name of the extended attribute to open.
    *
    * @param type                Tells if the stream should be read/write or
    *                            random access.
    *
    * @param custom              The DataAccessableParameters custom data
    *                            structure
    *
    * @return                    A valid, open EAAccessor instance for the
    *                            parameters described above.
    *
    * @exception  IOException    When an error occurred opening the
    *                            extended attribute.
    *
    * @exception  RandomAccessNotSupportedException   If the type is random
    *                                                 access.
    */

   public DataAccessor openStream(String name, int type, Object custom)
      throws IOException
   {
      if (type == DataAccessor.RANDOM)
         return (null);

      DataAccessableParameters dap = (DataAccessableParameters) custom;
      name = dap.checkParameters(name, environment.getNameSpace());

      // check to make sure that the user is specifying an extended attr.
      if (dap.getDataSelector() != DataAccessableParameters.EA_STREAM_SELECTOR)
         return (null);

      int EAHandle;

      try
      {
      // actually malloc() the EAHandle struct, and store the memory address
      //   in the Java int: 'EAHandle'
      EAHandle = callsService.allocEAHandle();

      // if malloc() failed, bail here
      if (EAHandle == 0)
         throw new FileNotFoundException();

      try
      {
         callsService.openEA(
            0,    // no dirHandle
            fullNodeName,
            name,
            NameSpace.nameToNumber(environment.getNameSpace()),
            EAHandle);  // pass the address of the EAHandle struct
      }
      catch (NSIException nsie)
      {
         // this is a special exception that signals to anyone opening an
         //    EA stream, why it failed.
         callsService.freePtr(EAHandle);
         throw new FileNotFoundException("" + nsie.getCCode());
      }
      catch (SessionException e)
      {
         callsService.freePtr(EAHandle);
         throw new FileNotFoundException();
      }
      }
      catch (SessionException e)
      {
         throw (new IOException (e.getMessage ()));
      }

      return (new EAAccessor(EAHandle, session));
   }


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

   /** @internal
    *
    */
   public String getNameInNamespace ()
      throws NamingException
   {
      return (fullNodeName);
   }


/* **************************************************************************
   Implement ComponentContext
****************************************************************************/

   /**
    * Claims all name components for the file system provider.
    *
    * <p>We handle all name resolution, including resolution of the name
    * of any object to which we federate.  However, that object is then
    * responsible to take care of claiming any other names.
    * </p>
    *
    * @param      name           The multi-component name to resolve.
    * @param      cont           The continuation information.
    * @return                    HeadTail containing all components
    *                            in 'name'.
    */
   protected HeadTail p_parseComponent(Name name, Continuation cont)
   {
      return (new HeadTail(name, null));
   }

   /**
    * Resolves the given single-component name.
    *
    * @param      name           The single-component name to be resolved.
    *                            If 'name' is empty, this method returns
    *                            'this'.
    * @param      cont           The continuation information.
    * @return                    Object bound to this context under 'name'.
    * @exception NamingException When an error occurs resolving the name.
    */
   protected Object c_lookup (Name name, Continuation cont)
      throws NamingException
   {
      if (name.isEmpty())
      {
         cont.setSuccess();
         return getInstance();
      }

      if (name.size() > 0 && name.get(0).length() == 0)
      {
         NamingException ne = new NameNotFoundException();
         cont.setError(this, name);
         throw cont.fillInException(ne);
      }

      Exception rootException = null;
      if (environment.getSupportReferences())
      {
         ReferencePage rp = null;
         Object lock = 
            ExtendedAttributeLock.getInstance(fullUrlName, JNDIEAName);
         synchronized(lock)
         {
            try
            {
               // read named References from ext. attributes
               rp = getReferencePage(name, cont);
            }
            catch (NamingException e)
            {
               throw e;
            }
            finally
            {
               ExtendedAttributeLock.releaseInstance(lock);
            }
         }
         Reference ref;
         Object obj = null;

         // try to find the specified named reference
         if (rp != null && (ref = rp.getReference(name.get(0))) != null)
         {
            try
            {
               // try to create the referenced object
               obj = NamingManager.getObjectInstance(ref,
                     null, null, environment.getEnvironment(false));

               // call setContinue() after getObjectInstance() because an
               //   exception may be thrown
               if (obj instanceof Context || name.size() > 1)
                  cont.setContinue(obj, name.getPrefix(1), this, name.getSuffix(1));
               else
                  cont.setSuccess();

               return (obj);
            }
            catch (Exception e)
            {
               rootException = e;  // if exception, fall through to code below
            }
         }
      } // supports references

      NamingException ne = new NameNotFoundException();
      if (rootException != null)
         ne.setRootCause(rootException);
      cont.setError(this, name);
      throw cont.fillInException(ne);
   }

   /**
    * 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 names of objects bound to this context and their respective
    * class types.
    *
    * @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 named
    *                            References of all bound objects.
    */
   protected NamingEnumeration c_list (Name name, Continuation cont)
      throws NamingException
   {
      if (!name.isEmpty())
      {
         resolveNext(name, cont);
         return (null);
      }

      if (environment.getSupportReferences())
      {
         Object lock = 
            ExtendedAttributeLock.getInstance(fullUrlName, JNDIEAName);
         synchronized(lock)
         {
            try
            {
               // read named References from ext. attributes
               ReferencePage rp = getReferencePage(name, cont);
               if (rp != null)
                  return new 
                     NamedReferenceNCEnumeration(rp.names(), rp.references());
            } catch (NamingException e)
            {
               throw e;
            }finally
            {
               ExtendedAttributeLock.releaseInstance(lock);
            }
         } // synchronized
      } // supportReferences
      return new NamingEnumerator();
   }

   /**
    * Lists all bindings 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 named
    *                            References of all bound objects.
    */
   protected NamingEnumeration c_listBindings (Name name, Continuation cont)
      throws NamingException
   {
      if (!name.isEmpty ())
      {
         resolveNext(name, cont);
         return null;
      }

      if (environment.getSupportReferences())
      {
         Object lock = 
            ExtendedAttributeLock.getInstance(fullUrlName, JNDIEAName);
         synchronized(lock)
         {
            try
            {
               // read named References from ext. attributes
               ReferencePage rp = getReferencePage(name, cont);
               if (rp != null)
                  return new NamedReferenceBindingEnumeration(
                                 rp.names(),
                                 rp.references(), 
                                 getEnvironment());
            } catch (NamingException e)
            {
               throw e;
            }finally
            {
               ExtendedAttributeLock.releaseInstance(lock);
            }
         }
      }
      return new NamingEnumerator();
   }

   /**
    * Binds a Referencable object to this context.
    *
    * @param      name           The single-component name under which
    *                            to bind 'obj'.
    * @param      obj            The Referenceable object which should
    *                            be bound to this context.
    * @param      cont           The continuation information.
    * @exception NamingException When an error occurs binding 'obj' to this
    *                            context.
    * @exception NameAlreadyBoundException When an object is already bound
    *                            to this context under the specified name.
    */
   protected void c_bind (Name name, Object obj, Continuation cont)
      throws NamingException
   {
      if (name.isEmpty ())
      {
         cont.setError (this, name);
         throw cont.fillInException (new InvalidNameException ());
      }

       // check to see if we can store bindings
      if (environment.getSupportReferences())
      {
         Object lock = 
            ExtendedAttributeLock.getInstance(fullUrlName, JNDIEAName);
         synchronized(lock)
         {
            try
            {
               /*
                  call to getReference() belongs before getReferencePage() 
                  because getReference() can throw an exception if 'obj' 
                  cannot be made into a Reference, and in that case, the 
                  ReferencePage should not be read in -- so that execution 
                  time is saved.  
               */
               Reference ref = this.getReference(obj, name, cont);

               // read named References from ext. attributes 
               ReferencePage rp = getReferencePage(name, cont);
               String nameStr = name.toString();

               if (rp != null)
               {
                  // check for object bound under the same name
                  if (rp.getReference(nameStr) != null)
                  {
                     cont.setError (this, name);
                     throw cont.fillInException (new NameAlreadyBoundException ());
                  }

                  // put the new object, replacing any other objects
                  rp.putReference(nameStr, ref);
                  putReferencePage(rp, name, cont);

                  return;
               }
            } // try block
            catch (NamingException e)
            {
               throw e;
            }finally
            {
               ExtendedAttributeLock.releaseInstance(lock);
            }
         } // synchronized
      } // if supportReferences

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

   /**
    * Binds a Referenceable object to this context even if an object is
    * already bound under the specified name.
    *
    * @param      name           The single-component name under which
    *                            to bind 'obj'.
    * @param      obj            The Referenceable object which should
    *                            be bound to this context.
    * @param      cont           The continuation information.
    * @exception NamingException When an error occurs binding 'obj' to this
    *                            context.
    */
   protected void c_rebind (Name name, Object obj, Continuation cont)
      throws NamingException
   {
      if (name.isEmpty ())
      {
         cont.setError (this, name);
         throw cont.fillInException (new InvalidNameException ());
      }

       // check to see if we can store bindings
      if (environment.getSupportReferences())
      {
         Object lock = 
            ExtendedAttributeLock.getInstance(fullUrlName, JNDIEAName);
         synchronized(lock)
         {
            try
            {
               /*
                  call to getReference() belongs before getReferencePage() 
                  because getReference() can throw an exception if 'obj' 
                  cannot be made into a Reference, and in that case, the 
                  ReferencePage should not be read in -- so that execution 
                  time is saved.
               */
               Reference ref = this.getReference (obj, name, cont);

               // read named References from ext. attributes
               ReferencePage rp = getReferencePage (name, cont);

               // check to see if we can store bindings
               if (rp != null)
               {
                  rp.putReference (name.toString (), ref);
                  putReferencePage (rp, name, cont);
                  return;
               }
            } // try block
            catch (NamingException e)
            {
               throw e;
            }finally
            {
               ExtendedAttributeLock.releaseInstance(lock);
            }
         } // synchronized
      } // if supportReferences

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

   /**
    * Unbinds a previously bound object from this context.
    *
    * @param      name           The single-component name specifying
    *                            which binding to delete.
    * @param      cont           The continuation information.
    * @exception NamingException When an error occurs unbinding the object.
    */
   protected void c_unbind (Name name, Continuation cont)
      throws NamingException
   {
      if (name.isEmpty ())
      {
         cont.setError (this, name);
         throw cont.fillInException (new InvalidNameException ());
      }

       // check to see if we can store bindings
      if (environment.getSupportReferences())
      {
         Object lock = 
            ExtendedAttributeLock.getInstance(fullUrlName, JNDIEAName);
         synchronized(lock)
         {
            try
            {
               // read named References from ext. attributes
               ReferencePage rp = getReferencePage (name, cont);

               // check to see if we can store bindings
               if (rp != null)
               {
                  if (rp.removeReference(name.toString()) == null)
                  {
                     cont.setError(this, name);
                     throw cont.fillInException(new NameNotFoundException());
                  }
                  putReferencePage (rp, name, cont);
                  return;
               }
            } // try block
            catch (NamingException e)
            {
               throw e;
            }finally
            {
               ExtendedAttributeLock.releaseInstance(lock);
            }
         } // synchronized
      } // if supportReferences

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

   /**
    * Renames a binding in this context.
    *
    * @param      oldname        The single-component name specifying
    *                            which binding to rename.
    * @param      newname        The single-component name specifying the
    *                            new name of the binding.
    * @param      cont           The continuation information.
    * @exception NamingException When an error occurs renaming 'oldname' to
    *                            'newname'.
    * @exception NameNotFoundException When the binding specified by
    *                            'oldname' is not present.
    * @exception NameAlreadyBoundException When an object is already bound
    *                            to this context under the new name.
    */
   protected void c_rename (Name oldname, Name newname, Continuation cont)
      throws NamingException
   {
      if (oldname.isEmpty ())
      {
         cont.setError (this, oldname);
         throw cont.fillInException (new InvalidNameException ());
      }

       // check to see if we can store bindings
      if (environment.getSupportReferences())
      {
         Object lock = 
            ExtendedAttributeLock.getInstance(fullUrlName, JNDIEAName);
         synchronized(lock)
         {
            try
            {
               // read named References from ext. attributes
               ReferencePage rp = getReferencePage (oldname, cont);
               String oldNameStr = oldname.toString ();

               // check to see if we can store bindings
               if (rp != null)
               {
                  if (rp.getReference(oldNameStr) == null)
                  {
                     cont.setError(this, oldname);
                     throw cont.fillInException(new NameNotFoundException());
                  }

                  String newNameStr = newname.toString();

                  // check to see that the new name does not exist
                  if (rp.getReference(newNameStr) != null)
                  {
                     cont.setError(this, newname);
                     throw cont.fillInException(new NameAlreadyBoundException());
                  }

                  Reference ref = rp.removeReference(oldNameStr);

                  rp.putReference(newNameStr, ref);

                  putReferencePage(rp, oldname, cont);
                  return;
               }
            } // try block
            catch (NamingException e)
            {
               throw e;
            }finally
            {
               ExtendedAttributeLock.releaseInstance(lock);
            }
         } // synchronized
      } // if supportReferences

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

   // not implemented by default (DirectoryDirContext overrides this)
   /**
    * Destroys a sub-context in the NetWare file system naming system.
    *
    * @see        DirectoryDirContext#c_destroySubcontext
    *
    * @param      name           The single-component name specifying
    *                            which subcontext to destroy.  Binding
    *                            names do not apply here, unless they
    *                            are actually file system objects with
    *                            a native binding to this context.
    * @param      cont           The continuation information.
    * @exception OperationNotSupportedException Always throws this exception.
    */
   protected void c_destroySubcontext(Name name, Continuation cont)
      throws NamingException
   {
      cont.setError(this, name);
      throw cont.fillInException(new OperationNotSupportedException());
   }

   /**
    * Creates a subcontext under this context in the NetWare file sytem
    * naming system.
    *
    * @param      name           The single-component name of subcontext
    *                            to create.
    * @param      cont           The continuation information.
    * @return                    The newly created subcontext.
    * @exception OperationNotSupportedException Always throws this exception.
    *
    * @see DirectoryDirContext#c_createSubcontext(Name,AttributeSet
    */
   protected Context c_createSubcontext(Name name, 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           The name should be empty coming from the
    *                            tookit.
    * @param      cont           The continuation information.
    * @return                    The 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(environment.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
****************************************************************************/

   /**
    * Binds name to object in this context and adds the given attributes.
    *
    * <p>Binds 'name' to the object 'obj' and associate the attributes
    * 'attrs' with the named object.
    * </p>
    *
    * @param      name           The name to bind. It cannot be empty.
    * @param      obj            The object to bind.
    * @param      attrs          The attributes to associate with the bound
    *                            object.
    * @exception NameAlreadyBoundException If name is already bound.
    * @exception NamingException When an error occurs binding the object
    *                            or associating the attributes with the
    *                            bound object.
    */
   protected void c_bind(
      Name name,
      Object obj,
      Attributes attrs,
      Continuation cont)
      throws NamingException
   {
      if (name.isEmpty())
      {
         cont.setError(this, name);
         throw cont.fillInException(new InvalidNameException());
      }

      if (obj instanceof DirContext)
      {
         c_bind(name, obj, cont);
         try
         {
            Name n = 
               new FileSystemNameParser(environment.getServerName(), false).
               parse("");

            ((DirContext)obj).modifyAttributes(
                              n, DirContext.REPLACE_ATTRIBUTE, attrs);
         } catch (NamingException ne)
         {
            c_unbind(name, cont);
            cont.setError(this, name);
            throw ne;
         }
      }else
      {
         cont.setError(this, name);
         throw cont.fillInException(new OperationNotSupportedException());
      }
      cont.setSuccess();
   }

   /**
    * Binds name to object in this context, overriding any existing bindings,
    * and adds the given attributes.
    *
    * <p>Binds 'name' to the object 'obj' and associates the attributes
    * 'attrs' with the named object.  If 'name' is already bound, 'obj'
    * overwrites the existing binding.  If 'attrs' is null, any existing
    * attributes remain unchanged.  Otherwise, the existing attributes are
    * removed and attrs is associated with the named object.
    *
    * @param      name           The name to bind 'obj' to. It cannot be
    *                            empty.
    * @param      obj            The object to bind.
    * @param      attrs          The attributes to associate with the bound
    *                            object.
    * @exception NamingException When an error occurs binding the object
    *                            or associating the attributes with the
    *                            bound object.
    */
   protected void c_rebind(
      Name name,
      Object obj,
      Attributes attrs,
      Continuation cont)
      throws NamingException
   {
      if (name.isEmpty())
      {
         cont.setError(this, name);
         throw cont.fillInException(new InvalidNameException());
      }

      if (obj instanceof DirContext)
      {
         Object currentObj = null;
         try
         {
            currentObj = c_lookup(name, cont);
         } catch (NamingException ne)
         {
            currentObj = null;
         }
         c_rebind(name, obj, cont);
         try
         {
            Name n = 
               new FileSystemNameParser(environment.getServerName(), false).
               parse("");

            ((DirContext)obj).modifyAttributes(
                              n, DirContext.REPLACE_ATTRIBUTE, attrs);
         } catch (NamingException ne)
         {
            if (currentObj == null)
               c_unbind(name, cont);
            else
               c_rebind(name, currentObj, cont);

            cont.setError(this, name);
            throw ne;
         }
      }else
      {
         cont.setError(this, name);
         throw cont.fillInException(new OperationNotSupportedException());
      }
      cont.setSuccess();
   }

   /**
    * Create new subcontext, and attach the given attributes.
    *
    * <p>Creates a new subcontext with the given name, binds it in this
    * context, and associates the provided attributes with the newly
    * created object.
    * </p>
    *
    * @param      name           The name to bind. It cannot be empty.
    * @param      obj            The object to bind.
    * @param      attrs          The attributes to associate with the bound
    *                            object.
    * @return                    The newly created context.
    * @exception NameAlreadyBoundException If 'name' is already bound.
    */
   protected DirContext c_createSubcontext(
      Name name,
      Attributes attrs,
      Continuation cont)
      throws NamingException
   {
      cont.setError(this, name);
      throw cont.fillInException(new OperationNotSupportedException());
   }

   /**
    * Generic search.  If there is a set of matchingAttrs, this converts
    * the attribute set in matchingAttrs into a
    * COM.novell.jndi.naming.directory.SearchFilter which is then
    * accessed in order to call the overloaded version of search that takes
    * a search filter string and array of arg's.
    * If matchingAttrs is empty, it returns itself and all subcontexts.
    */
   protected NamingEnumeration c_search(
         Name name,
         Attributes matchingAttrs,
         String[] returnAttrs,
         Continuation cont)
      throws NamingException
   {
      if (!name.isEmpty())
      {
         resolveNext(name, cont);
         return null;
      }

      if (matchingAttrs == null || matchingAttrs.size() == 0)
      {
         // Return this object and all subcontexts.
         Vector searchResults = new Vector();

         // Add all bindings
         NamingEnumeration be = listBindings("");
         while(be.hasMoreElements())
         {
            // Get next binding
            Binding binding = (Binding)be.next();
            DirContext dsCtx = (DirContext)binding.getObject();

            // Get this binding's attrs
            Attributes attrs = dsCtx.getAttributes("", returnAttrs);

            // Add this binding
            searchResults.addElement(new SearchResult(
                                          binding.getName(),
                                          binding.getClassName(),
                                          null, //dsCtx,
                                          attrs,
                                          true));
         }
         cont.setSuccess();
         return new SearchEnumerator(searchResults);
      }
      else
      {
         // Valid attribute set.  Convert to filter and use filter to search
         SearchFilterFactory sf = new SearchFilterFactory(matchingAttrs);
         SearchControls constraints = new SearchControls();
         constraints.setReturningAttributes(returnAttrs);
        return c_search (
            new CompoundName("", new Properties()),
            sf.getExpression(),
            sf.getArgs(),
            constraints,
            cont);
      }
   }

   /**
    * Generic search. This search will first examine this object, then
    * search all children.  This is a depth first search.
    */
   protected NamingEnumeration c_search(
         Name name,
         String filterExpr,
         Object[] filterArgs,
         SearchControls cons,
         Continuation cont)
      throws NamingException
   {
      if (!name.isEmpty())
      {
         resolveNext(name, cont);
         return null;
      }

      if (cons == null)
         cons = new SearchControls();

      int timeLim = cons.getTimeLimit();
      long timeEnd = 0;
      long timeLeft = 0;
      boolean timed = false;
      long countLim = cons.getCountLimit();
      boolean sized = countLim == 0 ? false : true;
      int scope = cons.getSearchScope();

      if (timeLim != 0)
      {
         timeEnd = System.currentTimeMillis() + timeLim;
         timed = true;
      }

      // Parse the filter expression.
      Rfc1960Parser ssp;
      try
      {
        ssp = new Rfc1960Parser(filterExpr);
      }
      catch (IllegalArgumentException e)
      {
         cont.setError(this, name);
         InvalidSearchFilterException ne =
               new InvalidSearchFilterException();
         ne.setRootCause(e);
         throw cont.fillInException(ne);
      }

      // Search this context
      Vector searchResults = new Vector();
      try
      {
         SearchResult sr = searchObject(
                              ssp,
                              cons,
                              filterArgs);
         if (sr != null)
         {
            searchResults.addElement(sr);
         }

         // Bail out if OBJECT_SCOPE
         if (scope == SearchControls.OBJECT_SCOPE)
         {
            return new SearchEnumerator(searchResults);
         }
      }
      catch (NamingException ne)
      {
         cont.setError(this, name);
         throw cont.fillInException(ne);
      }

      // Construct new constraints based on those passed in.
      // Change scope to OBJECT_SCOPE if ONELEVEL_SCOPE is passed in.
      SearchControls constraints = new SearchControls(
                                 scope == SearchControls.ONELEVEL_SCOPE ?
                                          SearchControls.OBJECT_SCOPE :
                                          scope,
                                 countLim,
                                 timeLim,
                                 cons.getReturningAttributes(),
                                 cons.getReturningObjFlag(),
                                 cons.getDerefLinkFlag());

      // Search bindings.
      NamingEnumeration be = listBindings("");
      while (be.hasMoreElements())
      {
         // Check and update constraints.
         if (timed)
         {
            if ((timeLeft = (timeEnd - System.currentTimeMillis())) <= 0)
            {
               // Time limit has been reached.  Bail out.
               break;
            }
            constraints.setTimeLimit((int)timeLeft);
         }
         if (sized)
         {
            if (countLim <= 0)
            {
               // Count limit has been reached.  Bail out.
               break;
            }
            constraints.setCountLimit(countLim);
         }

         // Get next binding
         Binding binding = (Binding) be.next();
         Object bindObj = binding.getObject();
         if (!(bindObj instanceof DirContext))
            continue;
            
         String bindingName = binding.getName();
         DirContext dsCtx = (DirContext)bindObj;

         NamingEnumeration se;
         try
         {
            // Search this binding and add all SearchResults to vector
            se = dsCtx.search ("", filterExpr, filterArgs, constraints);
         }
         catch (NamingException ne)
         {
            cont.setError(this, name);
            throw cont.fillInException(ne);
         }

         while (se.hasMoreElements())
         {
            SearchResult sr = (SearchResult) se.next();

            String newName = sr.getName();
            if (newName.length() == 0)
               newName = bindingName;
            else
               newName = dsCtx.composeName(
                                       newName,
                                       bindingName);

            sr.setName(newName);
            searchResults.addElement (sr);
            // Reduce count limit.
            if (sized)
            {
               countLim--;
            }
         }
      }

      cont.setSuccess();
      return new SearchEnumerator(searchResults);
   }

   /**
    *
    */
   protected NamingEnumeration c_search(
         Name name,
         String filter,
         SearchControls cons,
         Continuation cont)
      throws NamingException
   {
      SearchControls c = (cons == null ? new SearchControls() : cons);

      return c_search(name, filter, null, c, cont);
   }

   /**
    * 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 ContextFactoryImpl.getContextInstance(
               this, 
               null, 
               environment,
               ContextFactoryImpl.CREATE_SCHEMA_CONTEXT);
   }

   /**
    * Get the schema class definition context.
    *
    * <p>Returns the schema class definition for this file system context.
    * </p>
    *
    * @param      name           The name that should ould 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;
      }

      String bindingName;

      if (this instanceof VolumeDirContext)
         bindingName = SchemaClassDef.VOLUME_DIRCONTEXT;
      else if (this instanceof DirectoryDirContext)
         bindingName = SchemaClassDef.DIRECTORY_DIRCONTEXT;
      else if (this instanceof FileDirContext)
         bindingName = SchemaClassDef.FILE_DIRCONTEXT;
      else
      {
         cont.setError (this, name);
         throw (cont.fillInException (new NamingException ()));
      }

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

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

      cont.setSuccess ();
      return (root);

   } /* c_getSchemaClassDefinition() */

/* **************************************************************************
   implement Referenceable
****************************************************************************/

   /**
    * Retrieve a Reference for this context.
    *
    * @return                    Reference that contains all information
    *                            on how to locate this context once again.
    * @see        ReferenceFactoryImpl#createReference(DirEntryDirContext
    * @exception NamingException when an error occurs generating a reference
    *                            for this context.
    */
   public Reference getReference ()
      throws NamingException
   {
         return (new ReferenceFactoryImpl().createReference(this));
   }

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

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

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

/* **************************************************************************
   protected methods
****************************************************************************/

   /**
    * StaticAttributeValue interface helper method for modifyAttributes.
    *
    * <p>This method will see if the attrId of attr is found in the
    * attrValues set by calling the getAttributeValue helper method.  If the
    * attrId is part of the set, the appropriate modify will be attempted on
    * the attrId's value.
    * </p>
    *
    * @param      attr           The attribute to find and modify
    * @param      mod_op         The modification to preform if the
    *                            attribute is part of the set, valid values
    *                            are:
    *                            DirContext.ADD_ATTRIBUTE,
    *                            DirContext.DELETE_ATTRIBUTE, and
    *                            DirContext.REPLACE_ATTRIBUTE.
    * @param      attrValues     The context's attributeValues array to
    *                            check for the attribute ID in.
    * @param      name           Used for throwing a clean exception.
    * @param      cont           Used for throwing a clean exception.
    * @return                    true if the attribute ID was found in the
    *                            given set and modified.  false otherwise.
    * @exception NamingException When an error occured performing the
    *                            modify operation.
    *
    * @see DirectoryDirContext#c_modifyAttributes
    * @see FileDirContext#c_modifyAttributes
    */
   protected boolean modifyAttributes(
      Attribute attr,
      int mod_op,
      StaticAttributeValue[] attrValues,
      Name name,
      Continuation cont)
      throws NamingException
   {
      StaticAttributeValue value = getAttributeValue(
                                      attr.getID(),
                                      attrValues);
      if (value != null)
      {
         try
         {
            switch (mod_op)
            {
               case DirContext.ADD_ATTRIBUTE:
                  value.addAttribute(attr);
                  break;
               case DirContext.REMOVE_ATTRIBUTE:
                  value.deleteAttribute(attr);
                  break;
               case DirContext.REPLACE_ATTRIBUTE:
                  value.modifyAttribute(attr);
                  break;
               default:
                  cont.setError(this, name);
                  throw cont.fillInException(
                                 new
                                 AttributeModificationException("" + mod_op));
            }
         }
         catch (NamingException ne)
         {
            cont.setError(this, name);
            throw cont.fillInException(ne);
         }
         return true;
      }
      return false;
   }

   /**
    * StaticAttributeValue interface helper method for getAttributes.
    *
    * <p>This method will add all of the given attrValues attributes to the
    * given attribute set if the attrIds array is null, otherwise it will
    * attempt to find all of the attrIds that are in the given set and return
    * them.  This method uses the getAttributeById helper method to do this.
    * </p>
    *
    * @param      as             Attribute set to add attributes to.
    * @param      attrIds        The array of attribute IDs that should be
    *                            added to the as parameter if they are found
    *                            in the attrValues set.  If this value is
    *                            null, all attributes are added.
    * @param      attrValues     The contexts attributeValues array to
    *                            check for the attribute ID in.
    * @exception NamingException When an error occurs adding the given
    *                            attributes.
    *
    * @see DirectoryDirContext#c_getAttributes
    * @see FileDirContext#c_getAttributes
    */
   protected void addAttributes(
      Attributes as,
      String[] attrIds,
      StaticAttributeValue[] attrValues)
      throws NamingException
   {
      if (attrIds == null)
      {
         // add the WHOLE attribute set.
         for (int i=0; i < attrValues.length; i++)
            as.put(attrValues[i].buildAttribute());
      }
      else
      {
         // this is a subset of the WHOLE attribute set.
         for (int i = 0; i < attrIds.length; i++)
         {
            /*
               the jndi spec says that the attrIds array may be empty
               so check the various strings, if null, do nothing.
            */
            if (attrIds[i] != null)
               addAttributeById(as, attrIds[i], attrValues);
         }

      }
   }

   /**
    * StaticAttributeValue interface helper for getting attribute values.
    *
    * <p>This method looks for the given attribute ID in the given attribute
    * values set and returns the attribute value base that can be used to
    * obtain an attribute of the desired type, if it is found.
    * </p>
    *
    * @param      attrId         Attribute ID to return attribute value
    *                            base for.
    * @param      attrValues     The contexts attributeValues array to
    *                            check for the attribute ID in.
    *
    * @see DirEntryDirContext#modifyAttributes
    * @see DirEntryDirContext#addAttributeById
    */
   protected StaticAttributeValue getAttributeValue(
      String attrId,
      StaticAttributeValue[] attrValues)
   {
      if (attrValues == null)
         return null;

      for (int i=0; i < attrValues.length; i++)
      {
        if (attrId.equalsIgnoreCase(attrValues[i].getID()))
         return(attrValues[i]);
      }
      return(null);
   }

   /**
    * StaticAttributeValue interface helper for building attribute sets.
    *
    * <p>This method looks for the given attribute ID in the given attribute
    * values set and adds the attribute value to the given attribute set if
    * found.
    * </p>
    *
    * @param      as             Attribute set to add attributes to.
    * @param      attrId         Attribute ID of attribute value to be
    *                            added to the attribute set.
    * @param      attrValues     The contexts attributeValues array to
    *                            check for the attribute ID in.
    * @exception NamingException When an attribute cannot be added.
    *
    * @see com.novell.service.file.nw.naming.DirEntryDirContext#addAttributes
    */
   protected void addAttributeById(
      Attributes as, String attrId,
      StaticAttributeValue[] attrValues)
      throws NamingException
   {
      StaticAttributeValue value = getAttributeValue(attrId, attrValues);
      if (value != null)
      {
         as.put(value.buildAttribute());
      }
   }

   /**
    * Helper method for c_search.
    *
    * <p>This method simply breaks up a long, complex piece of code into two
    * pieces, for easier maintenance.  The c_search method will deal with
    * recursion, and when to stop.  This method will deal with obtaining
    * the attribute values and doing the compares that are needed.
    * </p>
    *
    * @param      ssp            The search string parser to use for
    *                            obtaining the needed attribute id's, compare
    *                            operations and operands.
    * @param      returnAttrs    The attributes that should be returned
    *                            with any contexts that match.
    * @param      filter         The search filter that contains the
    *                            attribute values to compare on replacement
    * @param      returnEnum     The SearchEnumeration to store all
    *                            matched results in.
    * @return                    true when a result was added to the
    *                            returnEnum false otherwise.
    * @exception NamingException When an error occurs while searching.
    */
   protected SearchResult searchObject(
      Rfc1960Parser ssp,
      SearchControls cons,
      Object[] filterArgs)
      throws NamingException
   {
      Attributes nodesAs = null;
      try
      {
         // get all the attributes for the given context
         nodesAs = getAttributes("");

         // Compare each search string
         while (ssp.hasMoreElements())
         {
            SearchStringComponent comp = ssp.next();
            String attrId = comp.getAttributeId();

            /*
               this is hard coded to a single attribute value.  If the
               single attribute value is an enumerator of multi-attribute
               values you are ok.  If you have a multiple valued attribute
               and the attribute value coming from getSubsAttributeValue is
               not an enumerator of all possible values, you are broken here
            */
            StaticAttributeValue value = getSubsAttributeValue(attrId);

            if (value != null)
            {
               if (comp.operandReplacement())
                  comp.setReplacementObject(
                           filterArgs[comp.getReplacementIndex()]);

               ssp.setCompareResult(comp, value.compare(comp));
            }else
               ssp.setCompareResult(comp, false);
         }

         if (!ssp.compared())
            return null;

      }catch (InvalidSearchFilterException isfe)
      {
         throw isfe;
      }
      catch (NamingException ne)
      {
         // do nothing, assume search not found and return
         return null;
      }

      // if you get to here, they all compare

      String[] returnAttrs = cons.getReturningAttributes();

      if (returnAttrs != null)
      {
         // get them their requested attrIds from this context
         nodesAs = getAttributes("", returnAttrs);
      }
      // else they get the whole set of attributes for this context

      return new SearchResult(
                  "",
                  cons.getReturningObjFlag() ? getInstance() : null,
                  nodesAs,
                  true);
   }

   /**
    * Checks existence of an explicitly bound object under the specified
    * name.
    *
    * @param      name           The relative atomic name for which to
    *                            check.
    * @param      cont           The continuation information is passed
    *                            so that we can throw a NamingException
    *                            in this method.
    * @return                    true if the name is existant, false if not.
    * @exception NamingException When an error occurs while getting the
    *                            bound references.
    */
   protected boolean isReferenceBound (Name name, Continuation cont)
      throws NamingException
   {
      if (environment.getSupportReferences())
      {
         Object lock = 
            ExtendedAttributeLock.getInstance(fullUrlName, JNDIEAName);
         synchronized(lock)
         {
            try
            {
               ReferencePage refPage = getReferencePage (name, cont);

               return(refPage != null &&
                      refPage.getReference(name.toString()) != null);
            } catch (NamingException e)
            {
               throw e;
            }finally
            {
               ExtendedAttributeLock.releaseInstance(lock);
            }
         } // synchronized
      } // supportReferences
      return false;
   }

   /**
    * Gets all the named references for this context.
    *
    * <p>The reference page is read from the extended attribute
    * '__JNDIReferences'.
    * </p>
    *
    * @param      name           The name is passed so that we can throw a
    *                            NamingException exception in this method.
    * @param      cont           The continuation information is passed so
    *                            that we can throw a NamingException
    *                            in this method.
    * @return                    A reference page containing all named
    *                            references of ojbects bound to this context.
    *                            Returns null if no reference page object
    *                            can be constructed (when we have no rights,
    *                            and in similar circumstances).
    * @exception NamingException When an error occurs while getting the
    *                            reference page.
    */
   protected ReferencePage getReferencePage (Name name, Continuation cont)
      throws NamingException
   {
      // Returning null here signals any callers that reading references
      //   is disabled -- this allows the different methods (list, bind,
      //   etc.) to handle the unavailable state as they wish.
//      if (environment.getSupportReferences() == false)
//         return (null);

      Exception e = null;
      InputStream is = null;  // is only non-null on successful EA open
      boolean open = false;   // only set to true between successful EA open
                              //   and successful EA close

      try
      {
         // open the ext. attr stream on this context
         is = new NFileInputStream(
                        DirEntryDirContext.JNDIEAName,
                        this,
                        0,
                        DataAccessableParameters.EA_STREAM_SELECTOR);

         open = true;   // signals successful EA open
         
         ReferencePage rp = new ReferencePageImpl (is);

         is.close ();

         open = false;  // signals successful EA close

         return (rp);
      }
      catch (ClassNotFoundException e1)
      {
         e = e1;
      }
      catch (IOException e2)
      {
         // if after successful EA open, an exception is thrown, then we
         //   need to clean up the EA stream so that we don't cause a
         //   memory leak
         if (open)
         {
            try
            {
               is.close ();
            }
            catch (IOException ignore) // ignore and take 'e2' as exception
            {
            }
         }

         // check for special exception that contains a string that
         //   tells the error code, so that we can detect harmless errors.
         String msg = e2.getMessage ();
         int ccode = 0;

         if (msg != null)
         {
            try
            {
               ccode = Integer.parseInt (msg);
            }
            catch (NumberFormatException e3)
            {
               msg = null;
            }
         }

         // 0xC9 is returned if the named EA does not exist
         // 0x898C is returned if you do not have modify priveleges
         //   on the dir entry which you are trying to reference
         //   I guess read priveleges won't do...
         // Don't throw exceptions for these error codes
         if (msg != null &&
             (ccode == DirEntryDirContext.NWE_NO_SUCH_EA ||
              ccode == ServerErrors.NWE_FILE_NO_MOD_PRIV))
         {
            return (new ReferencePageImpl ());
         }

         // 0x8989 is returned when the user has no permissions to read EAs.
         // Throwing NoPermissionException here signals any callers that
         //   there is no permission -- this allows the different methods
         //   (list, bind, etc.) to handle the no permission state
         //   as they wish.
         if (msg != null && ccode == ServerErrors.NWE_FILE_NO_SRCH_PRIV)
         {
            NamingException ne = new NoPermissionException();
            ne.setRootCause(e2);
            cont.setError(this, name);
            throw cont.fillInException(ne);
         }

         e = e2;
      }

      // if an exception was thrown, rethrow a NamingException
      if (e != null)
      {
         NamingException ne = new NamingException ();

         ne.setRootCause (e);
         cont.setError (this, name);

         throw cont.fillInException (ne);
      }

      // this code is unreachable, but the compiler complained...
      return (null);
   }

   /**
    * Writes all the named references for this context to the extended
    * attributes.
    *
    * <p>The reference page is written to the extended attribute
    * '__JNDIReferences'.
    * </p>
    *
    * @param      rp             The reference page containing all named
    *                            references to objects bound into this
    *                            context.
    * @param      name           The name is passed so that we can throw a
    *                            NamingException exception in this method.
    * @param      cont           The continuation information is passed so
    *                            that we can throw a NamingException in
    *                            this method.
    * @return                    A reference page containing all named
    *                            references to ojbects bound to this context.
    * @exception NamingException When an error occurs while writing the
    *                            reference page.
    */
   protected void putReferencePage (ReferencePage rp, Name name,
         Continuation cont)
      throws NamingException
   {
      OutputStream os = null;
      boolean open = false;

      try
      {
         // open the ext. attr stream on this context
         os = new NFileOutputStream(
                        DirEntryDirContext.JNDIEAName,
                        this,
                        0,
                        DataAccessableParameters.EA_STREAM_SELECTOR);

         open = true;

         rp.setDestination (os);
         rp.flush ();
         os.close ();

         open = false;
      }
      // if an exception was thrown, rethrow a NamingException
      catch (IOException e)
      {
         if (open)
         {
            try
            {
               os.close();
            }
            catch (IOException ignore)
            {
            }
         }

         NamingException ne = new NamingException ();

         ne.setRootCause (e);
         cont.setError (this, name);

         throw cont.fillInException (ne);
      }
   }

   /**
    * Returns an instance of this object through the factory
    */
   protected Object getInstance()
      throws NamingException
   {
/*
      NetDirContextFactory netFactory = new NetDirContextFactory ();
      NdsDirContextFactory ndsFactory = new NdsDirContextFactory ();
      Object object;

      object = netFactory.createWrappableDirContext (
                           this.name.toString (),
                           netEnvironment,
                           baseClass);

      object = ndsFactory.createNdsContext (
                           (DirContext)object,
                           ((WrappableDirContext)object).getBaseClass ());
*/
      return this;
   }

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

   /**
    * Returns the atomic name of this dir entry.
    *
    * @return                    The atomic name of this dir entry.
    */
   public String getName ()
   {
      return (atomicName);
   }

   /**
    * Returns the full natively-formatted name of the dir entry.
    *
    * @return                    The full native vol/path name of the dir
    *                            entry.
    */
   public String getFullNodeName ()
   {
      return (fullNodeName);
   }

   public String getFullUrlName()
   {
      return (fullUrlName);
   }

   /**
    * Returns parent directory of this dir entry.
    *
    * @return                    The parent directory of this dir entry.
    *                            If this method returns null, this dir
    *                            entry is a VolumeDirContext.
    */
   public DirectoryDirContext getDirectory ()
   {
      return (dir);
   }

   /**
    * Takes an object, and if the object is a Reference, or if it is
    * Referenceable, this method returns the Reference.
    *
    * @param      obj            The object to process.
    * @param      name           The name passed so that we can throw a
    *                            NamingException exception in this method.
    * @param      cont           The continuation information is passed so
    *                            that we can throw a NamingException in
    *                            this method.
    * @return                    The reference associated with 'obj'.
    * @exception NamingException when 'obj' is not a Reference or
    *                            Referenceable.
    */
   protected Reference getReference (Object obj, Name name,
         Continuation cont)
      throws NamingException
   {
      Reference ref;

      if (obj instanceof Reference)
      {
         ref = (Reference) obj;
      }
      else if (obj instanceof Referenceable)
      {
         ref = ((Referenceable) obj).getReference ();
      }
      else
      {
//NOTE: May need to throw another type of exception here
         cont.setError (this, name);
         throw cont.fillInException (new NamingException ());
      }

      return (ref);
   }
   // searchObject helper method

   protected abstract StaticAttributeValue getSubsAttributeValue(String attrId);

   protected void setupDirEntryStaticAttributeValues()
      throws NSIException
   {
      attributeValues = new StaticAttributeValue[4];
      attributeValues[DEI_SAV] = new DirectoryEntryInfoImpl(
                                       environment);

      attributeValues[EA_SAV] = new EAEnumeratorImpl(
                                       environment,
                                       this);

      attributeValues[TRU_SAV] = new TrusteeEnumeratorImpl(
                                       environment);

      attributeValues[EFR_SAV] = new EffectiveRightsImpl(
                                       environment);
   }

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


/**
 * The SearchEnumeration that is returned from all searchs.
 *
 * <p>The various search methods will use this class to instantiate and set
 * up the SearchEnumeration results that will be returned to the caller.
 * </p>
 *
 * @see com.novell.service.file.nw.naming.DirEntryDirContext#c_search
 */

class DirEntrySearchEnumerator implements NamingEnumeration
{
   private Vector store = null;
   private int index;

   public DirEntrySearchEnumerator()
   {
      store = new Vector();
      index = -1;
   }

   public boolean hasMoreElements()
   {
      return (index >= 0);
   }

   public boolean hasMore()
      throws NamingException
   {
      return (index >= 0);
   }

   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 (index < 0)
         throw new NoSuchElementException();
      return (SearchResult) store.elementAt(index--);
   }

   protected void add(SearchResult data)
   {
      store.addElement(data);
      ++index;
   }

   public void close() throws NamingException
   {
      index = -1;
   }
} /* DirEntrySearchEnumerator */


/**
 * Enumerates the named References as NameClassPairs.
 */
class NamedReferenceNCEnumeration implements NamingEnumeration
{
   Enumeration names;
   ReferenceEnumeration refs;

   public NamedReferenceNCEnumeration (Enumeration names,
         ReferenceEnumeration refs)
   {
      this.names = names;
      this.refs = refs;
   }

   public boolean hasMoreElements()
   {
      return names.hasMoreElements();
   }

   public boolean hasMore()
      throws NamingException
   {
      return names.hasMoreElements();
   }

   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 (!hasMore())
         throw new NoSuchElementException();

      String name = (String)names.nextElement();
      Reference ref = refs.next();

      return (new NameClassPair (name, ref.getClassName(), true));
   }

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


/**
 * Enumerates the named References as Bindings.
 */
class NamedReferenceBindingEnumeration implements NamingEnumeration
{
   Enumeration names;
   ReferenceEnumeration refs;
   Hashtable env;

   public NamedReferenceBindingEnumeration (Enumeration names,
         ReferenceEnumeration refs, Hashtable env)
   {
      this.names = names;
      this.refs = refs;
      this.env = env;
   }

   public boolean hasMoreElements()
   {
      return names.hasMoreElements();
   }

   public boolean hasMore()
      throws NamingException
   {
      return names.hasMoreElements();
   }

   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
   {
      String name = (String)names.nextElement();
      Reference ref = refs.next();

      try
      {
         Object obj = NamingManager.getObjectInstance(ref, null, null,env);

         return (new Binding(name, obj, true));
      }
      catch (NamingException ne)
      {
         throw ne;
      }
      catch (Exception e)
      {
         NamingException ne = new NamingException(e.getMessage());
         ne.setRootCause(e);
         throw ne;
      }
   }

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