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

  $Archive: /njcl_v2/src/com/novell/service/nds/naming/net/NetClassDefinitionsDirContext.java $
  $Revision: 13 $
  $Modtime: 1/28/00 12:46p $

  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.util.*;

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

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

import com.novell.service.jncp.NSIException;
//import com.novell.service.jncpv2.net.*;

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

import com.novell.service.nds.*;
import com.novell.service.nds.naming.*;

import com.novell.utility.naming.spi.ContextFactory;


/** @internal
* This class is bound to the root of the schema tree and has bindings
* to all NDS Class definitions.
*
* @see JNDI documentation section 5.2.4 Schema
*/
public class NetClassDefinitionsDirContext
   extends NetSchemaContainerDirContext
{
   private static NdsNamingExceptionFactory exceptionFactory =
       new NdsNamingExceptionFactory ();
   /**
    * Constructor.
    *
    * @param objectName    Distinguished name of this object
    * @param baseClass     NDS class name
    * @param environment properties to be set in this objects environment
    */
   protected NetClassDefinitionsDirContext (
         String objectName,
         String baseClass,
         NetEnvironment environment)
      throws NamingException
   {
      super (objectName, baseClass, environment);
   }

   /* ***********************************************************************
    * protected toolkit Context methods
    ************************************************************************/

   /**
    * Lists all NDS Class Definitions for current session
    */
   protected NamingEnumeration schema_list (
         Continuation cont)
      throws NamingException
   {
      cont.setSuccess ();
      return new ClassDefinitionsNameClassEnumerator (this);
   }

   /**
    * Lists all NDS Class Definitions for current session
    */
   protected NamingEnumeration schema_listBindings (
         Continuation cont)
      throws NamingException
   {
      cont.setSuccess ();
      return new ClassDefinitionsBindingEnumerator (this);
   }

   /**
    * Deletes a class definition from the Directory Schema.
    * Calling this is not allowed if the class is referenced by any
    * other class, or if objects of this class exist in the Directory.
    * Clients cannot subtract from the standard set of class definitions
    * defined by the Directory Base Schema (these are flagged nonremovable).
    * Clients can, however, add and remove non-standard definitions
    * (if not in use).
    */
   protected void schema_destroySubcontext (
         String name,
         Continuation cont)
      throws NamingException
   {
		// don't parse an empty string
		String target = (name.length () > 0) ?
							 nameParser.parse (name).get (0) :
							 "";

      // Make sure binding exists
      if (false == exists (target))
      {
         cont.setError (this, target);
         NameNotFoundException e = new NameNotFoundException ();
         throw cont.fillInException (e);
      }

      try
      {
         getService ().removeClassDef (target);
      }
      catch (Exception e)
      {
         NamingException ne = new NamingException ();

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

      cont.setSuccess ();
   }

   /* ***********************************************************************
    * protected toolkit DirContext methods
    ************************************************************************/

   /**
    * Creates a new NDS class definition and binds to the specified name.
    * The name of the new object class must be unique within the
    * Directory Schema class definitions.
    * New object-class names should be cleared through Developer Support
    * to guarantee uniqueness.
    */
   protected DirContext schema_createSubcontext (
         String name,
         Attributes attrs,
         Continuation cont)
      throws NamingException
   {
      if (isEmpty (name))
      {
         cont.setError (this, name);
         throw cont.fillInException (new InvalidNameException ());
      }

      String target = nameParser.parse (name).get (0);

      // See if class currently exists
      if (exists (target))
      {
         cont.setError (this, name);
         throw cont.fillInException (new NameAlreadyBoundException ());
      }

      if (attrs == null)
      {
         attrs = new BasicAttributes ();
      }

      // Create new class def
      try
      {
         NetBuffer classItems = new NetBuffer (getService (),
                           NetJNI.DSV_DEFINE_CLASS,
                           environment.getBatchSize ());

         // Set Super Class Names in classItems
         storeClassItem (classItems, attrs.get (
            NetClassDefinitionDirContext.ATTR_OBJECT_SUPER_CLASSES));

         // Set Containment Class Names in classItems
         storeClassItem (classItems, attrs.get (
            NetClassDefinitionDirContext.ATTR_OBJECT_CONTAINMENT_CLASSES));

         // Set Naming Attributes in classItems
         storeClassItem (classItems, attrs.get (
            NetClassDefinitionDirContext.ATTR_OBJECT_NAMING_ATTRIBUTES));

         // Set Mandatory Attributes in classItems
         storeClassItem (classItems, attrs.get (
            NetClassDefinitionDirContext.ATTR_OBJECT_MANDATORY_ATTRIBUTES));

         // Set Optional Attributes in classItems
         storeClassItem (classItems, attrs.get (
            NetClassDefinitionDirContext.ATTR_OBJECT_OPTIONAL_ATTRIBUTES));

         // Load attributes in order to extract attr values
         Attribute aFlags = attrs.get (
            NetClassDefinitionDirContext.ATTR_FLAGS);
         Attribute aAsn1ID = attrs.get (
            NetClassDefinitionDirContext.ATTR_ASN1NAME);

         // Get flags for NetClassInfo constructor
         long flags = 0;
         if (null != aFlags)
         {
            flags = ((NdsInteger)
                     aFlags.getAll ().nextElement ()).getInteger ();
         }

         // Get asn1ID for NetClassInfo constructor
         NetAsn1ID asn1ID = new NetAsn1ID ();
         if (null != aAsn1ID)
         {
            asn1ID.setData (((NdsOctetString) aAsn1ID.
                           getAll ().nextElement ()).
                           getOctetString ());
         }

         // Construct the NetClassInfo with values
         NetClassInfo classInfo = new NetClassInfo ((int) flags, asn1ID);

         // Finally, create the new class def
         getService ().defineClass (
                        target,
                        classInfo,
                        classItems.getHandle ());
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);

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

      return ((DirContext) a_lookup (name, cont));
   }

   /* ***********************************************************************
    * Utility methods
    ************************************************************************/

   /**
    * Returns true if the named class exists.
    * This is called by various bind/create/delete methods
    */
   protected boolean exists (
         String name)
   {
      try
      {
         NetIterationHandle iterationHandle = new NetIterationHandle (
                                             getService (),
                                             NetJNI.DSV_READ_CLASS_DEF);

         NetBuffer classNames = new NetBuffer (getService (),
                                       NetJNI.DSV_READ_CLASS_DEF,
                                       environment.getBatchSize ());

         NetReadClassDefBuffer classDefs = new NetReadClassDefBuffer (
                                       getService (),
                                       NetJNI.DS_CLASS_DEF_NAMES,
                                       environment.getBatchSize ());

         classNames.putClassName (name);

         getService ().readClassDef (
                        NetJNI.DS_CLASS_DEF_NAMES,
                        false,
                        classNames.getHandle (),
                        iterationHandle,
                        classDefs);

      }
      catch (Exception e)
      {
         return false;
      }
      return true;
   }

   /* *
    * Stores all values in attr in classItems.
    * This is called by schema_createSubcontext
    */
   private void storeClassItem (
         NetBuffer classItems,
         Attribute attr)
      throws NamingException, NSIException
   {
      Enumeration e;
      int itemCount;

      classItems.beginClassItem ();

      if (null != attr)
      {
         e = attr.getAll ();
         itemCount = attr.size ();
         while (itemCount-- > 0)
         {
            classItems.putClassItem (
               ((NdsDistinguishedName) e.nextElement ()).
                                       getDistinguishedName ());
         }
      }
   }

} /* NetClassDefinitionsDirContext */


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

/** @internal
 * NamingEnumeration subclass setup specifically for
 * NetClassDefinitionsDirContext.  This will enumerate all
 * Class Definitions.
 */
class ClassDefinitionsNameClassEnumerator implements NamingEnumeration
{
   private static NdsNamingExceptionFactory exceptionFactory =
       new NdsNamingExceptionFactory ();
   private NetClassDefinitionsDirContext classDefsContext;
   private NetEnvironment environment;

   private NetService service;
   private NetIterationHandle iterationHandle;
   private NetReadClassDefBuffer classDefs;

   private Enumeration entries;


   /**
    * Default Constructor.
    *
    * @param cassDefsContext     The NetClassDefinitionsDirContext
    *                            constructing this class.
    */
   public ClassDefinitionsNameClassEnumerator (
         NetClassDefinitionsDirContext classDefsContext)
      throws NamingException
   {
      try
      {
         this.classDefsContext = classDefsContext;
         environment = classDefsContext.environment;
         service = classDefsContext.getService ();

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

         classDefs = new NetReadClassDefBuffer (
                              service,
                              NetJNI.DS_CLASS_DEF_NAMES,
                              environment.getBatchSize ());

         getClassDefs ();
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);
         throw ne;
      }

   } /* ClassDefinitionsNameClassEnumerator () */

   /**
    * Returns false only when classDefs buffer is empty and there are
    * no more iterations of NWDSReadClassDef to perform.
    */
   public boolean hasMoreElements ()
   {
      if (!entries.hasMoreElements () && !iterationHandle.moreIterations ())
      {
         return (false);
      }
      return (true);
   }

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

   /**
    *
    */
   private void getClassDefs ()
      throws NoSuchElementException
   {
      try
      {
         service.readClassDef (
                        NetJNI.DS_CLASS_DEF_NAMES,
                        true,
                        0,
                        iterationHandle,
                        classDefs);

         entries = classDefs.getEntries ();
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

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

   } /* getClassDefs () */

   /**
    * Returns the next element in the classDefs buffer.  If the buffer is
    * empty, it attempts to refill it.
    */
   public Object nextElement ()
   {
      try
      {
         return (next ());
      }
      catch (NamingException e)
      {
         throw (new NoSuchElementException ());
      }
   }

   /**
    *
    */
   public Object next ()
      throws NamingException
   {
      if (!this.hasMoreElements ())
      {
         throw (new NoSuchElementException ());
      }

      try
      {
         if (!entries.hasMoreElements ())
         {
            // No more elements in this buffer, get next buffer
            getClassDefs ();
         }

         // Get next element from this buffer
         NetClassInfo classInfo = (NetClassInfo) entries.nextElement ();

         return (new NameClassPair (
                        classInfo.getName (),
                        Schema.SCHEMA_CLASS));
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);
         throw (ne);
      }

   } /* next () */

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

} /* ClassDefinitionsNameClassEnumerator */


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

/** @internal
 * NamingEnumeration subclass setup specifically for
 * NetClassDefinitionsDirContext.  This will enumerate all
 * Class Definitions.
 */
class ClassDefinitionsBindingEnumerator implements NamingEnumeration
{
   private static ContextFactory factory = new SchemaContextFactory ();
   private static NdsNamingExceptionFactory exceptionFactory =
       new NdsNamingExceptionFactory ();

   private NetClassDefinitionsDirContext classDefsContext;
   private NetEnvironment environment;
   private int classAttributesLevel;

   private NetService service;
   private NetIterationHandle iterationHandle;
   private NetReadClassDefBuffer classDefs;

   private Enumeration entries;


   /**
    * @param cassDefsContext     The NetClassDefinitionsDirContext
    *                            constructing this class.
    */
   public ClassDefinitionsBindingEnumerator (
         NetClassDefinitionsDirContext classDefsContext)
      throws NamingException
   {
      try
      {
         this.classDefsContext = classDefsContext;
         environment = classDefsContext.environment;
         service = classDefsContext.getService ();

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

         classDefs = new NetReadClassDefBuffer (
                        service,
                        NetJNI.DS_CLASS_DEFS,
                        environment.getBatchSize ());

/* optimization code
         classAttributesLevel = environment.getClassAttributesLevel();

         // INFO_CLASS_DEFS (3) has a bug in NWNet, so throw exception here
         // FULL_CLASS_DEFS (4) has a bug in NWNet, so throw exception here
         if (classAttributesLevel == -1 ||
             classAttributesLevel == 3 ||
             classAttributesLevel == 4)
         {
            throw new NamingException (NdsEnvironment.CLASS_ATTRIBUTE_LEVEL +
                  "=" + environment.getClassAttributesLevelString ());
         }
*/
         getClassDefs ();
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);
         throw ne;
      }

   } /* ClassDefinitionsBindingEnumerator () */

   /**
    *
    */
   public boolean hasMoreElements ()
   {
      if (!entries.hasMoreElements () && !iterationHandle.moreIterations ())
      {
         return (false);
      }
      return (true);
   }

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

   /**
    *
    */
   private void getClassDefs ()
      throws NSIException
   {
      try
      {
         service.readClassDef (
                        NetJNI.DS_CLASS_DEFS,
                        true,
                        0,
                        iterationHandle,
                        classDefs);

         entries = classDefs.getEntries ();
      }
      catch (SessionException e)
      {
         NSIException ne = new NSIException ();

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

   } /* getClassDefs () */

   /**
    * Returns the next element in the classDefs buffer.  If the buffer is
    * empty, it attempts to refill it.
    */
   public Object nextElement ()
   {
      try
      {
         return (next ());
      }
      catch (NamingException e)
      {
         throw (new NoSuchElementException ());
      }
   }

   /**
    *
    */
   public Object next ()
      throws NamingException
   {
      if (!this.hasMoreElements ())
      {
         throw (new NoSuchElementException ());
      }

      try
      {
         if (!entries.hasMoreElements ())
         {
            // No more elements in this buffer, get next buffer
            getClassDefs ();
         }

         StringBuffer distinguishedName;

         // Get next element from this buffer
         NetClassInfo classInfo = (NetClassInfo) entries.nextElement ();

         distinguishedName = new StringBuffer (NetService.MAX_DN_CHARS);
         distinguishedName.append ("\"");
         distinguishedName.append (classInfo.getName ());
         distinguishedName.append ("\".");
         distinguishedName.append (classDefsContext.getDistinguishedName ());

         return (new Binding (
                        classInfo.getName (),
                        factory.getContextInstance (
                                    new String (distinguishedName),
                                    classInfo,
                                    environment)));
      }
      catch (NSIException e)
      {
			NamingException ne = exceptionFactory.getNamingException (e);
         throw (ne);
      }

   } /* next () */

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

} /* ClassDefinitionsBindingEnumerator */
