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

  $Archive: /njcl_v2/src/com/novell/service/file/nw/naming/Schema.java $
  $Revision: 12 $
  $Modtime: 2/22/00 5:12p $
 
  Copyright (c) 1998 Novell, Inc.  All Rights Reserved.

  THIS WORK IS  SUBJECT  TO  U.S.  AND  INTERNATIONAL  COPYRIGHT  LAWS  AND
  TREATIES.   NO  PART  OF  THIS  WORK MAY BE  USED,  PRACTICED,  PERFORMED
  COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED,  ABRIDGED, CONDENSED,
  EXPANDED,  COLLECTED,  COMPILED,  LINKED,  RECAST, TRANSFORMED OR ADAPTED
  WITHOUT THE PRIOR WRITTEN CONSENT OF NOVELL, INC. ANY USE OR EXPLOITATION
  OF THIS WORK WITHOUT AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO
  CRIMINAL AND CIVIL LIABILITY.

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

package com.novell.service.file.nw.naming;


import java.util.*;

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

import com.novell.utility.naming.EmptyNamingEnumerator;

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


/**@internal
 * This class is the root schema context for the file system provider.
 *
 * <p>This is the root schema DirContext that is returned from all File
 * System contexts' getSchema() method.
 *
 * <p>From this context, you can browse down to the class definition,
 * attribute definition, and syntax definition contexts.  The attributes
 * on these DSContexts allow you to inspect the file system schema.
 *
 * <p>For example, on the "Volume Definitions" class definition schema
 * context, there is an attribute called "Valid Attribute ID List" which
 * contains multiple strings that specify the valid attribute IDs which
 * can be accessed from a VolumeDirContext.
 * </p>
 *
 * @see com.novell.service.file.nw.naming.DirEntryDirContext#c_getSchema
 * @see com.novell.service.file.nw.naming.FileSystemInitialDirContext#c_getSchema
 */
public class Schema implements DirContext
{
   // other contstants
   public static final int NCP_OBJECT     = 0;  // name class pair list enum
   public static final int BINDING_OBJECT = 1;  // binding list enum
   public static final int LOOKUP_OBJECT  = 2;
   public static final int AS_OBJECT      = 3;  // attributeset
   public static final int NP_OBJECT      = 4;  // nameparser

   public static final String SCHEMA_CLASS = "Classes";
   public static final String SCHEMA_ATTRIBUTE = "Attributes";
   public static final String SCHEMA_SYNTAX = "Syntaxes";


   private Binding[] bindings;
   private static String distinguishedName = "[Root]";
   private FSEnvironment environment;

   /**
    * We have to have the server name so that we can construct an
    * appropriate file system name parser.
    */
   protected Schema (FSEnvironment environment)
   {
      bindings = new Binding[3];

      this.environment = environment;
      bindings[0] = new Binding (
                                    FSSchema.CLASS_DEFINITIONS,
                                    SchemaClassDef.class.getName (),
                                    new SchemaClassDef (environment));

      bindings[1] = new Binding (
                                    FSSchema.ATTRIBUTE_DEFINITIONS,
                                    SchemaAttrDef.class.getName (),
                                    new SchemaAttrDef (environment));

      bindings[2] = new Binding (
                                    FSSchema.SYNTAX_DEFINITIONS,
                                    SchemaSyntaxDef.class.getName (),
                                    new SchemaSyntaxDef(environment));

   } /* Schema() */


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

   public String getNameInNamespace ()
      throws NamingException
   {
      return (distinguishedName);
   }

   public NamingEnumeration list (
         String name)
      throws NamingException
   {
      if (name == null || name.length() == 0)
      {
         return (new NamingEnumerator ());
      }
      else
         return (NamingEnumeration)objectFromName(name, NCP_OBJECT);
   }

   public NamingEnumeration list(Name name)
      throws NamingException
   {
      return list(name.toString());
   }

   public NamingEnumeration listBindings (
         String name)
      throws NamingException
   {
      if (name == null || name.length() == 0)
      {
         return (new NamingEnumerator ());
      }
      else
         return (NamingEnumeration) objectFromName(name, BINDING_OBJECT);
   }

   public NamingEnumeration listBindings(Name name)
      throws NamingException
   {
      return listBindings(name.toString());
   }

   public Object lookup(String name) throws NamingException
   {
      if (name == null || name.length() == 0)
         return this;

      return objectFromName(name, LOOKUP_OBJECT);
   }

   public Object lookup(Name name) throws NamingException
   {
      return lookup(name.toString());
   }

   public NameParser getNameParser(String name) throws
      NamingException
   {
      if (name == null || name.length() == 0)
         return new FileSystemNameParser(environment.getServerName(), true);
      else
         return (NameParser) objectFromName(name, NP_OBJECT);
   }

   public NameParser getNameParser(Name name) throws
      NamingException
   {
      return getNameParser(name.toString());
   }

   public String composeName (
         String name,
         String prefix)
      throws NamingException
   {
      Name fullName;

      fullName = composeName(
                                 new CompositeName(name),
                                 new CompositeName(prefix));
      return fullName.toString();
   }

   public Name composeName (
         Name name,
         Name prefix)
      throws NamingException
   {
      Name res = (Name)prefix.clone();

      if (name == null)
      {
         return res;
      }
      res.addAll(name);

      return res;
   }

   public Hashtable getEnvironment()
      throws NamingException
   {
      return environment.getEnvironment(true);
   }

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

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


   /* methods that don't just throw an exception (but do nothing) */

   /* methods that just throw an exception */

   public void bind(String name, Object obj) 
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public void bind(Name name, Object obj) 
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public void rebind(String name, Object obj) 
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public void rebind(Name name, Object obj) 
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public void unbind(String name) 
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public void unbind(Name name) 
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public void rename(String oldName, String newName)
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public void rename(Name oldName, Name newName) 
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public void destroySubcontext(String name) 
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public void destroySubcontext(Name name) 
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public Context createSubcontext(String name) 
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public Context createSubcontext(Name name) 
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public Object lookupLink(String name) 
      throws NamingException
   {
      throw new InvalidNameException();
   }

   public Object lookupLink(Name name) 
      throws NamingException
   {
      throw new InvalidNameException();
   }

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

/* **************************************************************************
*  DSContext implementation
****************************************************************************/

   /* methods that actually do something */

   public Attributes getAttributes(String name)
      throws NamingException
   {
      return getAttributes(name, null);
   }

   public Attributes getAttributes(Name name)
      throws NamingException
   {
      return getAttributes(name, null);
   }

   public Attributes getAttributes(String name, String[] attrIds)
      throws NamingException
   {
      if (name == null || name.length() == 0)
         return new NAttributes(true);    // the root has no attributes
      else
      {
         Attributes as = (Attributes) objectFromName(name, AS_OBJECT);
         if (attrIds == null)
            return as;

         Attributes rvalue = new NAttributes(true);
         for (int i=0; i < attrIds.length; i++)
         {
            Attribute attr = as.get(attrIds[i]);
            if (attr != null)
               rvalue.put(attr);
         }
         return rvalue;
      }
   }

   public Attributes getAttributes(Name name, String[] attrIds)
      throws NamingException
   {
      return getAttributes(name.toString(), attrIds);
   }

   /* methods that don't just throw an exception (but do nothing) */

   /* methods that just throw an exception */

   public void modifyAttributes(String name, int mod_op, Attributes attrs)
      throws NamingException
   {
      throw new AttributeModificationException();
   }

   public void modifyAttributes(Name name, int mod_op, Attributes attrs)
      throws NamingException
   {
      throw new AttributeModificationException();
   }

   public void modifyAttributes(String name, ModificationItem[] mods)
      throws NamingException
   {
      throw new AttributeModificationException();
   }

   public void modifyAttributes(Name name, ModificationItem[] mods)
      throws NamingException
   {
      throw new AttributeModificationException();
   }

   public void bind(String name, Object obj, Attributes attrs)
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public void bind(Name name, Object obj, Attributes attrs)
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public void rebind(String name, Object obj, Attributes attrs)
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public void rebind(Name name, Object obj, Attributes attrs)
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public DirContext createSubcontext(
      String name,
      Attributes attrs)
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public DirContext createSubcontext(
      Name name,
      Attributes attrs)
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public DirContext getSchema(String name)
       throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public DirContext getSchema(Name name)
       throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public DirContext getSchemaClassDefinition(String name)
       throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public DirContext getSchemaClassDefinition(Name name)
       throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   /* *************** searches *************** */

   public NamingEnumeration search(
      String name,
      Attributes matchingAttributes)
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public NamingEnumeration search(
      Name name,
      Attributes matchingAttributes)
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public NamingEnumeration search(
      String name,
      Attributes matchingAttributes,
      String[] attributesToReturn)
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public NamingEnumeration search(
      Name name,
      Attributes matchingAttributes,
      String[] attributesToReturn)
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public NamingEnumeration search(
      String name,
      String filter,
      SearchControls cons)
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public NamingEnumeration search(
      Name name,
      String filter,
      SearchControls cons)
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public NamingEnumeration search(
      String name,
      String filterExpr,
      Object [] filterArgs,
      SearchControls cons)
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

   public NamingEnumeration search(
      Name name,
      String filterExpr,
      Object [] filterArgs,
      SearchControls cons)
      throws NamingException
   {
      throw new OperationNotSupportedException();
   }

/* **************************************************************************
*  Helper methods
****************************************************************************/

   private Object objectFromName(String name, int returnType)
      throws NamingException
   {
      NameParser np = new FileSystemNameParser(
                              environment.getServerName(), 
                              true);
      Name ns = np.parse(name);
      int level1Type = -1;

      if (ns.size() < 3)
      {
         // find if they want the class, attribute or syntax definitions node

         for (int i = 0; i < bindings.length; i++)
         {
            if (bindings[i].getName ().equalsIgnoreCase(ns.get(0)))
            {
               level1Type = i;
               break;
            }
         }
         if (level1Type == -1)
            throw new NameNotFoundException();
      }

      if (ns.size() == 2)
      {
         /*
            they also are asking for an actual subclass under either the
            class, attribute or syntax definitions nodes.  All we will do
            is verify that the name is valid, and return an empty set if it
            is, otherwise we will throw an exception
         */

         int level2Type;
         switch (level1Type)
         {
            case 0:           // Class definitions
               level2Type = SchemaClassDef.typeFromName(
                                                ns.get(1));
               break;
            case 1:           // Attribute definitions
               level2Type = SchemaAttrDef.typeFromName(
                                                ns.get(1));
               break;
            case 2:           // Syntax definitions
               level2Type = SchemaSyntaxDef.typeFromName(
                                                ns.get(1));
               break;
            default:
               level2Type = -1;
               break;
         }
         if (level2Type == -1)
            throw new NameNotFoundException();

         switch (returnType)
         {
            case NCP_OBJECT:     // name class pair enumeration
               return (new EmptyNamingEnumerator ());
            case BINDING_OBJECT: // binding enumeration
               return (new EmptyNamingEnumerator ());
            case LOOKUP_OBJECT:
            case AS_OBJECT:
               DirContext ctx;
               switch (level1Type)
               {
                  case 0:
                     ctx = new SchemaClassDef(level2Type, environment);
                     break;
                  case 1:
                     ctx = new SchemaAttrDef(level2Type, environment);
                     break;
                  case 2:
                     ctx = new SchemaSyntaxDef(level2Type, environment);
                     break;
                  default:
                     throw new InvalidNameException();
               }
               if (returnType == LOOKUP_OBJECT)
                  return ctx;
               return ctx.getAttributes("");
            case NP_OBJECT:     // nameparser
               return new FileSystemNameParser(
                              environment.getServerName(), 
                              true);
            default:
               throw new InvalidNameException();
         }
      }

      if (ns.size() > 2)
         throw new NameNotFoundException();

      /*
         if execution gets to here, it must be a single name, return the
         right one.
      */

      switch (returnType)
      {
         case NCP_OBJECT:     // name class pair enumeration
         case BINDING_OBJECT: // binding enumeration
            switch (level1Type)
            {
               case 0:
                  return SchemaClassDef.getEnumeration(returnType,
                        environment);
               case 1:
                  return SchemaAttrDef.getEnumeration(returnType,
                        environment);
               case 2:
                  return SchemaSyntaxDef.getEnumeration(returnType,
                        environment);
               default:
                  throw new InvalidNameException();
            }
         case LOOKUP_OBJECT:
            switch (level1Type)
            {
               case 0:
                  return new SchemaClassDef(environment);
               case 1:
                  return new SchemaAttrDef(environment);
               case 2:
                  return new SchemaSyntaxDef(environment);
               default:
                  throw new InvalidNameException();
            }
         case AS_OBJECT:
            return new NAttributes(true);    // no attributes associated are with this level
         case NP_OBJECT:     // nameparser
            return new FileSystemNameParser(
                           environment.getServerName(), 
                           true);
         default:
            throw new InvalidNameException();
      }
   }


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

   /**
    *
    */
   class NamingEnumerator implements NamingEnumeration
   {
      private int count = 0;

      public boolean hasMoreElements ()
      {
         return (count < bindings.length);
      }

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

      public Object nextElement ()
      {
         try
         {
            return (bindings[count++]);
         }
         catch (ArrayIndexOutOfBoundsException e)
         {
            throw (new NoSuchElementException ());
         }
      }

      public Object next ()
         throws NamingException
      {
         return (nextElement ());
      }

      public void close ()
         throws NamingException
      {
         count = bindings.length;
      }

   } /* NamingEnumerator */

} /* Schema */


