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

  $Archive: /njcl_v2rmi/src/com/novell/service/file/nw/calls/DirectoryEntryInfoImpl.java $
  $Revision: 18 $
  $Modtime: 1/05/01 2:43p $
 
  Copyright (c) 1998 Novell, Inc.  All Rights Reserved.

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

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

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

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.NoSuchElementException;

import java.rmi.RemoteException;

import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.AttributeModificationException;
import javax.naming.directory.InvalidSearchFilterException;

import com.novell.service.jncp.NSIException;

import com.novell.service.session.SessionException;
import com.novell.service.session.xplat.CallsService;
import com.novell.service.jncpv2r.net.NetService;

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

import com.novell.service.file.nw.naming.FSAttribute;
import com.novell.service.file.nw.naming.SchemaAttrDef;
import com.novell.service.file.nw.naming.SchemaSyntaxDef;
import com.novell.service.file.nw.naming.FSEnvironment;

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

import com.novell.service.rfc1960.SearchStringComponent;

/**@internal
 * This class allows access to standard directory entry attributes such as
 * file size, owner, and name.
 *
 * <p>This class provides the implementation to get the actual values from
 * a NetWare server for a given directory entry.  The interface for
 * accessing these values is defined in the DirectoryEntryInformation class
 * in the com.novell.service.file package.
 *
 * <p>From the file system JNDI provider's perspective, this class fits in
 * as a single-valued attribute value.  When an attribute set is requested
 * from JNDI, all StaticAttributeValues are iterated through, and the
 * values returned by the StaticAttributeValue.nextElement() method
 * are then the JNDI attribute values stored in an instance of
 * javax.naming.directory.Attribute.  The JNDI attribute has the name
 * as defined in this class in the DirectoryEntryInformation.ATTRIBUTE_ID.
 * </p>
 *
 * @see com.novell.service.file.nw.naming.StaticAttributeValue
 * @see com.novell.service.file.DirectoryEntryInformation
 * @see com.novell.service.file.nw.naming.FSAttribute
 * @see com.novell.service.file.nw.naming.DirEntryDirContext
 */
public class DirectoryEntryInfoImpl
   extends DirectoryEntryInformation
   implements StaticAttributeValue
{

/* **************************************************************************
* Bit mask for file attributes
****************************************************************************/

   /**
    * File Attributes
    *
    * <p>(FA_NORMAL = 0x00)
    * </p>
    */
   public static final int FA_NORMAL =          0x00;

   /**
    * File Attributes
    *
    * <p>(FA_READ_ONLY = 0x01)
    * </p>
    */
   public static final int FA_READ_ONLY =       0x01;

   /**
    * File Attributes
    *
    * <p>(FA_HIDDEN = 0x02)
    * </p>
    */
   public static final int FA_HIDDEN =          0x02;

   /**
    * File Attributes
    *
    * <p>(FA_SYSTEM = 0x04)
    * </p>
    */
   public static final int FA_SYSTEM =          0x04;

   /**
    * File Attributes
    *
    * <p>(FA_EXECUTE_ONLY = 0x08)
    * </p>
    */
   public static final int FA_EXECUTE_ONLY =    0x08;

   /**
    * File Attributes
    *
    * <p>(FA_DIRECTORY = 0x10)
    * </p>
    */
   public static final int FA_DIRECTORY =       0x10;

   /**
    * File Attributes
    *
    * <p>(FA_NEEDS_ARCHIVED = 0x20)
    * </p>
    */
   public static final int FA_NEEDS_ARCHIVED =  0x20;

   /**
    * File Attributes
    *
    * <p>(FA_SHAREABLE = 0x80)
    * </p>
    */
   public static final int FA_SHAREABLE =       0x80;

/* **************************************************************************
* private bit masks for return info control from NWGetNSEntryInfo
****************************************************************************/

   public static final int IM_ENTRY_NAME =      0x0001;
   static final int IM_SPACE_ALLOWED =   0x0002;
   public static final int IM_ATTRIBUTES =      0x0004;
   static final int IM_SIZE =            0x0008;
   static final int IM_TOTAL_SIZE =      0x0010;
   static final int IM_EA =              0x0020;
   static final int IM_ARCHIVE =         0x0040;
   static final int IM_MODIFY =          0x0080;
   static final int IM_CREATION =        0x0100;
   static final int IM_OWNING_NAMESPAC = 0x0200;
   static final int IM_DIRECTORY =       0x0400;
   static final int IM_RIGHTS =          0x0800;

/* **************************************************************************
   private state data members
****************************************************************************/

   private FSEnvironment environment;
   private String fullNodeName;
   private String nameSpace;
   private NetService netService;
   private CallsService callsService;
   private boolean sessionIsNDS;

   private boolean hasMoreElements = true;
   private final int refreshMask =
                        IM_SPACE_ALLOWED |
                        IM_ATTRIBUTES |
                        IM_SIZE |
                        IM_TOTAL_SIZE |
                        IM_CREATION |
                        IM_MODIFY |
                        IM_ARCHIVE |
                        IM_RIGHTS |
                        IM_DIRECTORY |
                        IM_EA |
                        IM_OWNING_NAMESPAC |
                        IM_ENTRY_NAME;

/* **************************************************************************
   Public Constructor
****************************************************************************/

   /**
    * Constructs a DirectoryEntryInfoImpl object.
    *
    * @param environment         The JNDI DSContext's environment associated
    *                            with this attribute and this static
    *                            attribute value.
    *
    * @exception NSIException    If an error occurs.
    */

   public DirectoryEntryInfoImpl(FSEnvironment environment)
      throws NSIException
   {
      this.environment = environment;
      fullNodeName = environment.getFullNodeName();
      nameSpace = environment.getNameSpace();
      try
      {
         this.callsService = environment.getCallsService();
         this.netService = environment.getNetService();
         this.sessionIsNDS = environment.sessionIsNDS();
      } catch (NamingException e)
      {
         throw new NSIException("" + e, e);
      }
   }

   /**
    * Constructs a DirectoryEntryInfoImpl from a DirectoryEntryInformation 
    * object.
    *
    * @param dei                 The DirectoryEntryInformation object to 
    *                            clone into a DirectoryEntryInfoImpl object.
    */

   private DirectoryEntryInfoImpl(
      DirectoryEntryInformation dei,
      int creatorID,
      int modifierID,
      int archiverID)
   {
      super(
         dei.getSpaceAlloc(),
         dei.getAttributes(),
         dei.getFlags(),
         dei.getDataStreamSize(),
         dei.getTotalStreamSize(),
         dei.getNumberOfStreams(),
         dei.getCreationTime(),
         dei.getCreationDate(),
         dei.getCreator(),
         dei.getModifyTime(),
         dei.getModifyDate(),
         dei.getModifier(),
         dei.getLastAccessDate(),
         dei.getArchiveTime(),
         dei.getArchiveDate(),
         dei.getArchiver(),
         dei.getInheritedRightsMask(),
         dei.getDirEntNum(),
         dei.getDosDirNum(),
         dei.getVolNumber(),
         dei.getEADataSize(),
         dei.getEAKeyCount(),
         dei.getEAKeySize(),
         dei.getNSCreator(),
         dei.getNameLength(),
         dei.getEntryName(),
         dei.getModifySearchAttributes());
         this.creatorID = creatorID;
         this.modifierID = modifierID;
         this.archiverID = archiverID;
   }

/* **************************************************************************
   Enumeration implementation
****************************************************************************/

   /**
    * Tests whether the attribute value has already been returned.
    *
    * <p>Because this directory entry information matches directory entries
    * one-to-one, there is only ever one value returned from this
    * classes enumeration.
    * </p>
    *
    * @return                    true if nextElement() has never been called,
    *                            otherwise returns false.
    *
    * @see #nextElement
    * @see #buildAttribute
    */
   public boolean hasMoreElements()
   {
      return hasMoreElements;
   }

   /**
    * Retrieves the attribute value, if it has not yet been retrieved.
    *
    * <p>The StaticAttributeValue interface also declares this method for
    * lazy evaluation of jndi attribute values.  The buildAttribute method
    * will return a FSAttribute which will have a reference to this object.
    * The FSAttribute.getValues method will return an enumerator which will
    * forward the hasMoreElements and nextElement calls back into this
    * object.  One, and only one object will ever be returned from this method.
    * </p>
    *
    * @return                    DirectoryEntryInformation object.
    * @exception  NoSuchElementException When the directory entry information
    *                            object has already been retrieved.
    * @exception  NSIException   refresh can throw a NSIException.
    *
    * @see #hasMoreElements
    * @see #buildAttribute
    * @see com.novell.service.file.nw.DirectoryEntryInformation
    */
   public Object nextElement()
   {
      if (!hasMoreElements)
         throw new NoSuchElementException();

      hasMoreElements = false;   // only give them one

      refresh(refreshMask);

      return(this);
   }

/* **************************************************************************
   StaticAttributeValue implementation
****************************************************************************/

   /**
    * Builds and returns a JNDI Attribute for this attribute value.
    *
    * @return                    The JNDI Attribute that contains all
    *                            values accessible from this static
    *                            attribute value.
    * @exception NamingException When an error occurs building the new
    *                            JNDI attribute.
    *
    * @see com.novell.service.file.nw.naming.DirEntryDirContext#c_getAttributes
    */
   public Attribute buildAttribute()
      throws NamingException
   {
      DirectoryEntryInfoImpl dei = null;

      try
      {
         dei = new DirectoryEntryInfoImpl(environment);
      } catch (NSIException nsie)
      {
         NamingException ne = new NamingException(nsie.getMessage());
         ne.setRootCause(nsie);
         throw ne;
      }

      return new 
         FSAttribute(
            ATTRIBUTE_ID,
            dei,
            new SchemaAttrDef(SchemaAttrDef.DEI_ATTRDEF, environment),
            new SchemaSyntaxDef(SchemaAttrDef.DEI_ATTRDEF, environment));
   }

   /**
    * Adds all values in a JNDI Attribute to this static attribute value.
    *
    * <p>This static attribute value only supports one value.
    * </p>
    *
    * @param      attr           The attribute containing the values to be
    *                            added.
    * @exception AttributeModificationException Always throws this exception
    *                            because there can never be less than
    *                            one set of directory entry information
    *                            for any given directory entry.
    */
   public void addAttribute(Attribute attr)
      throws NamingException
   {
      throw new AttributeModificationException();
   }

   /**
    * Deletes all values in a JNDI Attribute from this static attribute
    * value.
    *
    * <p>This static attribute value only supports one value.
    * </p>
    *
    * @param      attr           The attribute containing the values to be
    *                            removed.
    * @exception AttributeModificationException Always throws this exception
    *                            because there can never be less than
    *                            one set of directory entry information
    *                            for any given directory entry.
    */
   public void deleteAttribute(Attribute attr)
      throws NamingException
   {
      throw new AttributeModificationException();
   }

   /**
    * Modifies the value of this static attribute value as specified by the
    * values in the specified JNDI Attribute.
    *
    * <p>Modifies the current DirectoryEntryInformation state to the value of
    * the attribute.  Not all fields of the DirectoryEntryInformation can be
    * modified, only the fields listed here will be modified, all other
    * fields will be ignored.
    * <ul>
    * <li><i>attributes</i>
    * <li><i>createDate</i>
    * <li><i>createTime</i>
    * <li><i>creatorID</i>
    * <li><i>modifyDate</i>
    * <li><i>modifyTime</i>
    * <li><i>modifierID</i>
    * <li><i>archiveDate</i>
    * <li><i>archiveTime</i>
    * <li><i>archiverID</i>
    * <li><i>lastAccessDate</i>
    * <li><i>inheritedRightsMask - top 16 bits is revoke,
    *        bottom 16 is grant</i>
    * <li><i>inheritanceRevokeMask</i>
    * <li><i>maximumSpace</i>
    * <li><i>modifySearchAttributes</i>
    * </ul>
    *
    * <p>The modifySearchAttributes is used to determine the types of
    * directory entries to be searched for the modify, namely, SA_HIDDEN,
    * SA_SYSTEM, SA_SUBDIR_FILES, and SA_SUBDIR_ONLY.  This field defaults
    * to the first three of these.
    * </p>
    *
    * @param      attr           The attribute containing the values to be
    *                            modified.  There can only be one value for
    *                            this static attribute value.
    * @exception AttributeModificationException When the read-only fields
    *                            of the value in specified attribute differ
    *                            with the read-only fields of this static
    *                            attribute value.
    *
    * @see com.novell.service.file.nw.DirectoryEntryInformation
    */
   public void modifyAttribute(Attribute attr)
      throws NamingException
   {
      Enumeration enum = attr.getAll();
      while (enum.hasMoreElements())
      {
         DirectoryEntryInformation info = (DirectoryEntryInformation)
                                          enum.nextElement();
         try
         {
            modify(info);
         } catch (NSIException e)
         {
            NamingException ne = new AttributeModificationException();
            ne.setRootCause(e);
            throw ne;
         }
      }
   }

   /**
    * Gets this static attribute values Attribute ID.
    *
    * <p>This attribute ID is reflected in the JNDI world as the
    * attribute ID in the attribute set of a directory entry.
    * </p>
    *
    * @return                    The attribute ID.
    *
    * @see com.novell.service.file.nw.DirectoryEntryInformation#ATTRIBUTE_ID
    */
   public String getID()
   {
      return(ATTRIBUTE_ID);
   }

   /**
    * Compares the object in the search string component with this static
    * attribute value.
    *
    * <p>This method will compare the value object against this static
    * attribute value's current value, in the manner specified in the
    * search string component, and return the result of this compare.
    * </p>
    *
    * @param ssc                 The search string component to use for the
    *                            compare.
    * @return                    true if the operation compares true, false
    *                            otherwise.
    *
    * @exception InvalidSearchFilterException When the search filter
    *                            is invalid.
    */
   public boolean compare(SearchStringComponent ssc)
      throws NamingException
   {
      try
      {
         return equals(ssc);
      } catch (IllegalArgumentException e)
      {
         InvalidSearchFilterException isfe =
            new InvalidSearchFilterException();

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

   /**
    * Compares the object in the search string component with this static
    * attribute value.
    *
    * <p>This method will compare the value object against this static
    * attribute value's current value, in the manner specified in the
    * search string component, and return the result of this compare.
    * </p>
    *
    * @param ssc                 The search string component to use for the
    *                            compare.
    * @return                    true if the operation compares true, false
    *                            otherwise.
    *
    * @exception NSIException If refresh fails.
    * @exception IllegalArgumentException If the search string component
    *                            operation type is not supported.
    */
   public boolean equals(SearchStringComponent ssc)
   {
      if (ssc == null)
         return false;

      int type = ssc.getOperationType();

      if (type == ssc.PRESENT)
         return true;

      if (type != ssc.EQUALS && type != ssc.SUBSTRING)
         return false;

      DirectoryEntryInformation dei = null;
      String name = null;
      boolean nameOnly = true;
      if (ssc.operandReplacement())
      {
         dei = (DirectoryEntryInformation) ssc.getReplacementObject();
         if (dei == null)
            throw new IllegalArgumentException(ssc.getOperation());
         synchronized(dei)
         {
            name = dei.getEntryName();
         }
         nameOnly = false;
      }else
         name = ssc.getOperand();

      refresh(refreshMask);
      if (type == ssc.EQUALS)
      {
         if (nameOnly)
            return name.equalsIgnoreCase(getEntryName());
         try
         {
            synchronized(dei)
            {
               return equals(dei);
            }
         } catch (Exception e)
         {
            return false;
         }
      }

      if (type == ssc.SUBSTRING)
      {
         return SearchStringComponent.compareSubString(
                                          name,
                                          getEntryName(),
                                          true);
      }
      return false;
   }

   /**
    * Returns the static interface object.
    *
    * <p>This method returns the appropriate object for the static interface
    * to this attribute value.
    * </p>
    *
    * @return                    DirectoryEntryInformation.
    *
    */
   public Object getStaticInterface()
   {
     DirectoryEntryInfoImpl dei = null;
     dei = new DirectoryEntryInfoImpl(environment);
      dei.refresh(refreshMask);
      return dei;
   }


/* **************************************************************************
   state update methods
****************************************************************************/

   /**
    * Refreshes the state of this static attribute value.
    *
    * <p>Causes the data to be retrieved from the Netware server.
    * </p>
    *
    * @param      mask           The return mask to use in
    *                            NWGetNSEntryInfo().
    * @exception  NSIException   When an error occurs reading the data
    *                            from the server.
    */
   public void refresh(int mask)
      throws NSIException
   {
      int attributes = TA_ALL;
      int ccode;

      try
      {
         callsService.getNSEntryInfo(
            0,
            fullNodeName,
            NameSpace.nameToNumber(nameSpace),
            NameSpace.nameToNumber(nameSpace),
            attributes,
            mask,
            this);
      }
      catch (Exception e)
      {
         throw new NSIException(e.getMessage(), 0, e);
      }

      // now fill out the creator, modifier and archiever ObjectNames

      ObjectName on;

      try
      {
         on = ObjectNameImpl.objectIdToObjectName(
                                 creatorID, 
                                 callsService, 
                                 netService,
                                 sessionIsNDS);

         setCreator(on.getName());
      } catch (NSIException nsie)
      {
         // the direntry might have no creator recorded
         setCreator("");
      }
      try
      {

         on = ObjectNameImpl.objectIdToObjectName(
                                 modifierID, 
                                 callsService,
                                 netService,
                                 sessionIsNDS);
         setModifier(on.getName());
      } catch (NSIException nsie)
      {
         // the direntry might not have been modified yet
         setModifier("");
      }
      try
      {
         on = ObjectNameImpl.objectIdToObjectName(
                                 archiverID, 
                                 callsService,
                                 netService,
                                 sessionIsNDS);
         setArchiver(on.getName());
      } catch (NSIException nsie)
      {
         // the direntry might not have been archived yet
         setArchiver("");
      }
   }

   /**
    * Modify this static attribute value's server-side data using
    * the data provided in the specified directory entry information object.
    *
    * <p>Sends the data that is currently in the specified directory
    * entry information object to NWSetNSEntryDOSInfo, thereby modifing
    * the attributes.
    * </p>
    *
    * @param      mask           The return mask to use in
    *                            NWSetNSEntryDOSInfo().
    * @exception  NSIException   When an error occurs setting the data
    *                            to the server.
    */

   public void modify(DirectoryEntryInformation info)
      throws NSIException
   {
      synchronized(info)
      {
         int modifyMask = info.getModifyMask();
         if (modifyMask == 0)
            return;

         int creatorID;
         int modifierID;
         int archiverID;

         if ((modifyMask & DM_CREATOR) != 0)
            creatorID  = ObjectNameImpl.nameToObjectId(
                                             info.getCreator(),
                                             callsService,
                                             netService,
                                             sessionIsNDS);
         else
            creatorID = 0;

         if ((modifyMask & DM_ARCHIVER) != 0)
            archiverID  = ObjectNameImpl.nameToObjectId(
                                             info.getArchiver(),
                                             callsService,
                                             netService,
                                             sessionIsNDS);
         else
            archiverID = 0;

         if ((modifyMask & DM_MODIFIER) != 0)
            modifierID  = ObjectNameImpl.nameToObjectId(
                                             info.getModifier(),
                                             callsService,
                                             netService,
                                             sessionIsNDS);
         else
            modifierID = 0;

         DirectoryEntryInfoImpl dei = null;
         if (info instanceof DirectoryEntryInfoImpl)
         {
            dei = (DirectoryEntryInfoImpl)info;
            dei.creatorID = creatorID;
            dei.modifierID = modifierID;
            dei.archiverID = archiverID;
         }else
            dei = new DirectoryEntryInfoImpl(
                           info, 
                           creatorID, 
                           modifierID, 
                           archiverID);
            

         try
         {
            callsService.setNSEntryDOSInfo(
                           0,
                           fullNodeName,
                           NameSpace.nameToNumber(nameSpace),
                           info.getModifySearchAttributes(),
                           info.getModifyMask(),
                           dei);
         }
         catch (Exception e)
         {
            throw new NSIException(e.getMessage(), 0, e);
         }
      }
   }
}
