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

  $Archive: /njcl_v2rmi/src/com/novell/service/nds/NdsTimestamp.java $
  $Revision: 9 $
  $Modtime: 11/15/00 3: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.nds;


import java.io.Serializable;

import java.util.Date;
import java.util.Enumeration;
import java.util.GregorianCalendar;
import java.util.TimeZone;
import java.util.Vector;

import com.novell.service.schema.SchemaValue;
import com.novell.service.schema.SchemaComposite;


/**
 * Provides access to attribute values of the Timestamp
 * syntax. It is used for attributes whose values mark the time
 * when a particular event occurred or will occur. A time stamp
 * value has three components:
 * <ol><li>The wholeSeconds field consists of the whole number
 * of seconds, where zero equals 12:00 midnight, January 1, 1970,
 * UTC.
 * <li>The replicaNum field identifies the server that minted the
 * timestamp. A replica number is assigned whenever a replica is
 * created on a server.
 * <li>The eventID field  is an integer that orders events
 * occurring within the same whole-second interval. The event
 * number restarts at one for each new second.
 * </ol>
 * <p>The initial NULL value of a time stamp has seconds = 1 and
 * event = 0. Values can be skipped, but must not be reused. An
 * unknown event is coded as 0xFFFF.</p>
 * <p>Time stamps can be compared for equality and for ordering.
 * Two Timestamp values are matched for equality by comparing
 * the whole seconds fields and then the event fields. If the
 * whole seconds fields are unequal, order is determined by that
 * field alone. If the seconds fields are equal and the event
 * fields are unequal, order is determined by the event fields.
 * If both fields are equal, the time stamps are equal. For
 * ordering comparisons, the Timestamp value is treated as a
 * 64-bit unsigned integer, with seconds as most significant.
 * 
 * <p>Matching Rules: Equality and Ordering</p>
 * 
 * @see NdsSyntaxId
 */
public class NdsTimestamp
   implements NdsAttributeValue, SchemaComposite, Cloneable, Serializable
{
   private static final int matchingRules = equality|ordering;

   private static String[] compositeIds;

   static {
      compositeIds = new String[3];
      compositeIds[0] = "wholeSeconds";
      compositeIds[1] = "replicaNumber";
      compositeIds[2] = "eventId";
   }

  /**
   * @internal
   */
   protected long wholeSeconds;

  /**
   * @internal
   */
   protected int replicaNumber;

  /**
   * @internal
   */
   protected int eventId;

   private String name;


   /**
   * Constructs an NdsTimestamp object based on the specified
   * wholeSeconds, eventId, and replicaNumber parameters.
   * 
   * @param wholeSeconds  The whole number of seconds stored as a
   *                      long value.
   * @param eventId       An integer that orders events occurring
   *                      within the same whole-second interval
   *                      stored as an int value.
   * @param replicaNumber The replica number identifying the server
   *                      that crated the timestamp stored as an int
   *                      value.
   */
   public NdsTimestamp (
         long wholeSeconds,
         int eventId,
         int replicaNumber)
   {
      this.wholeSeconds = wholeSeconds;
      this.replicaNumber = replicaNumber;
      this.eventId = eventId;
   }

  /**
   * @internal
   */
   protected NdsTimestamp ()
   {
      this.wholeSeconds = -1;
      this.replicaNumber = -1;
      this.eventId = -1;
   }

   /*
    * Private constructor; not documented!
    */
   private NdsTimestamp (
         String name,
         long wholeSeconds,
         int eventId,
         int replicaNumber)
   {
      this.name = name;
      this.wholeSeconds = wholeSeconds;
      this.replicaNumber = replicaNumber;
      this.eventId = eventId;
   }


  /**
   * @internal
   */
   protected NdsTimestamp (
         NdsTimestamp aTimestamp)
   {
      this.wholeSeconds = aTimestamp.wholeSeconds;
      this.replicaNumber = aTimestamp.replicaNumber;
      this.eventId = aTimestamp.eventId;
   }


   // ******************** SchemaValue Interface ********************

  /**
   * Compares two objects for ordering, or compares two strings
   * lexicographically. For the Timestamp syntax, the compareTo
   * method compares this NDS Object with a reference NDS Object
   * contained in the anObject parameter. The comparison is done
   * according to the ordering matching rule for this syntax.
   * 
   * @param anObject The reference NDS Object with which
   *                 to compare.
   *
   * @return An int set to 0 if the reference Object is equal to this
   *         Object; an int <0 if the reference Object is less than
   *         this Object; an int >0 if the reference Object is greater
   *         than this Object.
   *
   * @exception IllegalArgumentException The passed-in object is of
   *            a different type than this NDS Object.
   */
   public int compareTo (
         Object anObject)
      throws Exception
   {
      throw (new Exception ());
   }

  /**
   * Compares two Objects for equality. The equals method compares
   * this object value with the value of the reference object in
   * the anObject parameter.
   *
   * <p>The equals method implements the most discriminating possible
   * equivalence relation on objects; that is, for any reference values
   * X and Y, this method returns TRUE if and only if X and Y refer to
   * the same object (X==Y has the value TRUE).</p>
   * 
   * @param anObject The reference object with which to compare.
   *
   * @return A boolean set to TRUE if and only if the argument is not
   *         NULL and is an NDS integer object that contains the
   *         same value as this object, otherwise set to FALSE.
   */
   public boolean equals (
         Object anObject)
   {
      if ((anObject != null) && (anObject instanceof NdsTimestamp))
      {
         NdsTimestamp anotherTimestamp = (NdsTimestamp) anObject;

         if (wholeSeconds != anotherTimestamp.wholeSeconds)
         {
            return (false);
         }
         if (replicaNumber != anotherTimestamp.replicaNumber)
         {
            return (false);
         }
         if (eventId != anotherTimestamp.eventId)
         {
            return (false);
         }
         return (true);
      }
      return (false);
   }

  /**
   * @internal
   */
   public String getName ()
   {
      return (name);
   }

  /**
   * @internal
   */
   public String getSyntaxId ()
   {
      return (NdsSyntaxId.TIMESTAMP);
   }


   // ******************** SchemaComposite Interface ********************

   /**
    * Returns the number of elements in this sequence.
    *
    * @return The number of elements in this sequence as an int.
    */
   public int count ()
   {
      return (compositeIds.length);
   }

   /**
   * Returns a SchemaValue object based on the given passed-in
   * string containing the element value. The SchemaValue object
   * is a Java language data type.
   *
   * @param compositeId The passed-in string containing the element
   *                    value.
   *
   * @return The SchemaValue object containing the wholeSeconds,
   *         replicaNumber, and eventId, or NULL.
   */
   public SchemaValue getValue (
         String compositeId)
   {
      if (compositeId.equalsIgnoreCase (compositeIds[0]))
      {
         // getWholeSeconds ();
         return (new NdsLong (compositeIds[0], wholeSeconds));
      }
      else if (compositeId.equalsIgnoreCase (compositeIds[1]))
      {
         // getReplicaNumber ();
         return (new NdsInteger (compositeIds[1], replicaNumber));
      }
      else if (compositeId.equalsIgnoreCase (compositeIds[2]))
      {
         // getEventId ();
         return (new NdsInteger (compositeIds[2], eventId));
      }
      return (null);
   }

  /**
   * Returns an Enumeration of composite IDs.
   *
   * @return An Enumeration of composite IDs.
   */
   public Enumeration getValueIds ()
   {
      Vector vector = new Vector ();

      for (int i = 0; i < compositeIds.length; i++)
         vector.addElement (compositeIds[i]);
      return (vector.elements ());
   }

   /**
    * Returns an enumeration of the elements in this composite.
    *
    * @return An enumeration of the composite elements
    *         (wholeSeconds, replicaNumber and eventId).
    */
   public Enumeration getValues ()
   {
      Vector values = new Vector ();

      values.addElement (new NdsLong (compositeIds[0], wholeSeconds));
      values.addElement (new NdsInteger (compositeIds[1], replicaNumber));
      values.addElement (new NdsInteger (compositeIds[2], eventId));
      return (values.elements ());
   }


   // ******************** NdsAttributeValue Interface ********************

  /**
   * Compares two Objects using the approximate matching rule.
   * 
   * The approximate method does not apply to the Timestamp
   * syntax because this syntax does not support the approximate
   * equals matching rule.
   */
   public boolean approximate (
         Object anObject)
      throws Exception
   {
      throw (new Exception ());
   }

  /**
   * Returns the int that represents the NDS syntax ID.
   *
   * @return The syntax ID as an int.
   */
   public int getNdsSyntaxId ()
   {
      return (NdsSyntaxId.TIMESTAMP_ID);
   }

  /**
   * Checks to see if this object supports the specified
   * matching rules. The Timestamp syntax supports the equality
   * and ordering matching rules.
   *
   * @param matchingRules The set of matching rules to check.
   *
   * @return A Boolean set to TRUE if the matching rules for this
   *         object are equal to the matchingRules parameter.
   */
   public boolean supportsMatchingRules (
         int matchingRules)
   {
      if ((matchingRules & this.matchingRules) == matchingRules)
      {
         return (true);
      }
      return (false);
   }


   // ******************** Object Class ********************

  /**
   * Creates a new object of the same class as this object. It
   * then initializes each of the new object's fields by assigning
   * them the same value as the corresponding fields in this object.
   * No constructor is called.
   *
   * @return A clone of this object instance containing the cloned
   *         syntax.
   */
   public Object clone ()
   {
      try
      {
         NdsTimestamp timeStamp = (NdsTimestamp) super.clone ();

         timeStamp.wholeSeconds = this.wholeSeconds;
         timeStamp.replicaNumber = this.replicaNumber;
         timeStamp.eventId = this.eventId;

         timeStamp.name = this.name;

         return (timeStamp);
      }
      catch (CloneNotSupportedException e)
      {
         // this shouldn't happen, since we are Cloneable
         throw (new InternalError ());
      }

   } /* clone () */

  /**
   * Generates a string representation of the object. It
   * returns a string that textually represents the object.
   * The result should be a concise but informative
   * representation that is easy to read.
   *
   * @return The String representation of the object.
   */
   public String toString ()
   {
      return (
         String.valueOf (wholeSeconds) + ";" +
         String.valueOf (replicaNumber) + ";" +
         String.valueOf (eventId));
   }


   // ******************** NdsTimestamp Class ********************

  /**
   * Returns the whole seconds stored as a long value in the
   * object data member.
   * 
   * @return The whole seconds as a long value.
   */
   public long getWholeSeconds ()
   {
      return (wholeSeconds & INT_MASK);
   }

  /**
   * Returns the replica number stored as an int value in the
   *         object data member.
   * 
   * @return The replica number as an int value.
   */
   public int getReplicaNumber ()
   {
      return (replicaNumber);
   }

  /**
   * Returns event ID stored as an int value in the object data
   * member.
   * 
   * @return The Event ID  as an int value.
   */
   public int getEventId ()
   {
      return (eventId);
   }

  /**
   * Returns the Date stored as a Date value in the object
   * data member. It converts time from milliseconds to seconds.
   *
   * @return The date and time as a Date value.
   */
   public Date getDate ()
   {
      Date date = new Date ();

      date.setTime (wholeSeconds * 1000);
      return (date);
   }

  /**
   * Returns the GregorianCalendar value stored in the object
   * data member. It converts the date from standard calendar
   * to Gregorian calendar.
   *
   * @return The GregorianCalendar date value.
   */
   public GregorianCalendar getGregorianCalendar ()
   {
      GregorianCalendar calendar;

      calendar = new GregorianCalendar (TimeZone.getTimeZone ("UTC"));
      calendar.setTime (getDate ());
      return (calendar);
   }

} /* NdsTimestamp */

