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

  $Archive: /njcl/src/com/novell/service/file/nw/calls/BinderyName.java $
  $Revision: 3 $
  $Modtime: 4/13/98 9:59a $
 
  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;

/** @internal
 * Novell bindery object name/type support class
 * 
 * <p>This class provides Novell Bindery Object name support.  The bindery
 * object name consists of a name, type and objectId.  This class has also 
 * provided support for a nameType which is simply a string the combines the
 * name and type in the "name+type" format.  Typically the objectId will only 
 * be used by name space provider SPI's, and will not be of interest to the 
 * user application layer.
 * </p>
 *
 */
 
public class BinderyName extends ObjectName
{
   private int type;
   private int objectId;
   private String nameType;

/* **************************************************************************
* public constructors (published)
****************************************************************************/

   /**
    * Default constructor
    * 
    * <p>Construct this object.  The bindery object name, type and objectId 
    * is currently unknown.  The getName method will return null until the 
    * setName method is called.  Type and objectId will be initialized to
    * -1. nameType will be initialized to null.
    * </p>
    */
    
   public BinderyName()
   {
      this(null, -1, -1);
   }

   /**
    * Bindery Object Name known constructor
    * 
    * <p>Construct this object, with the bindery object type, and objectId
    * currently unknown.  Type and objectId will be initialized to  -1.  
    * nameType will be initialized to null.
    * </p>
    *
    * @param name                Bindery Object Name
    */
    
   public BinderyName(String name)
   {
      this(name, -1, -1);
   }

   /**
    * Bindery Object Name+type known constructor
    * 
    * <p> The name coming in is in the "name+type" format.
    * Construct this object, by parsing the name into the bindery object 
    * name and the bindery object type.  This method will throw a 
    * IllegalArgumentException if the name is not in the expected format 
    * (ie. name+type).  
    * The boolean input parameter was needed to distinguish this constructor
    * from the BinderyName(String) constructor.  The actual value of this
    * parameter will be ignored.
    * </p>
    *
    * @param name                Name+type formated name
    *
    * @param nameType            Constructor signature that identifies that
    *                            the name attribute is expected to be a valid
    *                            name+type format and that a 
    *                            IllegalArgumentException will be thrown if 
    *                            it is not a valid format.  The actual value 
    *                            of this parameter will be ignored.
    *
    * @exception IllegalArgumentException
    *
    * @see #getNameType
    * @see #setName
    * @see #setType
    * @see #setNameType
    */
    
   public BinderyName(String name, boolean nameType)
   {
      objectId = -1;
      buildBinderyName(name);
   }

   /**
    * Bindery Object Type known constructor
    * 
    * <p>Construct this object, with the bindery object name currently 
    * unknown.  The getName method will return null until the setName method
    * is called.  objectId will be initialized to -1.
    * </p>
    * @param type                bindery object type
    */
    
   public BinderyName(int type)
   {
      this(null, type, -1);
   }

   /**
    * Bindery Object Name and Bindery Object Type known constructor
    * 
    * <p>The objectId will be iniitlized to -1.
    * </p>
    * @param name                bindery object name
    * @param type                bindery object type
    */
    
   public BinderyName(String name, int type)
   {
      this(name, type, -1);
   }


   /**
    * Full Constructor
    * 
    * <p>The name, type and objectId state data members will all be set to
    * the appropriate input parameters.
    * </p>
    * @param name                bindery object name
    * @param type                bindery object type
    * @param objectId            bindery object ID
    */
    
   public BinderyName(String name, int type, int objectId)
   {
      super(name);
      this.type = type;
      this.objectId = objectId;
      buildNameType();
   }


/* **************************************************************************
* public accessors (published)
****************************************************************************/

   /**
    * Sets the bindery name.
    * 
    * <p>Sets the bindery name and causes the nameType (see getNameType) to 
    * be created if the type has already been set.  The name should not be
    * in the name+type format for this method, use the setNameType method
    * if the name is already in this format.
    * </p>
    *
    * @param name                The bindery name
    *
    * @see #getNameType
    * @see #setName
    * @see #setType
    * @see #setNameType
    */
    
   public void setName(String name)
   {
      super.setName(name);
      buildNameType();
   }

   /**
    * Gets the bindery object type.
    * 
    * <p>Returns the bindery object type, which is the type handed into the 
    * constructor or set with the last call to the setType method.
    * </p>
    *
    * @return                    The bindery object type
    */
    
   public int getType()
   {
      return type;
   }


   /**
    * Sets the bindery object type.
    * 
    * <p>Sets the object type.
    * </p>
    *
    * @param type                The bindery object type
    * @return                     
    * @exception 
    *
    */
    
   public void setType(int type)
   {
      this.type = type;   
      buildNameType();
   }

   /**
    * Gets the object ID
    * 
    * <p>This method returns the current value of object ID.  This 
    * is only intended to be used by jndi name space providers and should not
    * be used by the application layer.  At present, the value of objectID
    * is actually set by a native method in the namespace provider, and this
    * method is used to convert the object ID to a name, which is
    * what the user application layer is concerned about.
    * </p>
    *
    * @return                    The current value of objectId
    */
    
   public int getObjectId()
   {
      return objectId;
   }

   /**
    * Sets the object ID
    * 
    * <p>This method returns the current value of object ID.  This 
    * is only intended to be used by jndi name space providers and should not
    * be used by the application layer.
    * </p>
    *
    * @param objectId            The value to set objectId to
    */

   public void setObjectId(int objectId)
   {
      this.objectId = objectId;
   }

   /**
    * Returns the nameType
    * 
    * <p>This method will return a "name+type" formated name if the 
    * bindery name and type have been set, either through the constructors or
    * through the accessor methods.  If either the bindery name or the type
    * have not been set, this method will return a null.
    * </p>
    * <p>Also note that if the bindery name and type have both been set, and 
    * either the setName or setType methods are called, the nameType
    * will be recalculated for each subsequent call.
    * </p>
    *
    * @return                    null if bindery name and type have not been
    *                            setup, valid nameType if they have
    *
    * @see #setName
    * @see #setType
    * @see #setNameType
    */
    
   public String getNameType()
   {
      return nameType;
   }

   /**
    * Sets the bindery nameType
    * 
    * The name coming in is in the "name+type" format
    * 
    * <p>The name is expected to be in the "name+type" format.  This method
    * will parse the name into the bindery object name and the bindery object 
    * type, causing these data members to be set accordingly.  This method 
    * will throw a IllegalArgumentException if the name is not in the 
    * expected format (ie. name+type).  
    *
    * @param name                The name+type string
    *
    * @exception IllegalArgumentException
    *
    * @see #getNameType
    * @see #setName
    * @see #setType
    */
    
   public void setNameType(String name)
   {
      buildBinderyName(name);
   }

   /**
    * Tells if the given name is a valid name type
    * 
    * <p>If the given name is in the appropriate Name+Type format
    * this method will return true.  Otherwise false is returned.
    * </p>
    *
    * @param name                The name to check for name+type format.
    * @return                    True if it is in the name+type format. 
    *                            returns false otherwise.
    */
    
   public static boolean isNameType(String name)
   {
      if (name == null)
         return false;

      int offset = name.lastIndexOf("+");
      if (offset == -1)
         return false;

      try
      {
         Integer t = Integer.valueOf(name.substring(offset + 1), 16);
         int type = t.intValue();
      } catch (NumberFormatException nfe)
      {
         return false;
      }
      return true;
   }

/* **************************************************************************
* public methods (overridden)
****************************************************************************/

   public String toString()
   {
      return new String(
                     "Name: " + getName() + " Type: " + type + 
                     " ObjectId: " + getObjectId());
   }

   public Object clone()
   {
      BinderyName obj = (BinderyName) super.clone();
      obj.type = type;
      obj.objectId = objectId;
      obj.nameType = nameType;
      return obj;
   }

   public boolean equals(Object obj)
   {
      if (obj == this)
         return true;

      if (!(obj instanceof BinderyName))
         return false;

      BinderyName bn = (BinderyName)obj;

      if (!bn.getName().equalsIgnoreCase(getName()))
         return false;
      
      if (bn.getType() != type)
         return false;

      if (bn.getObjectId() != getObjectId())
         return false;
      return true;
   }

/* **************************************************************************
* private methods
****************************************************************************/

   private void buildNameType()
   {
      //combine name and type into jndiName

      /*
         There is a race condition when instantiating a BinderyNameImpl.  The
         following happens.

         1. BinderyNameImpl constructor does a super(name, type, objectId)
            causing the BinderyName constructor to be hit.

         2. BinderyName constructor does a super(name)

         3. BinderyName constructor then calls this method.

         4. BinderyNameImpl has overridden the getName method to return a 
            name+type string, which this method has not set up yet.

         bottom line is the getName call below has to call the 
         ObjectName.getName method (leave the super on).
      */

      if (super.getName() == null || type == -1)
      {
         nameType = null;
         return;
      }
      nameType = new String(super.getName() + "+" + Integer.toHexString(type));
   }

   private void buildBinderyName(String name)
   {
      if (name == null)
         throw new IllegalArgumentException(name);

      int offset = name.lastIndexOf("+");
      if (offset == -1)
         throw new IllegalArgumentException(name);

      try
      {
         Integer t = Integer.valueOf(name.substring(offset + 1), 16);
         type = t.intValue();
      } catch (NumberFormatException nfe)
      {
         throw new IllegalArgumentException(name);
      }
      super.setName(new String(name.substring(0, offset)));
      nameType = name;
   }
}   