/*******************************************************************************
*                                                                              *
*  File:        Enumerator.java                         Revision:  1.0         *
*                                                                              *
*  Purpose:     implements an enumeration data type for "Rhino"                *
*                                                                              *
*  Creation:    21.11.2001                     Last Modification:  21.11.2001  *
*                                                                              *
*  Platform:    IBM-compatible PC running Windows 98SE                         *
*                                                                              *
*  Environment: Java 1.3, Rhino 1.5                                            *
*                                                                              *
*  Author:      Andreas Rozek           Phone:  ++49 (711) 6770682             *
*               Kirschblütenweg 15      Fax:    -                              *
*             D-70569 Stuttgart         EMail:  Andreas.Rozek@T-Online.De      *
*               Germany                                                        *
*                                                                              *
*  URL:         http://www.Andreas-Rozek.de/                                   *
*                                                                              *
*  Copyright:   this software is published under the  "GNU Lesser General Pub- *
*               lic  License"  (see  "http://www.fsf.org/copyleft/lesser.html" *
*               for additional information)                                    *
*                                                                              *
*  Comments:    the specification of this data type combines the "JScript" da- *
*               ta type "Enumerator" and the "java.util.Enumeration" interface *
*                                                                              *
*               This version of "Enumerator" has been realized as a Rhino      *
*               "Host Object" with the additional implementation of methods    *
*               from the "Scriptable" interface.                               *
*                                                                              *
*******************************************************************************/

package sunda.rhino.data;

import org.mozilla.javascript.*;                   // Rhino distribution classes
import sunda.rhino.*;                           // basic Rhino extension classes

public class Enumerator extends ScriptableObject {

/*******************************************************************************
*                                                                              *
*                          Non-Public Class Constants                          *
*                                                                              *
*******************************************************************************/

/**** a shortcut for Rhino's "undefined" object ****/

  final static Object undefined = org.mozilla.javascript.Undefined.instance;

/**** list of all enumerable ids in this object ****/

  final static Object[] SlotNameList = new Object[] {
    "actualElement", "atEnd", "Count", "equals", "hasMoreElements", "moveFirst", "nextElement", "toString"
  };

/*******************************************************************************
*                                                                              *
*                         Non-Public Instance Variables                        *
*                                                                              *
*******************************************************************************/

  Object[] ItemList;          // the sequence of objects within this enumeration
  int      ItemCount;                 // actual number of elements in "ItemList"

  int      actualIndex;              // points to the "current" enumeration item

/*******************************************************************************
*                                                                              *
*                                 Constructor                                  *
*                                                                              *
*******************************************************************************/

  public Enumerator () {
    super();                                              // just to be complete

  /**** instantiate an empty enumeration ****/

    ItemList  = new Object[8];
    ItemCount = 0;

  /**** ...and position it at the beginning ****/

    actualIndex = 0;
  };


  public Enumerator (Object[] newItemArray) throws RhinoException {
    super();                                              // just to be complete

  /**** instantiate an enumeration with all elements from "newItemArray" ****/

    if ((newItemArray == null) || (newItemArray == undefined))
    throw new NullValueException("no \"newItemArray\" given");

    ItemCount = newItemArray.length;
    ItemList  = new Object[ItemCount];
      java.lang.System.arraycopy(newItemArray,0, ItemList,0, ItemCount);

  /**** ...and position it at the beginning ****/

    actualIndex = 0;
  };

/*******************************************************************************
*                                                                              *
*                           Public Instance Methods                            *
*                                                                              *
*******************************************************************************/

/*******************************************************************************
*                                                                              *
* add                                adds the given object to this enumeration *
*                                                                              *
*******************************************************************************/

  public void add (Object newItem) throws RhinoException {
    add(newItem,false);
  };


  public void add (Object newItem, boolean NullOrUndefinedAllowed) throws RhinoException {
    if (((newItem == null) || (newItem == undefined)) && !NullOrUndefinedAllowed)
    throw new NullValueException("no \"newItem\" given");

    if (actualIndex > 0)
    throw new sunda.rhino.IllegalStateException("Enumerator is already within a traversal");

    if (ItemCount == ItemList.length) {
      Object[] newItemList = new Object[ItemCount < 1024 ? ItemCount*2 : ItemCount+1024];
      java.lang.System.arraycopy(ItemList,0, newItemList,0, ItemCount);
      ItemList = newItemList;
    };

    ItemList[ItemCount] = newItem;
    ItemCount++;
  };

/*******************************************************************************
*                                                                              *
* addContents           adds the elements of a given array to this enumeration *
*                                                                              *
*******************************************************************************/

  public void addContents (Object[] newItemArray) throws RhinoException {
    addContents(newItemArray,false);
  };


  public void addContents (Object[] newItemArray, boolean NullOrUndefinedAllowed) throws RhinoException {
    if ((newItemArray == null) || (newItemArray == undefined))
    throw new NullValueException("no \"newItemArray\" given");

    if (actualIndex > 0)
    throw new sunda.rhino.IllegalStateException("Enumerator is already within a traversal");

    int newItemCount = newItemArray.length;
    if (ItemCount+newItemCount > ItemList.length) {
      Object[] newItemList = new Object[ItemCount+newItemCount];
      java.lang.System.arraycopy(ItemList,0, newItemList,0, ItemCount);
      ItemList = newItemList;
    };

    for (int i = 0; i < newItemCount; i++) {
      if (!NullOrUndefinedAllowed && ((newItemArray[i] == null) || (newItemArray[i] == undefined)))
      throw new NullValueException("missing element in \"newItemArray\"");
    };

    java.lang.System.arraycopy(newItemArray,0, ItemList,ItemCount, newItemCount);
    ItemCount+=newItemCount;
  };

/*******************************************************************************
*                                                                              *
* getClassName                  yields the name of this class under JavaScript *
*                                                                              *
*******************************************************************************/

  public String getClassName () {
    return "Enumerator";
  };

/*******************************************************************************
*                                                                              *
* getIds           yields a list of all enumerable slot indices in this object *
*                                                                              *
*******************************************************************************/

  public Object[] getIds () {
    return SlotNameList;
  };

/*******************************************************************************
*                                                                              *
* jsConstructor           initializes a recently created "Enumerator" instance *
*                                                                              *
*******************************************************************************/

  public void jsConstructor (Object RhinoArray) throws RhinoException {
    if ((RhinoArray == null) || (RhinoArray == undefined)) return;

  /**** check proper argument type ****/

    if (!(RhinoArray instanceof NativeArray))
    throw new sunda.rhino.IllegalArgumentException("an Enumerator can only be constructed from an array of elements");

  /**** copy any array items into the enumeration ****/

    ItemCount = (int) ((NativeArray) RhinoArray).jsGet_length(); // "long" would be better...
    ItemList  = new Object[ItemCount];
      for (int i = 0; i < ItemCount; i++) {
        ItemList[i] = ((NativeArray) RhinoArray).get(i,null);
      };

  /**** ...and position it at the beginning ****/

    actualIndex = 0;
  };

/*******************************************************************************
*                                                                              *
* jsFunction_atEnd      does the "item pointer" point beyond the last element? *
*                                                                              *
*******************************************************************************/

  public boolean jsFunction_atEnd () {
    return (actualIndex >= ItemCount);
  };

/*******************************************************************************
*                                                                              *
* jsFunction_equals              compares this enumeration with a given object *
*                                                                              *
*******************************************************************************/

  public boolean jsFunction_equals (Object Candidate) {
    if (this == Candidate) return true;                  // check identity first
    if (!(Candidate instanceof Enumerator)) return false;      // incorrect type

  /**** now compare the two enumeration's contents ****/

    if (this.ItemCount != ((Enumerator) Candidate).ItemCount) return false;
    for (int i = 0; i < ItemCount; i++) {
      if (!(this.ItemList[i].equals(((Enumerator) Candidate).ItemList[i]))) return false;
    };

  /**** ok - both enumerations seem to be equal ****/

    return true;
  };

/*******************************************************************************
*                                                                              *
* jsFunction_hasMoreElements   does this enumeration contain further elements? *
*                                                                              *
*******************************************************************************/

  public boolean jsFunction_hasMoreElements () {
    return (actualIndex < ItemCount-1);
  };

/*******************************************************************************
*                                                                              *
* jsFunction_moveFirst          resets the "item pointer" to the first element *
*                                                                              *
*******************************************************************************/

  public void jsFunction_moveFirst () {
    actualIndex = 0;
  };

/*******************************************************************************
*                                                                              *
* jsFunction_moveNext    moves "item pointer" to next item of this enumeration *
*                                                                              *
*******************************************************************************/

  public void jsFunction_moveNext () {
    if (actualIndex < ItemCount) actualIndex += 1;
  };

/*******************************************************************************
*                                                                              *
* jsFunction_nextElement  yields the next element & moves "item pointer" to it *
*                                                                              *
*******************************************************************************/

  public Object jsFunction_nextElement () {
    if (actualIndex < ItemCount) actualIndex += 1;
    return (actualIndex < ItemCount ? ItemList[actualIndex] : undefined);
  };

/*******************************************************************************
*                                                                              *
* jsFunction_toString      yields a literal representation of this enumeration *
*                                                                              *
*******************************************************************************/

  public String jsFunction_toString () {
    return this.toString();
  };

/*******************************************************************************
*                                                                              *
* jsGet_actualElement          yields the "actual" element of this enumeration *
*                                                                              *
*******************************************************************************/

  public Object jsGet_actualElement () {
    return (actualIndex < ItemCount ? ItemList[actualIndex] : undefined);
  };

/*******************************************************************************
*                                                                              *
* jsGet_Count         yields the actual number of elements in this enumeration *
*                                                                              *
*******************************************************************************/

  public int jsGet_Count () {
    return ItemCount;
  };

/*******************************************************************************
*                                                                              *
* toString                 yields a literal representation of this enumeration *
*                                                                              *
*******************************************************************************/

  public String toString () {
    switch (ItemCount) {
      case 0:  return "";
      case 1:  return (ItemList[0] == null ? "(null)" : ItemList[0].toString());
      default:
        StringBuffer Result = new StringBuffer("(");
          Result.append(ItemList[0] == null ? "null" : ItemList[0].toString());

          for (int i = 1; i < ItemCount; i++) {
            Result.append(',');
            Result.append(ItemList[i] == null ? "null" : ItemList[i].toString());
          };

          Result.append(')');
        return Result.toString();
    }
  };
};

