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

  $Archive: /njcl_v2/src/com/novell/service/nds/naming/net/NetDirContext.java $
  $Revision: 65 $
  $Modtime: 8/06/01 10:38a $

  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.nds.naming.net;


import java.io.*;

import java.util.*;

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

import com.sun.jndi.toolkit.ctx.*;

import com.novell.service.jncp.*;
import com.novell.service.jncpv2.clx.ClxJNI;

import com.novell.service.nds.*;
import com.novell.service.nds.naming.*;
import com.novell.service.nds.naming.NdsDirContextWrappable;
import com.novell.service.nds.net.*;
import com.novell.service.nds.ldap.LdapAttributeValueFactory;

import com.novell.service.nds.NdsIterator;
import com.novell.service.nds.NdsIteratorControls;
import com.novell.service.nds.NdsIteratorFactory;

import com.novell.service.session.Session;
import com.novell.service.session.SessionException;
import com.novell.service.jncpv2.net.*;

import com.novell.service.toolkit.jcl.NWInteger;

import com.novell.utility.naming.Environment;
import com.novell.utility.naming.directory.NAttributes;
import com.novell.utility.naming.spi.ContextFactory;
import com.novell.utility.naming.spi.ContextFactoryBuilder;


/** @internal
 *
 */
public class NetDirContext
   extends ComponentDirContext
   implements NdsObject, NdsDataAccessor, NdsDirContextWrappable
{
   protected static NetContextFactory factory;
   protected static SchemaContextFactory schemaFactory;
   protected static PartitionContextFactory partitionFactory;
	protected static NdsNamingExceptionFactory exceptionFactory;

   static
   {
      factory = new NetContextFactory ();
      schemaFactory = new SchemaContextFactory ();
      partitionFactory = new PartitionContextFactory ();
      exceptionFactory = new NdsNamingExceptionFactory ();

   } /* static */

   protected String distinguishedName;
   protected NdsObjectInfo objectInfo;
   protected NetEnvironment environment;

   protected NameParser nameParser;

   /**
    * Constructs an NetDirContext.
    *
    * @param objectName    NDS distinguished name in partial dot form.
    * @param objectInfo    NWNet data structure.
    * @param environment   properties to control context behavior
    *
    * @exception NamingException
    */
   protected NetDirContext (
         String objectName,
         NdsObjectInfo objectInfo,
         NetEnvironment environment)
      throws NamingException
   {
      this.distinguishedName = objectName;
      this.objectInfo = objectInfo;

      this.environment = (NetEnvironment) environment.clone ();

      this.nameParser = new NdsNameParser (getTreeName ());
   }


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

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

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

   } /* addToEnvironment () */


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

   } /* removeFromEnvironment () */

   /**
    *
    */
   public Hashtable getEnvironment ()
      throws NamingException
   {
      return (environment.getEnvironment ());

   } /* getEnvironment () */

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


   // ******************** ComponentDirContext overrides ********************

   /**
    * Determines which of the first components of 'name' belong
    * to this naming system.
    * The default implementation just selects the first component
    * (this is the policy for supporting strong separation).
    * Subclass should override this method according its own policies.
    * For example, a weakly separated system with dynamic boundary
    * determination would simply return as head 'name'.
    * A weakly separated with static boundary
    * determination would select the components in the front of 'name'
    * that conform to some syntax rules.  (e.g. in X.500 syntax, perhaps
    * select front components that have a equal sign).
    */
   protected HeadTail p_parseComponent (
         Name name,
         Continuation cont)
      throws NamingException
   {
      if (name.isEmpty ())
      {
         return (new HeadTail (nameParser.parse(""), name.getSuffix (0)));
      }
      else if (name instanceof CompoundName)
      {
         return (new HeadTail (name, new CompositeName ()));
      }
      else
      {
         Name head;
         Name tail;

         // determine if already at boundary
         if (name.get (0).equals (""))
         {
            head = nameParser.parse ("");
            tail = name.getSuffix (0);
         }
         else
         {
            // head is distinguished name
            head = nameParser.parse (name.get (0));
            tail = name.getSuffix (1);
         }
         return (new HeadTail (head, tail));
      }

   } /* p_parseComponent () */

      // ********** nns operations **********

   /**
    * Resolves the nns for 'name' when the named context is acting
    * as an intermediate context.
    *
    * For a system that supports junctions, this would be equilvalent to
    *      c_lookup(name, cont);
    * because for junctions, an intermediate slash simply signifies
    * a syntactic nns_name.
    *
    * For a system that supports implicit nns, this would be equivalent to
    *    c_lookup_nns (name, cont);
    * because for implicit nns, a slash always signifies the implicit nns,
    * regardless of whether it is intermediate or trailing.
    *
    * Context that implement implicit nns must provide appropriate override.
    */
   protected Object c_resolveIntermediate_nns (
         Name name,
         Continuation cont)
      throws NamingException
   {
      try
      {
         return (c_lookup_nns (name, cont));

         // Do not append "" to Continuation 'cont' even if set
         // because the intention is to ignore the nns
      }
      catch (NamingException e)
      {
         e.appendRemainingComponent (""); // add nns back
         throw (e);
      }

   } /* c_resolveIntermediate_nns () */


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

   /**
    *
    */
   protected  void c_bind (
         Name name,
         Object object,
         Continuation cont)
      throws NamingException
   {
/*
      if ( !(object instanceof Referenceable))
      {
         cont.setError (this, name.toString ());
         throw (cont.fillInException (new NamingException ()));
      }
*/
      if (object instanceof NdsObject)
      {
         NdsObject ndsObject = (NdsObject) object;

         if (getTreeName ().equalsIgnoreCase (ndsObject.getTreeName ()))
         {
            Attributes attrs = new BasicAttributes (true);

            // 1. obj exists inside our tree
            // obj  = Java object representing some existing NDS object.
            //        (aliased object)
            // name = NDS name of alias object.

            try
            {
               attrs.put ("Object Class", new NdsClassName ("Alias"));
               attrs.put (
                        "Aliased Object Name",
                        new NdsDistinguishedName (
                                 ndsObject.getDistinguishedName ()));
            }
            catch (Exception e)
            {
               NamingException ne = new InvalidAttributeValueException ();

               cont.setError (this, name.toString ());
               ne.setRootCause (e);
               throw (cont.fillInException (ne));
            }

            this.createSubcontext (name, attrs);
            return;
         }
         else
         {
            // 2. outside of our tree
            cont.setError (this, name.toString ());
            throw (cont.fillInException (new NamingException ()));
         }
      }

      {
         // 3. not NdsObject
         Attributes attrs = new BasicAttributes ();

         attrs = JavaObject.encodeObject ('#', object, attrs, false);

         this.c_createSubcontext (name, attrs, cont);
         return;
      }

/*
      cont.setError (this, name.toString ());
      throw (cont.fillInException (new NamingException ()));
*/

   } /* c_bind () */

   /**
    * The NDS Naming System will not support this operation.
    * A mandatory set of attributes is required to create any NdsDirContext.
    */
   protected Context c_createSubcontext (
         Name name,
         Continuation cont)
      throws NamingException
   {
      cont.setError (this, name.toString ());
      throw (cont.fillInException (new OperationNotSupportedException ()));

   } /* c_createSubcontext () */

   /**
    *
    */
   protected void c_destroySubcontext (
         Name name,
         Continuation cont)
      throws NamingException
   {
      try
      {
         NdsName target;
         NetService service = environment.getService ();

         target = (NdsName) name.clone ();
         target.addAll (0, getNameInstance ());

         service.removeObject (target.getCanonicalString ());

         cont.setSuccess ();
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);

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

         ne.setRootCause (e);
         cont.setError (this, name.toString ());
         throw (cont.fillInException (ne));
      }

   } /* c_destroySubcontext () */

   /**
    *
    */
   protected NameParser c_getNameParser (
         Name name,
         Continuation cont)
      throws NamingException
   {
      if (!valid_context (name, cont))
      {
         NamingException e = new NameNotFoundException ();

         cont.setError (this, name.toString ());
         throw (cont.fillInException (e));
      }

      cont.setSuccess ();
      return (nameParser);

   } /* c_getNameParser () */

   /**
    *
    */
   protected NamingEnumeration c_list (
         Name name,
         Continuation cont)
      throws NamingException
   {
      NdsName target;

      target = (NdsName) name.clone ();
      target.addAll (0, getNameInstance ());

      return (new NetNameClassPairEnumerator (
                                                target.getCanonicalString (),
                                                environment));

   } /* c_list () */

   /**
    *
    */
   protected NamingEnumeration c_listBindings (
         Name name,
         Continuation cont)
      throws NamingException
   {
      NdsName target;

      target = (NdsName) name.clone ();
      target.addAll (0, getNameInstance ());

      return (new NetBindingEnumerator (
                     target.getCanonicalString (),
                     environment));

   } /* c_listBindings () */

   /**
    * Retrieves the named object.
    */
   protected Object c_lookup (
         Name name,
         Continuation cont)
      throws NamingException
   {
      try
      {
         Object object;

         if (name.isEmpty ())
         {
            object = factory.getContextInstance (
                                 getNameInstance ().getCanonicalString (),
                                 objectInfo,
                                 environment);
         }
         else
         {
            NdsName target;

            target = (NdsName) name.clone ();
            target.addAll (0, getNameInstance ());

            object = factory.getContextInstance (
                                 target.getCanonicalString (),
                                 environment);
         }
/*
         if (object != null && object instanceof LinkRef)
         {
            cont.setContinue (object, this);
            return (null);
         }
*/

         if (object instanceof NetJavaContainerDirContext)
         {
            object = ((NetJavaContainerDirContext) object).decodeObject ();
         }

         cont.setSuccess ();
         return (object);
      }
      catch (NamingException e)
      {
         cont.setError (this, name.toString ());
         throw (cont.fillInException (e));
      }

   } /* c_lookup () */

   /**
    *
    */
   protected Object c_lookupLink (
         Name name,
         Continuation cont)
      throws NamingException
   {
      try
      {
         Context context;
         NetEnvironment environment;

         environment = (NetEnvironment) this.environment.clone ();
         environment.setServiceKey (AliasNetService.KEY);

         if (name.isEmpty ())
         {
            context = factory.getContextInstance (
                                 getNameInstance ().getCanonicalString (),
                                 objectInfo,
                                 environment);
         }
         else
         {
            NdsName target;

            target = (NdsName) name.clone ();
            target.addAll (0, getNameInstance ());

            context = factory.getContextInstance (
                                 target.getCanonicalString (),
                                 environment);
         }
/*
         if (context != null && context instanceof LinkRef)
         {
            cont.setContinue (context, this);
            return (null);
         }
*/
         cont.setSuccess ();
         return (context);
      }
      catch (NamingException e)
      {
         cont.setError (this, name.toString ());
         throw (cont.fillInException (e));
      }

   } /* c_lookupLink () */

   /**
    *
    */
   protected void c_rebind (
         Name name,
         Object object,
         Continuation cont)
      throws NamingException
   {
      try
      {
         // remove the current binding
         c_unbind (name, cont);
      }
      catch (NameNotFoundException e)
      {
         // ignore: rebind defaults to bind behavior
      }

      // create new binding
      c_bind (name, object, cont);

   } /* c_rebind () */

   /**
    * Binds 'newName' to the object bound to 'oldName', and
    * unbinds 'oldName'.  Operation involving 'newName' call
    * the p_*() methods because 'newName' could be an instance
    * of CompositeName.
    */
   protected void c_rename (
         Name oldname,
         Name newname,
         Continuation cont)
      throws NamingException
   {
      int ccode;
      NetService service = environment.getService ();
      // if newname is a composite name, convert to NdsName
      NdsName newNameTarget;
      if (!(newname instanceof NdsName))
      {
         newNameTarget = (NdsName) nameParser.parse (newname.toString ());
      }
      else
      {
         newNameTarget = (NdsName) newname.clone ();
      }
      NdsName oldNameTarget = (NdsName) oldname.clone ();

/* currently c_lookup never returns null
      if (null == context)
      {
         cont.setError (this, oldname);
         throw (cont.fillInException (new NameNotFoundException ()));
      }
*/

/*
   Rename oldname to newname.
   Possible scenarios:
   1) context = initial context and names are composite names
   2) context = intermediate context and names are relative to context

   can we have:
   3) context = initial context and oldname = composite name and
      newname = compound name? (and vice versa)
   4) context = initial context with compound names? (Yes)
   5) context = intermediate context with composite names?
   6) differing composite names; ie. different trees?

   If we are operating off of an initial context, all we need to do is Modify
   the DN and were done. If we are operating off of an intermediate context,
   then we should rebind the JNDI object to make sure our JNDI context stream
   remains consistent.
*/

      if (oldname.isEmpty ())
      {
         NamingException e = new InvalidNameException ();

         cont.setError (this, oldname.toString ());
         throw (cont.fillInException (e));
      }

      if (newname.isEmpty ())
      {
         NamingException e = new InvalidNameException ();

         cont.setError (this, newname.toString ());
         throw (cont.fillInException (e));
      }

      // convert names to fully distinguished, compound names
      oldNameTarget.addAll (0, getNameInstance ());
      newNameTarget.addAll (0, getNameInstance ());

      try
      {
         String oldParent = ((NdsName)oldNameTarget
                                .getPrefix( oldNameTarget.size()-1))
                                   .getTypelessName()
                                        .getCanonicalString();
         String newParent = ((NdsName)newNameTarget
                                .getPrefix( newNameTarget.size()-1))
                                   .getTypelessName()
                                        .getCanonicalString();
         
         if ( oldParent.equalsIgnoreCase( newParent ) )             
         // do the rename through modifyRDN
             service.modifyRDN (
                        oldNameTarget.getCanonicalString(),
                        newNameTarget.getCanonicalString(),
                        true);
         else
         // do the rename through modifyDN
             service.modifyDN (
                        oldNameTarget.getCanonicalString(),
                        newNameTarget.getCanonicalString(),
                        true);

         cont.setSuccess ();
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);

         cont.setError (this, oldNameTarget.getCanonicalString ());
         throw (cont.fillInException (ne));
      }
      catch (SessionException e)
      {
         NamingException ne = new NamingException ();

         ne.setRootCause (e);
         cont.setError (this, oldNameTarget.getCanonicalString ());
         throw (cont.fillInException (ne));
      }

   } /* c_rename () */

   /**
    *
    */
   protected void c_unbind (
         Name name,
         Continuation cont)
      throws NamingException
   {
      int ccode;
      NdsName target;
      NetService service = environment.getService ();

      target = (NdsName) name.clone ();
      target.addAll (0, getNameInstance ());

      try
      {
         service.removeObject (target.getCanonicalString ());

         cont.setSuccess();
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);
			cont.setError (this, name.toString ());
			throw (cont.fillInException (ne));
      }
      catch (SessionException e)
      {
         NamingException ne = new NamingException ();

         ne.setRootCause (e);
         cont.setError (this, name.toString ());
         throw (cont.fillInException (ne));
      }

   } /* c_unbind () */

      // ********** nns operations **********

   /**
    *
    */
   protected void c_bind_nns (
         Name name,
         Object obj,
         Continuation cont)
      throws NamingException
   {
      if (name.isEmpty ())
      {
         NamingException e = new OperationNotSupportedException ();

         cont.setErrorNNS (this, name.toString ());
         throw (cont.fillInException (e));
      }

      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }

   } /* c_bind_nns () */

   protected Context c_createSubcontext_nns (
         Name name,
         Continuation cont)
      throws NamingException
   {
      if (name.isEmpty())
      {
         NamingException e = new OperationNotSupportedException ();

         cont.setErrorNNS (this, name.toString ());
         throw (cont.fillInException (e));
      }

      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }
      return (null);

   }

   protected void c_destroySubcontext_nns (
         Name name,
         Continuation cont)
      throws NamingException
   {
      if (name.isEmpty())
      {
         NamingException e = new OperationNotSupportedException ();

         cont.setErrorNNS (this, name.toString ());
         throw (cont.fillInException (e));
      }

      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }

   }

   protected NameParser c_getNameParser_nns (
         Name name,
         Continuation cont)
      throws NamingException
   {
      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }
      return (null);

   } /* c_getNameParser_nns () */

   protected NamingEnumeration c_list_nns (
         Name name,
         Continuation cont)
      throws NamingException
   {
      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }
      return (null);
   }

   protected NamingEnumeration c_listBindings_nns (
         Name name,
         Continuation cont)
      throws NamingException
   {
      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }
      return (null);
   }

   /**
    *
    */
   protected Object c_lookup_nns (
         Name name,
         Continuation cont)
      throws NamingException
   {
      NetDirContext netDirContext = (NetDirContext) c_lookup (name, cont);

      if (null == netDirContext.getNNSP ())
      {
         cont.setErrorNNS (this, name.toString ());
         throw (cont.fillInException (new NameNotFoundException()));
      }
      cont.setSuccess ();
      return (netDirContext.getNNSP ());

   } /* c_lookup_nns () */

   protected Object c_lookupLink_nns (
         Name name,
         Continuation cont)
      throws NamingException
   {
      return (c_lookup_nns (name, cont));
   }

   /**
    *
    */
   protected void c_rebind_nns (
         Name name,
         Object object,
         Continuation cont)
      throws NamingException
   {
      if (name.isEmpty ())
      {
         NamingException e = new OperationNotSupportedException ();

         cont.setErrorNNS (this, name.toString ());
         throw (cont.fillInException (e));
      }

      object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }

   } /* c_rebind_nns () */

   /**
    *
    */
   protected void c_rename_nns (
         Name oldname,
         Name newname,
         Continuation cont)
      throws NamingException
   {
      if (oldname.isEmpty ())
      {
         NamingException e = new OperationNotSupportedException ();

         cont.setErrorNNS (this, oldname.toString ());
         throw (cont.fillInException (e));
      }

      Object object = c_lookup_nns (oldname, cont);

      if (object != null)
      {
         cont.setContinue (object, oldname, this);
      }

   } /* c_rename_nns () */

   protected void c_unbind_nns (
         Name name,
         Continuation cont)
      throws NamingException
   {
      if (name.isEmpty())
      {
         NamingException e = new OperationNotSupportedException ();

         cont.setErrorNNS (this, name.toString ());
         throw (cont.fillInException (e));
      }

      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }

   }


   // ******************** ComponentDirContext Interface ********************

   /**
    *
    */
   protected void c_bind (
         Name name,
         Object obj,
         Attributes attrs,
         Continuation cont)
      throws NamingException
   {
      if (obj instanceof NetDirContext)
      {
         // 1. obj exists inside our tree

         this.createSubcontext (name, attrs);

         // 2. outside of our tree

      }
      else if (obj instanceof DirContext)
      {
         DirContext dsContext = (DirContext) obj;

         c_bind (name, obj, cont);

         if (null != attrs)
         {
            try
            {
               dsContext.modifyAttributes ("", REPLACE_ATTRIBUTE, attrs);
            }
            catch (NamingException ne)
            {
               c_unbind (name, cont);
               throw ne;
            }
         }
      }
      else if (obj instanceof Referenceable)
      {
         // 3. not NDS object but referencable
      }
      else
      {
         // throw NamingException();
      }

   } /* c_bind () */

   /**
    *
    */
   protected DirContext c_createSubcontext (
         Name name,
         Attributes attrs,
         Continuation cont)
      throws NamingException
   {
      NdsName target;
      NetService service = environment.getService ();
      NetBuffer objectInfo;
      Vector streamAttrVals = new Vector ();

      // create NDS object name
      target = (NdsName) name.clone ();
      target.addAll (0, getNameInstance ());

      try
      {
         NamingEnumeration attributeList = attrs.getAll ();
         NetAttributeValueFactory valueFactory;

         valueFactory = new NetAttributeValueFactory ();
         objectInfo = new NetBuffer (
                              service,
                              NetJNI.DSV_ADD_ENTRY,
                              environment.getBatchSize ());

         // enumerate attributes to create NWNet buffer
         while (attributeList.hasMoreElements ())
         {
            Attribute attribute = (Attribute) attributeList.next ();
            Enumeration values = attribute.getAll ();

            // add NDS attribue name
            objectInfo.putAttributeName (attribute.getID ());

            // enumerate attribute values to create NWNet buffer
            while (values.hasMoreElements ())
            {
               Object object = values.nextElement ();
               NetAttributeValue value;

               value = valueFactory.getValueInstance (object);

               if (value instanceof NetStream)
               {
                  streamAttrVals.addElement (value);
                  streamAttrVals.addElement (attribute);
                  streamAttrVals.addElement (new Integer (ADD_ATTRIBUTE));

                  // insert an empty value
                  objectInfo.putAttributeValue (
                                 value.getNdsSyntaxId (),
                                 null);
               }
               else
               {
                  // add NDS attribute value
                  objectInfo.putAttributeValue (
                                 value.getNdsSyntaxId (),
                                 value.toByte ());
               }
            }
         }
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);

			cont.setError (this, name.toString ());
			throw (cont.fillInException (ne));
      }
      catch (Exception e)
      {
         NamingException ne = new NamingException ();

         cont.setError (this, name.toString ());
         ne.setRootCause (e);
         throw (cont.fillInException (ne));
      }

      try
      {
         // create NDS object
         service.addObject (
                           target.getCanonicalString (),
                           null,
                           false,
                           objectInfo.getHandle ());
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);

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

         ne.setRootCause (e);
         cont.setError (this, target.toString ());
         throw (cont.fillInException (ne));
      }

      transferStreamBytesAfterAttrPut (streamAttrVals, environment,
            target.getCanonicalString ());

      Object ctx = c_lookup (name, cont);

      return ((ctx instanceof DirContext) ? (DirContext) ctx : null);

   } /* c_createSubcontext () */

   /**
    *
    */
   protected Attributes c_getAttributes (
         Name name,
         String[] attributeNames,
         Continuation cont)
      throws NamingException
   {
      NdsName target;

      try
      {
         String level = environment.getObjectAttributeLevel ();
         Attributes attributeSet;

         target = (NdsName) name.clone ();
         target.addAll (0, getNameInstance ());

         if (level.equals (NdsEnvironment.OBJECT_ATTRS))
         {
            attributeSet = new NetAttributes (
                                    target.getCanonicalString (),
                                    environment,
                                    attributeNames);
         }
         else if (level.equals (NdsEnvironment.OBJECT_ATTR_NAMES))
         {
            attributeSet = new NetAttributes (
                                    target.getCanonicalString (),
                                    environment,
                                    attributeNames);
         }
         else if (level.equals (NdsEnvironment.DSI_OBJECT_ATTRS))
         {
            attributeSet = new NetEntryAttributes (
                                    target.getCanonicalString (),
                                    attributeNames,
                                    environment);
         }
         else
         {
            attributeSet = new BasicAttributes (true);
         }
         return (attributeSet);
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);

			cont.setError (this, name.toString ());
			throw (cont.fillInException (ne));
      }
      catch (NamingException e)
      {
         cont.setError (this, name.toString ());
         throw (cont.fillInException (e));
      }

   } /* c_getAttributes () */

   /**
    *
    */
   protected DirContext c_getSchema (
         Name name,
         Continuation cont)
      throws NamingException
   {
      NdsName target;
      Context context;

      try
      {
//Note: needs non-global schema enhancement
         target = (NdsName) nameParser.parse ("SC=Schema");

         context = schemaFactory.getContextInstance (
                                    target.getCanonicalString (),
                                    Schema.SCHEMA_CONTAINER,
                                    environment);
         cont.setSuccess ();
         return ((DirContext) context);
      }
      catch (NamingException e)
      {
         cont.setError (this, name.toString ());
         throw (cont.fillInException (e));
      }

   } /* c_getSchema () */

   /**
    *
    */
   protected DirContext c_getSchemaClassDefinition (
         Name name,
         Continuation cont)
      throws NamingException
   {
      Name target;
      NetDirContext netContext = (NetDirContext) c_lookup (name, cont);

      try
      {
         Attributes attrs;
         NamingEnumeration enumeration;
         DirContext context;

         attrs = c_getAttributes (
                                    name,
                                    new String[] { "Object Class" },
                                    cont);

         enumeration = new NetClassDefinitionsBindingEnumerator (
                              attrs.get ("Object Class").getAll (),
                              environment);

         context = new SchemaStaticDirContext ("[Root]", enumeration, environment);
/*
//Note: needs non-global schema enhancement
         target = nameParser.parse (netContext.getBaseClass ());
//         target = nameParser.parse ("CL=" + netContext.getBaseClass ());
         target.add (0, "SC=Classes");
         target.add (0, "SC=Schema");

         context = schemaFactory.getContextInstance (
                                    target.toString (),
                                    environment);
*/
         cont.setSuccess ();
         return ((DirContext) context);
      }
      catch (NamingException e)
      {
         cont.setError (this, name.toString ());
         throw (cont.fillInException (e));
      }

   } /* c_getSchemaClassDefinition () */

   /**
    *
    */
   protected void c_modifyAttributes (
         Name name,
         int mod_op,
         Attributes attrs,
         Continuation cont)
      throws NamingException
   {
      NetService service = environment.getService ();
      NdsName target;
      NamingEnumeration attrEnum;
      NetBuffer changes;
      Vector streamAttrVals = new Vector ();

      // create NDS name
      target = (NdsName) name.clone ();
      target.addAll (0, getNameInstance ());

      try
      {
         changes = new NetBuffer (
                           service,
                           NetJNI.DSV_MODIFY_ENTRY,
                           environment.getBatchSize ());

         attrEnum = attrs.getAll ();

         // What happens if we overfill the changes buffer?

         while (attrEnum.hasMoreElements ())
         {
            Enumeration values;
            Attribute attribute;

            attribute = (Attribute) attrEnum.next ();

            // On REMOVE_ATTRIBUTE, If no values are specified, remove
            // the attribute, otherwise, remove the specified attribute
            // values.
            if(mod_op == REMOVE_ATTRIBUTE && attribute.size () == 0)
            {
               changes.putChange(NetJNI.DS_REMOVE_ATTRIBUTE,
                                 attribute.getID());
            }
            else
            {
               if(mod_op == REPLACE_ATTRIBUTE)
               {
                  changes.putChange(NetJNI.DS_CLEAR_ATTRIBUTE,
                                    attribute.getID());
               }

               values = attribute.getAll ();
               while (values.hasMoreElements ())
               {
                  NdsAttributeValue value;
                  NetAttributeValue valueImpl;

                  if(mod_op == REMOVE_ATTRIBUTE)
                  {
                     changes.putChange(NetJNI.DS_REMOVE_VALUE,
                                       attribute.getID());
                  }
                  else
                  {
                     changes.putChange(NetJNI.DS_ADD_VALUE,
                                       attribute.getID());
                  }

                  value = (NdsAttributeValue) values.nextElement ();
                  if( value instanceof NetAttributeValue )
                  {
                     valueImpl = (NetAttributeValue) value;
                  }
                  else
                  {
                     valueImpl = NetAttributeValueFactory.createValue(value);
                  }

                  if (valueImpl instanceof NetStream)
                  {
                     streamAttrVals.addElement (valueImpl);
                     streamAttrVals.addElement (attribute);
                     streamAttrVals.addElement (new Integer (mod_op));
                  }
                  else
                  {
                     changes.putAttributeValue (
                                    valueImpl.getNdsSyntaxId (),
                                    valueImpl.toByte ());
                  }
               }
            }
         }
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);

			cont.setError (this, name.toString ());
			throw (cont.fillInException (ne));
      }
      catch (Exception e) // by NetAttributeValueFactory.createValue()
      {
         cont.setError (this, name.toString ());
         throw new NamingException ();
      }

      NetIterationHandle iterationHandle = new NetIterationHandle (
                              service,
                              NetJNI.DSV_MODIFY_ENTRY);

      boolean more = false;

      try
      {
         service.modifyObject (
                                 target.getCanonicalString (),
                                 iterationHandle,  // reserved; set null
                                 more,             // reserved; set false
                                 changes.getHandle ());
      }
      catch (NSIException e)
      {
         AttributeModificationException ame;

         ame = new AttributeModificationException ();
         // null means NO operations were executed
         ame.setUnexecutedModifications (null);

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

         ne.setRootCause (e);
         cont.setError (this, target.toString ());
         throw (cont.fillInException (ne));
      }

      transferStreamBytesAfterAttrPut (streamAttrVals, environment,
            target.getCanonicalString ());

      cont.setSuccess ();

   } /* c_modifyAttributes ()  #1 */


   /* *
    * Since all NDS modifications are done on the back end, it is much more
    * efficient to package all modifications into one NetBuffer and send it
    * off as opposed to calling c_modifyAttributes(name, mod_op, attrs, cont)
    * for every item in the enumeration. If one fails, they all fail and the
    * unexecuted modifications are also handled differently.
    * (Pulled from an enumeration instead of an attributeSet.)
    */
   protected void c_modifyAttributes (
         Name name,
         ModificationItem[] mods,
         Continuation cont)
      throws NamingException
   {
      NetService service = environment.getService ();

      NdsName target;
      NetBuffer changes;
      Vector streamAttrVals = new Vector ();

      // create NDS name
      target = (NdsName) name.clone ();
      target.addAll (0, getNameInstance ());

      // create reserved parameters for modifyObject()  
      NetIterationHandle iterationHandle = new NetIterationHandle (
                              service,
                              NetJNI.DSV_MODIFY_ENTRY);

      boolean more = false;

      // enumerate mods to create NetBuffer
      try
      {
         changes = new NetBuffer (
                           service,
                           NetJNI.DSV_MODIFY_ENTRY,
                           NetBuffer.MAX_MESSAGE_LEN);
//                           environment.getBatchSize ());

         for (int i = 0; i < mods.length; i++)
         {
            Enumeration values;
            ModificationItem modItem;
            Attribute attribute;
            int modOp;
            int accruedWireSize = 0;

            modItem = mods[i];
            attribute = modItem.getAttribute ();
            modOp = modItem.getModificationOp ();

            // On REMOVE_ATTRIBUTE, If no values are specified, remove
            // the attribute, otherwise, remove the specified attribute
            // values.
            if(modOp == REMOVE_ATTRIBUTE && attribute.size () == 0)
            {
               changes.putChange(NetJNI.DS_REMOVE_ATTRIBUTE,
                                 attribute.getID());
            }
            else
            {
               if(modOp == REPLACE_ATTRIBUTE)
               {
                  changes.putChange(NetJNI.DS_CLEAR_ATTRIBUTE,
                                    attribute.getID());
               }

               values = attribute.getAll ();
               while (values.hasMoreElements ())
               {
                  NdsAttributeValue value;
                  NetAttributeValue valueImpl;

                  value = (NdsAttributeValue) values.nextElement ();
                  if( value instanceof NetAttributeValue )
                  {
                     valueImpl = (NetAttributeValue) value;
                  }
                  else
                  {
                     valueImpl = NetAttributeValueFactory.createValue(value);
                  }

                  if (valueImpl instanceof NetStream)
                  {
                     streamAttrVals.addElement (valueImpl);
                     streamAttrVals.addElement (attribute);
                     streamAttrVals.addElement (new Integer (modOp));
                  }
                  else
                  {
                     byte[] bytes = valueImpl.toByte ();
//NOTE: actualSize > value.length
                     int size = bytes.length;
                     
                     if(size > NetBuffer.MAX_MESSAGE_LEN)
                     {
                        throw new SizeLimitExceededException ();
                     }
                     
                     if(accruedWireSize + size > NetBuffer.MAX_MESSAGE_LEN)
                     {
                        doModify (
                            service,
                            target,
                            iterationHandle,
                            more,
                            changes,
                            name,
                            cont);
                            
                        changes = new NetBuffer (
                                          service,
                                          NetJNI.DSV_MODIFY_ENTRY,
                                          NetBuffer.MAX_MESSAGE_LEN);
                     
                        accruedWireSize = 0;
                     }
                     
                     accruedWireSize += size;
                     
                     if(modOp == REMOVE_ATTRIBUTE)
                     {
                        changes.putChange (NetJNI.DS_REMOVE_VALUE,
                                           attribute.getID());
                     }
                     else
                     {
                        changes.putChange (NetJNI.DS_ADD_VALUE,
                                           attribute.getID());
                     }
                     
                     changes.putAttributeValue (
                                    valueImpl.getNdsSyntaxId (),
                                    bytes);
                  }
               }
            }
         }
      }
      catch (NSIException e)
      {
         NamingException ne = exceptionFactory.getNamingException (e);

         cont.setError (this, name.toString ());
         throw (cont.fillInException (ne));
      }
      catch (Exception e) // by NetAttributeValueFactory.createValue()
      {
         cont.setError (this, name.toString ());
         throw new NamingException ();
      }

      doModify (
            service,
            target,
            iterationHandle,
            more,
            changes,
            name,
            cont);

      transferStreamBytesAfterAttrPut (streamAttrVals, environment,
            target.getCanonicalString ());

      cont.setSuccess ();

   } /* c_modifyAttributes ()  #2 */

   
   private void doModify (
        NetService service,
        NdsName target,
        NetIterationHandle iterationHandle,
        boolean more,
        NetBuffer changes,
        Name name,
        Continuation cont
   )
      throws NamingException
   {
      try
      {
         service.modifyObject (
                                 target.getCanonicalString (),
                                 iterationHandle,  // reserved; set null
                                 more,             // reserved; set false
                                 changes.getHandle ());
      }
      catch (NSIException e)
      {
         AttributeModificationException ame;

         ame = new AttributeModificationException ();
         // null means NO operations were executed
         ame.setUnexecutedModifications (null);

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

         ne.setRootCause (e);
         cont.setError (this, target.toString ());
         throw (cont.fillInException (ne));
      }
    
   } /* doModify() */
   

   /**
    *
    */
   protected void c_rebind (
         Name name,
         Object obj,
         Attributes attrs,
         Continuation cont)
      throws NamingException
   {

      if (valid_context (name, cont))
      {
         c_unbind (name, cont);
      }

      c_bind(name, obj, attrs, cont);

   } /* c_rebind () */

   /**
    *
    */
   protected NamingEnumeration c_search (
         Name name,
         Attributes matchingAttributes,
         String[] attributesToReturn,
         Continuation cont)
      throws NamingException
   {
      NdsName target;

      target = (NdsName) name.clone ();
      target.addAll (0, getNameInstance ());

      return (new NetSearchEnumerator (
                     target.getCanonicalString (),
                     environment,
                     matchingAttributes,
                     attributesToReturn));
   }

   /**
    *
    */
   protected NamingEnumeration c_search (
         Name name,
         String filter,
         SearchControls cons,
         Continuation cont)
      throws NamingException
   {
      return c_search (name, filter, null, cons, cont);
   }

   /**
    *
    */
   protected NamingEnumeration c_search (
         Name name,
         String filterExpr,
         Object [] filterArgs,
         SearchControls cons,
         Continuation cont)
      throws NamingException
   {
      NdsName target;

      target = (NdsName) name.clone ();
      target.addAll (0, getNameInstance ());

      return (new NetSearchEnumerator (
                     target.getCanonicalString (),
                     c_getSchema (target, cont),
                     environment,
                     filterExpr,
                     filterArgs,
                     cons));
   }

      // ********** nns operations **********

   protected void c_bind_nns (
         Name name,
         Object obj,
         Attributes attrs,
         Continuation cont)
      throws NamingException
   {
      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }
   }

   protected DirContext c_createSubcontext_nns (
         Name name,
         Attributes attrs,
         Continuation cont)
      throws NamingException
   {
      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }
      return (null);
   }

   /**
    *
    */
   protected Attributes c_getAttributes_nns (
         Name name,
         String[] attrIds,
         Continuation cont)
      throws NamingException
   {
      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }
      return (null);
   }

   protected DirContext c_getSchema_nns (
         Name name,
         Continuation cont)
      throws NamingException
   {
      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }
      return (null);

   } /* c_getSchema_nns () */

   protected DirContext c_getSchemaClassDefinition_nns (
         Name name,
         Continuation cont)
      throws NamingException
   {
      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }
      return (null);

   } /* c_getSchemaClassDefinition_nns () */

   protected void c_modifyAttributes_nns (
         Name name,
         int mod_op,
         Attributes attrs,
         Continuation cont)
      throws NamingException
   {
      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }
   }

   protected void c_modifyAttributes_nns (
         Name name,
         ModificationItem[] mods,
         Continuation cont)
      throws NamingException
   {
      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }
   }

   protected void c_rebind_nns (
         Name name,
         Object obj,
         Attributes attrs,
         Continuation cont)
      throws NamingException
   {
      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }
   }

   protected NamingEnumeration c_search_nns (
         Name name,
         Attributes matchingAttributes,
         String[] attributesToReturn,
         Continuation cont)
      throws NamingException
   {
      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }
      return (null);
   }

   protected NamingEnumeration c_search_nns (
         Name name,
         String filter,
         SearchControls cons,
         Continuation cont)
      throws NamingException
   {
      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }
      return (null);
   }

   protected NamingEnumeration c_search_nns (
         Name name,
         String filterExpr,
         Object [] filterArgs,
         SearchControls cons,
         Continuation cont)
      throws NamingException
   {
      Object object = c_lookup_nns (name, cont);

      if (object != null)
      {
         cont.setContinue (object, name, this);
      }
      return (null);
   }


   // ******************** Referenceable Interface ********************

   /**
    * Retrieves the Reference of this object.
    *
    * @return the Reference of this object.
    */
   public Reference getReference ()
      throws NamingException
   {
      return (null);

   } /* getReference () */


   // ******************** Partitionable Interface ********************

   /**
    *
    */
   public Context getPartitionRoot (
         String name)
      throws NamingException
   {
      return (getPartitionRoot (new CompositeName (name)));
   }

   /**
    *
    */
   public Context getPartitionRoot (
         Name name)
      throws NamingException
   {
      if (!name.isEmpty ())
      {
         Object object = this.lookup (name);

         if (object instanceof Partitionable)
         {
            return (((Partitionable) object).getPartitionRoot (""));
         }
         throw (new OperationNotSupportedException ());
      }

      return (partitionFactory.getContextInstance (
                                    "",
                                    Schema.PARTITION_ROOT,
                                    environment));

   } /* getPartitionRoot () */

   /**
    *
    */
   public DirContext getPartition (
         String name)
      throws NamingException
   {
      return (getPartition (new CompositeName (name)));
   }

   /**
    *
    */
   public DirContext getPartition (
         Name name)
      throws NamingException
   {
      if (!name.isEmpty ())
      {
         Object object = this.lookup (name);

         if (object instanceof Partitionable)
         {
            return (((Partitionable) object).getPartition (""));
         }
         throw (new OperationNotSupportedException ());
      }

      // call native function to get partition root name
      StringBuffer partitionRoot = new StringBuffer();
      NetService service = environment.getService ();

      try
      {
         service.getPartitionRoot (distinguishedName, partitionRoot);
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);
         throw (ne);
      }
      catch (SessionException e)
      {
         NamingException ne = new NamingException ();

         ne.setRootCause (e);
         throw (ne);
      }

      // create PartitionContext from partition root name
      return ((DirContext) partitionFactory.getContextInstance (
                                                new String (partitionRoot),
                                                Schema.PARTITION,
                                                environment));

   } /* getPartition () */


   // ******************** NdsIteratorFactory Interface ********************

   /**
    *
    */
   public NdsIterator createIterator (
         String name, 
         Attributes matchingAttributes,
         String[] attributesToReturn)
      throws NSIException
   {
      NdsName target;

      try
      {
         target = (NdsName) nameParser.parse (name);
         target.addAll (0, getNameInstance ());

         return (new BasicNdsIterator (
                        target.getCanonicalString (),
                        environment,
                        matchingAttributes,
                        attributesToReturn));
      }
      catch (NamingException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }

   }

   /**
    *
    */
   public NdsIterator createIterator (
         String name, 
         Attributes matchingAttributes)
      throws NSIException
   {
      return createIterator (name, matchingAttributes, null);
   }

   /**
    *
    */
   public NdsIterator createIterator (
         String name, 
         String filter,
         NdsIteratorControls cons)
      throws NSIException
   {
      return createIterator (name, filter, null, cons);
   }

   /**
    *
    */
   public NdsIterator createIterator (
         String name, 
         String filterExpr,
         Object[] filterArgs,
         NdsIteratorControls cons)
      throws NSIException
   {
      NdsName target;

      try
      {
         target = (NdsName) nameParser.parse (name);
         target.addAll (0, getNameInstance ());

         return (new BasicNdsIterator (
                        target.getCanonicalString (),
                        getSchema (target),
                        environment,
                        filterExpr,
                        filterArgs,
                        cons));
      }
      catch (NamingException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }

   }

   /**
    *
    */
   public NdsIterator createIterator (
         String name,
         NdsIteratorControls cons,
         String className,
         String subordinateName)
      throws NSIException
   {
      NdsName target;

      try
      {
         target = (NdsName) nameParser.parse (name);
         target.addAll (0, getNameInstance ());

         return (new ListNdsIterator (
                        target.getCanonicalString (),
                        environment,
                        className,
                        subordinateName,
                        cons));
      }
      catch (NamingException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }

   }

   // ******************** NdsObject Interface ********************

   /**
    *
    */
   public String getDistinguishedName ()
   {
      return (distinguishedName);
   }

   /**
    *
    */
   public int getObjectFlags ()
      throws NSIException
   {
      return (objectInfo.getObjectFlags ());
   }

   /**
    *
    */
   public int getSubordinateCount ()
      throws NSIException
   {
      return (objectInfo.getSubordinateCount ());
   }

   /**
    *
    */
   public Date getModificationTime ()
      throws NSIException
   {
      Date date = new Date ();

      date.setTime (objectInfo.getModificationTime () * 1000L);
      return (date);
   }

   /**
    *
    */
   public String getBaseClass ()
      throws NSIException
   {
      return (objectInfo.getBaseClass ());
   }

   /**
    *
    */
   public String getTreeName ()
   {
      return (environment.getHost ());
   }

   /**
    *
    */
   public NdsObjectRights getObjectEffectiveRights (String trustee)
      throws NSIException
   {
      if (trustee.equalsIgnoreCase ("[Public]")                  ||
          trustee.equalsIgnoreCase ("[Root]")                    ||
          trustee.equalsIgnoreCase ("[Creator]")                 ||
          trustee.equalsIgnoreCase ("[Self]"))
      {
         NetService service;
         NWInteger privileges = new NWInteger (0);

         try
         {
            service = environment.getService ();
         }
         catch (NamingException e)
         {
				NSIException ne = new NSIException ();

				ne.setRootCause (e);
				throw (ne);
         }

         try
         {
            service.getEffectiveRights (
                           trustee,             //subjectName,
                           distinguishedName,   //objectName,
                           "[Entry Rights]",
                           privileges);

            return (new NdsObjectRights (privileges.getValue ()));
         }
         catch (SessionException e)
         {
            NSIException ne = new NSIException ();

            ne.setRootCause (e);
            throw (ne);
         }

      }
      else
      {
         Context trusteeCtx = null;

         try
         {
            trusteeCtx = factory.getContextInstance (
                                 trustee,
                                 environment);
            
         }
         catch (NamingException e)
         {
				NSIException ne = new NSIException ();
				ne.setRootCause (e);
				throw (ne);
         }

         return getObjectEffectiveRights ((NdsObject)trusteeCtx);
      }

   } /* getObjectEffectiveRights () */


   /**
    *
    */
   public NdsObjectRights getObjectEffectiveRights (NdsObject trustee)
      throws NSIException
   {
      try
      {
         NWInteger privileges = new NWInteger (0);
         NetService service = environment.getService ();

         service.getEffectiveRights (
                        trustee.getDistinguishedName (),    //subjectName,
                        distinguishedName,                  //objectName,
                        "[Entry Rights]",
                        privileges);

         return (new NdsObjectRights (privileges.getValue ()));
      }
      catch (NamingException e)
      {
         NSIException ne = new NSIException ();

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

         ne.setRootCause (e);
         throw (ne);
      }

   } /* getObjectEffectiveRights () */

   /**
    * @ deprecated
    */
   public NdsAttributeRights getAttributeEffectiveRights (String trustee, String attrName)
      throws NSIException
   {
      if (trustee.equalsIgnoreCase ("[Public]")                  ||
          trustee.equalsIgnoreCase ("[Root]")                    ||
          trustee.equalsIgnoreCase ("[Creator]")                 ||
          trustee.equalsIgnoreCase ("[Self]"))
      {

         NetService service;
         NWInteger privileges = new NWInteger (0);

         try
         {
            service = environment.getService ();
         }
         catch (NamingException e)
         {
            throw new NSIException();
         }

         try
         {
            service.getEffectiveRights (
                           trustee,            //subjectName,
                           distinguishedName,  //objectName,
                           attrName,
                           privileges);
         }
         catch (SessionException e)
         {
            NSIException ne = new NSIException ();

            ne.setRootCause (e);
            throw (ne);
         }
         return new NdsAttributeRights(privileges.getValue ());
      }
      else
      {
         NdsObject trusteeCtx = null;

         try
         {
            trusteeCtx = (NdsObject) this.lookup (trustee);
         }
         catch (NamingException e)
         {
            throw (new NSIException ());
         }

         return getAttributeEffectiveRights (trusteeCtx, attrName);
      }

   } /* getAttributeEffectiveRights () */


   /**
    * @ deprecated
    */
   public NdsAttributeRights getAttributeEffectiveRights (NdsObject trustee, String attrName)
      throws NSIException
   {
      NetService service;
      NWInteger privileges = new NWInteger (0);

      try
      {
         service = environment.getService ();
      }
      catch (NamingException e)
      {
         throw new NSIException ();
      }

      try
      {
         service.getEffectiveRights (
                     trustee.getDistinguishedName (),    //subjectName,
                     distinguishedName,                  //objectName,
                     attrName,
                     privileges);
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }

      return new NdsAttributeRights (privileges.getValue ());

   } /* getAttributeEffectiveRights () */

   /**
    * @ deprecated
    */
   public NdsAttributeRights getAttributeEffectiveRights (String trustee)
      throws NSIException
   {
      if (trustee.equalsIgnoreCase ("[Public]")                  ||
          trustee.equalsIgnoreCase ("[Root]")                    ||
          trustee.equalsIgnoreCase ("[Creator]")                 ||
          trustee.equalsIgnoreCase ("[Self]"))
      {
         NetService service;
         NWInteger privileges = new NWInteger (0);

         try
         {
            service = environment.getService ();
         }
         catch(NamingException e)
         {
            throw new NSIException();
         }

         try
         {
            service.getEffectiveRights (
                           trustee,           //subjectName,
                           distinguishedName, //objectName,
                           "[All Attributes Rights]",
                           privileges);

            return new NdsAttributeRights (privileges.getValue ());
         }
         catch (SessionException e)
         {
            NSIException ne = new NSIException ();

            ne.setRootCause (e);
            throw (ne);
         }
      }
      else
      {
         NdsObject trusteeCtx = null;

         try
         {
            trusteeCtx = (NdsObject) this.lookup (trustee);
         }
         catch (NamingException e)
         {
            throw (new NSIException ());
         }

         return getAttributeEffectiveRights (trusteeCtx);
      }

   } /* getAttributeEffectiveRights () */


   /**
    * @ deprecated
    */
   public NdsAttributeRights getAttributeEffectiveRights (NdsObject trustee)
      throws NSIException
   {

      return (getAttributeEffectiveRights (
                  trustee,
                  "[All Attributes Rights]"));

   } /* getAttributeEffectiveRights () */

   /**
    *
    */
   public NamingEnumeration getAttributesEffectiveRights (
            NdsObject trustee,
            String[] attrIds)
      throws NSIException
   {
      return getAttributesEffectiveRights (
                     trustee.getDistinguishedName (),
                     attrIds);
                     
   } /* getAttributesEffectiveRights () */
   
   /**
    *
    */
   public NamingEnumeration getAttributesEffectiveRights (
            String trustee,
            String[] attrIds)
      throws NSIException
   {
      return (new NetAttributeRightsEnumerator (
                     trustee,
                     distinguishedName,
                     environment,
                     attrIds));
                     
   } /* getAttributesEffectiveRights () */
   
   public NamingEnumeration list (
            String name,
            boolean containers)
      throws NSIException, NamingException
   {
      if (containers == true)
      {
         return (new NetFilteredNameClassEnumerator (
                        name.length()==0 ? distinguishedName : name,
                        containers,
                        environment));
      }
      else
      {
         return (new NetNameClassPairEnumerator (
                        name.length()==0 ? distinguishedName : name,
                        environment));
      }

   } /* list () */

   public NamingEnumeration list (
            String name,
            String className,
            String subordinateName)
      throws NSIException, NamingException
   {
      return (new NetFilteredNameClassEnumerator (
                     name.length()==0 ? distinguishedName : name,
                     className,
                     subordinateName,
                     environment));
   } /* list () */

   public NamingEnumeration listBindings (
            String name,
            boolean containers)
      throws NSIException, NamingException
   {
      if (containers)
      {
         return (new NetFilteredBindingEnumerator (
                        name.length()==0 ? distinguishedName : name,
                        containers,
                        environment));
      }
      else
      {
         return (new NetBindingEnumerator (
                        name.length()==0 ? distinguishedName : name,
                        environment));
      }
   } /* listBindings () */

   public NamingEnumeration listBindings (
            String name,
            String className,
            String subordinateName)
      throws NSIException, NamingException
   {
      return (new NetFilteredBindingEnumerator (
                     name.length()==0 ? distinguishedName : name,
                     className,
                     subordinateName,
                     environment));
   } /* listBindings () */

   // ******************** NdsDataAccessor Interface ********************

   /**
    *
    */
   public Environment getEnvironmentInstance ()
      throws NamingException
   {
      return (environment);
   }


   // ******************** NetDirContext Class ********************

   /**
    *
    */
   protected boolean valid_context (
         Name name,
         Continuation cont)
      throws NamingException
   {
      int ccode;
      NdsName target;

      if (name.isEmpty ())
      {
         cont.setSuccess ();
         return (true);
      }

      NetService service = environment.getService ();

      target = (NdsName) name.clone ();
      target.addAll (0, getNameInstance ());

      try
      {
         service.resolveName (
                        target.getCanonicalString (),
                        null,                         // conn
                        null);                        // objectID

         cont.setSuccess ();
         return (true);
      }
      catch (NSIException e)
      {
         if (e.getCCode () == NDSErrors.ERR_NO_SUCH_ENTRY)
         {
            cont.setSuccess ();
            return (false);
         }
         else
         {
				NamingException ne = exceptionFactory.getNamingException (e);

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

         cont.setError (this, name.toString ());
         ne.setRootCause (e);
         throw (cont.fillInException (ne));
      }

    } /* valid_context () */

   /**
    *
    */
   protected boolean valid_penultimate_context (
         Name name,
         Continuation cont)
      throws NamingException
   {
      NdsName target;

      if (name.isEmpty ())
      {
         cont.setSuccess ();
         return (false);
      }

      // remove the last component
      target = (NdsName) name.clone ();
      target.remove (target.size () - 1);

      return (valid_context (target, cont));

   } /* valid_penultimate_context () */

   /**
    *
    */
   protected Object getNNSP ()
      throws NamingException
   {
      return (null);
   }

   protected NdsName components;

   /**
    *
    */
   protected synchronized NdsName getNameInstance ()
      throws NamingException
   {
      if (components == null)
      {
         components = (NdsName) nameParser.parse (distinguishedName);
      }
      return (components);

   } /* getNameInstance () */


   // this method expects a vector with two entries added for each
   //   stream attribute value: the actual attr. val. impl.; the attribute
   //   name.
   protected void transferStreamBytesAfterAttrPut (
         Vector streamAttrVals,
         NetEnvironment environment,
         String objectName)
      throws NamingException
   {
      Enumeration enum = streamAttrVals.elements();
      NetStream st;
      Attribute attr;
      int modOp;
      boolean exceptionalState = false;
      Exception rootException = null;
      Vector modExItems = null;

      while (enum.hasMoreElements())
      {
         st = (NetStream) enum.nextElement();

         enum.hasMoreElements();

         attr = (Attribute) enum.nextElement();

         enum.hasMoreElements();

         modOp = ((Integer) enum.nextElement()).intValue();

         if (exceptionalState)
         {
            modExItems.addElement(new ModificationItem(modOp, attr));
            continue;
         }

         try
         {
            st.initialize(
                              environment.getSession (),
                              environment.getService (),
                              objectName,
                              attr.getID(),
                              environment.getBatchSize());

            st.transferStreamToWriteBytes();
         }
         catch (IOException e)
         {
            exceptionalState = true;
            rootException = e;
         }
         catch (NSIException e)
         {
            exceptionalState = true;
            rootException = e;
         }

         if (exceptionalState)
         {
            modExItems = new Vector ();
            modExItems.addElement(new ModificationItem(modOp, attr));
         }
      }

      if (exceptionalState)
      {
         AttributeModificationException ne = new AttributeModificationException();
         ModificationItem[] items = new ModificationItem[modExItems.size()];

         for (int i = 0; i < items.length; i++)
            items[i] = (ModificationItem) modExItems.elementAt(i);

         ne.setUnexecutedModifications(items);
         ne.setRootCause(rootException);

         throw ne;
      }
   }

} /* NetDirContext */


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

/** @internal
 *
 */
class NetAttributeRightsEnumerator implements NamingEnumeration
{
   private String trusteeName;
   private String objectName;
   private NetEnvironment netEnvironment;
   private NetService service;

   private boolean allAttributes;
   private NetBuffer attributeBuffer;

   private int attributeCount;
   private NetIterationHandle iterationHandle;
   private NetBuffer privilegeInfo;

   private NetAttribute nextAttribute;

   private static NdsNamingExceptionFactory exceptionFactory =
       new NdsNamingExceptionFactory ();

   /**
    *
    */
   NetAttributeRightsEnumerator (
         String trusteeName,
         String objectName,
         NetEnvironment netEnvironment,
         String[] attributeNames)
      throws NSIException
   {
      try
      {
         this.trusteeName = trusteeName;
         this.objectName = objectName;
         this.netEnvironment = netEnvironment;
         this.service = netEnvironment.getService ();
         
         iterationHandle = new NetIterationHandle (
                                    service,
                                    NetJNI.DSV_READ);

         if (null == attributeNames)
         {
            // return rights for [All Attributes Rights]
            NWInteger privileges = new NWInteger (0);
            
            try
            {
               service.getEffectiveRights (
                              trusteeName,
                              objectName,
                              "[All Attributes Rights]",
                              privileges);
            }
            catch (SessionException e)
            {
               NSIException ne = new NSIException ();

               ne.setRootCause (e);
               throw (ne);
            }
            nextAttribute = new NetAttribute(
                  "[All Attributes Rights]",
                  new NdsInteger((long)privileges.getValue ()));
            attributeCount = 0;
         }
         else
         {
            if (0 == attributeNames.length)
            {
               // return rights for all attributes
               this.allAttributes = true;
               this.attributeBuffer = new NetBuffer ();
            }
            else
            {
               // return rights for specified attributes
               this.allAttributes = false;
               attributeBuffer = new NetBuffer (
                                          service,
                                          NetJNI.DSV_READ,
                                          netEnvironment.getBatchSize ());

               // put attributeNames in NdsBuffer
               for (int i = 0; i < attributeNames.length; i++)
               {
                  attributeBuffer.putAttributeName (attributeNames[i]);
               }
            }
                                       
            privilegeInfo = new NetBuffer (
                                 service,
                                 netEnvironment.getBatchSize ());
            nextAttribute = getFirstAttribute ();
         }
      }
      catch (NamingException e)
      {
			NSIException ne = new NSIException ();

			ne.setRootCause (e);
			throw (ne);
      }

   } /* NetAttributeRightsEnumerator () */

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

   public boolean hasMore ()
   {
      if (null == nextAttribute)
      {
         return (false);
      }
      return (true);
      
   } /* hasMore () */

   /**
    *
    */
   private int updatePrivilegeInfo ()
      throws NSIException
   {
      try
      {
         service.listAttrsEffectiveRights (
                     objectName,
                     trusteeName,
                     allAttributes,
                     attributeBuffer.getHandle (),
                     iterationHandle,
                     privilegeInfo.getHandle ());
        
         return (privilegeInfo.getAttrCount ());
      }
      catch (NSIException e)
      {
         if (e.getCCode () == NDSErrors.ERR_NO_SUCH_ATTRIBUTE)
         {
            return (0);
         }
         else
         {
            throw (e);
         }
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

         ne.setRootCause (e);
         throw (ne);
      }

   } /* updatePrivilegeInfo () */

   /**
    *
    */
   private NetAttribute getFirstAttribute ()
      throws NSIException
   {
      if (0 == (attributeCount = updatePrivilegeInfo ()))
      {
         return (null);
      }
      attributeCount--;
      try
      {
         return (new NetAttribute (
                        objectName,
                        netEnvironment,
                        privilegeInfo));
      }
      catch (NamingException e)
      {
         throw new NSIException ();
      }

   } /* getFirstAttribute () */

   /**
    *
    */
   private NetAttribute getNextAttribute ()
      throws NSIException
   {
      if (0 == attributeCount)
      {
         if (!iterationHandle.moreIterations ())
         {
            return (null);
         }
         attributeCount = updatePrivilegeInfo ();
      }
      attributeCount--;
      try
      {
         return (new NetAttribute (
                        objectName,
                        netEnvironment,
                        privilegeInfo));
      }
      catch (NamingException e)
      {
         throw new NSIException ();
      }

   } /* getNextAttribute () */

   /**
    *
    */
   public Object nextElement ()
      throws NSIException
   {
      return (next ());
   }

   /**
    *
    */
   public Object next ()
      throws NSIException
   {
      if (false == this.hasMoreElements ())
      {
         throw (new NoSuchElementException ());
      }
      NetAttribute currentAttribute = nextAttribute;

      nextAttribute = getNextAttribute ();

      try
      {
         // handle attribute values in multiple iterations
         while (currentAttribute.equalsApproximate (nextAttribute))
         {
            Enumeration valueList = nextAttribute.getAll ();

            while (valueList.hasMoreElements ())
            {
               currentAttribute.add (valueList.nextElement ());
            }
            nextAttribute = getNextAttribute ();
         }
      }
      catch (NamingException e)
      {
         throw new NSIException ();
      }
      return (new NdsAttributeRights (currentAttribute));

   } /* next () */

   /**
    *
    */
   public void close ()
      throws NamingException
   {
		try
		{
			iterationHandle.closeIteration ();
		}
      catch (NSIException e)
      {
         NamingException ne = exceptionFactory.getNamingException (e);
         throw (ne);
      }
   }

} /* NetAttributeRightsEnumerator */


