/*******************************************************************************
*                                                                              *
*  File:        ByteArray.java                          Revision:  1.0         *
*                                                                              *
*  Purpose:     implements an array of bytes under "Rhino"                     *
*                                                                              *
*  Creation:    08.01.2002                     Last Modification:  08.01.2002  *
*                                                                              *
*  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:    (none)                                                         *
*                                                                              *
*******************************************************************************/

package sunda.rhino.data;

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

public class ByteArray 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[] {
    "append", "clear", "equals", "fill", "isEmpty", "insert", "overwrite", "remove",
    "replace", "Size", "slice", "toString"
  };

/**** dec-to-hex conversion table ****/

  final static char HexTable[] = {
    '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
  };

/*******************************************************************************
*                                                                              *
*                          Non-Public Class Variables                          *
*                                                                              *
*******************************************************************************/

  static Scriptable Scope;             // required for "ByteArray" instantiation

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

  byte Array[];                                         // the actual byte array

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

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

  /**** instantiate an empty byte array ****/

    Array = new byte[0];                              // just for safety reasons
  };

/*******************************************************************************
*                                                                              *
*                             Public Class Methods                             *
*                                                                              *
*******************************************************************************/

/*******************************************************************************
*                                                                              *
* finishInit                            terminates script class initialization *
*                                                                              *
*******************************************************************************/

  public static void finishInit (Scriptable ObjectScope, FunctionObject ObjectConstructor, Scriptable ObjectPrototype) {
    Scope = ObjectScope;       // will be required for "ByteArray" instantiation

  /**** the following initialization is not really necessary ****/

    try {
      defineClass(Scope,ByteArray.class);   // guarantees "ByteArray" definition
    } catch (Exception Signal) {
      /* nop */                // it's difficult to handle problems at this time
    };
  };

/*******************************************************************************
*                                                                              *
*                         Non-Public Instance Methods                          *
*                                                                              *
*******************************************************************************/

/*******************************************************************************
*                                                                              *
* append                             appends the contents of a given ByteArray *
*                                                                              *
*******************************************************************************/

  ByteArray append (ByteArray Source, int SourceOffset, int SourceCount) {
    return replace(Array.length, 0, Source, SourceOffset, SourceCount);
  };

/*******************************************************************************
*                                                                              *
* ByteArrayArgument                   checks and yields a "ByteArray" argument *
*                                                                              *
*******************************************************************************/

  ByteArray ByteArrayArgument (Object Argument, String Name) throws RhinoException {
    if ((Argument == undefined) || (Argument == null))
    throw new sunda.rhino.IllegalArgumentException("no \"" + Name + "\" array given");

    if (!(Argument instanceof ByteArray)) throw new sunda.rhino.IllegalArgumentException(
      "given \"" + Name + "\" must be of type \"ByteArray\""
    );

    return (ByteArray) Argument;
  };

/*******************************************************************************
*                                                                              *
* fill                      fills all or part of this array with a given value *
*                                                                              *
*******************************************************************************/

  ByteArray fill (int Offset, int Count, int Value) {
    byte ByteValue = (byte) (Value & 0xFF);

    if (Count == 0) return this;                         // nothing to be filled
      for (int i = 0; i < Count; i++) {
        Array[Offset] = ByteValue; Offset++;
      };
    return this;                           // allows for cascading of operations
  };

/*******************************************************************************
*                                                                              *
* insert                    inserts all or part of another array into this one *
*                                                                              *
*******************************************************************************/

  ByteArray insert (int Offset, ByteArray Source, int SourceOffset, int SourceCount) {
    return replace(Offset, 0, Source, SourceOffset, SourceCount);
  };

/*******************************************************************************
*                                                                              *
* intArgument                            checks and yields an integer argument *
*                                                                              *
*******************************************************************************/

  int intArgument (Object Argument, String Name) throws RhinoException {
    if (Argument == undefined) {
      throw new sunda.rhino.IllegalArgumentException("no \"" + Name + "\" given");
    } else {
      return intArgument(Argument, Name, 0);
    }
  };


  int intArgument (Object Argument, String Name, int Default) throws RhinoException {
    if (Argument == undefined) return Default;

    if (!(Argument instanceof Number)) throw new sunda.rhino.IllegalArgumentException(
      "non-numeric \"" + Name + "\" given (" + Argument.getClass().getName() + ")"
    );

    double Value = ((Number) Argument).doubleValue();
    if ((Value < Integer.MIN_VALUE) || (Value > Integer.MAX_VALUE))
    throw new sunda.rhino.IllegalArgumentException(
      "invalid \"" + Name + "\" given (" + Value + ")"
    );

    return (int) Value;
  };

/*******************************************************************************
*                                                                              *
* overwrite              overwrites all or part of this array with another one *
*                                                                              *
*******************************************************************************/

  ByteArray overwrite (int Offset, ByteArray Source, int SourceOffset, int SourceCount) {
    return replace(Offset, SourceCount, Source, SourceOffset, SourceCount);
  };

/*******************************************************************************
*                                                                              *
* remove                                          removes a part of this array *
*                                                                              *
*******************************************************************************/

  ByteArray remove (int Offset, int Count) {
    if (Count == 0) return this;                            // nothing to remove

  /**** construct a new temporary array and fill it as requested (consumes extra memory!) ****/

    byte[] newArray  = new byte[Array.length-Count];

    if (Offset > 0) {
      java.lang.System.arraycopy(Array,0, newArray,0, Offset);
    };

    int lastCount = Array.length-(Offset+Count);
    if (lastCount > 0) {
      java.lang.System.arraycopy(Array,Offset+Count, newArray,Offset, lastCount);
    };

  /**** then replace the old array with the new one ****/

    Array = newArray;       // the next GC will free any memory used for "Array"

  /**** that's it! ****/

    return this;                           // allows for cascading of operations
  };

/*******************************************************************************
*                                                                              *
* replace                  replaces all or part of this array with another one *
*                                                                              *
*******************************************************************************/

  ByteArray replace (int Offset, int Count, ByteArray Source, int SourceOffset, int SourceCount) {
    if (Count == SourceCount) {
      java.lang.System.arraycopy(Source.Array,SourceOffset, this.Array,Offset, Count);
      return this;                         // allows for cascading of operations
    };

  /**** construct a new temporary array and fill it as requested (consumes extra memory!) ****/

    byte[] newArray  = new byte[Array.length-Count+SourceCount];
    int    newOffset = 0;

    if (Offset > 0) {
      java.lang.System.arraycopy(Array,0, newArray,newOffset, Offset);
      newOffset += Offset;
    };

    if (SourceCount > 0) {
      java.lang.System.arraycopy(Source.Array,SourceOffset, newArray,newOffset, SourceCount);
      newOffset += SourceCount;
    };

    int lastCount = Array.length-(Offset+Count);
    if (lastCount > 0) {
      java.lang.System.arraycopy(Array,Offset+Count, newArray,newOffset, lastCount);
//    newOffset += lastCount;
    };

  /**** then replace the old array with the new one ****/

    Array = newArray;       // the next GC will free any memory used for "Array"

  /**** that's it! ****/

    return this;                           // allows for cascading of operations
  };

/*******************************************************************************
*                                                                              *
* Slice                                  yields a copy of a part of this array *
*                                                                              *
*******************************************************************************/

  ByteArray Slice (int Offset, int Count) throws RhinoException {

  /**** construct a Rhino ByteArray (not just an instance of the underlying Java class!) ****/

    ByteArray Result = null;
    try {
      Context currentContext = Context.getCurrentContext();

      Result = (ByteArray) currentContext.evaluateString(
        Scope, "new ByteArray(" + Count + ")", "ByteArray.slice", 1, null
      );
    } catch (JavaScriptException Signal) {
      throw new RhinoException(Signal.getClass().getName() + ": " + Signal.getMessage());
    };

  /**** then fill that array with the requested slice ****/

    java.lang.System.arraycopy(this.Array,Offset, Result.Array,0, Count);

  /**** that's it! ****/

    return Result;
  };

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

/*******************************************************************************
*                                                                              *
* get                                yields the value of a given array element *
*                                                                              *
*******************************************************************************/

  public Object get (int Index, Scriptable Scope) {
    if ((Index < 0) || (Index >= Array.length)) {
      return Scriptable.NOT_FOUND;
    } else {
      return Context.toObject(new Byte(Array[Index]), Scope);
    }
  };

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

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

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

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

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

  public void jsConstructor (Object Argument) throws RhinoException {
    if (Argument == null) return;                   // no further initialization

  /**** a numeric "Argument" will define the initial size of this array ****/

    if (Argument instanceof java.lang.Number) {
      double initialSize = ((java.lang.Number) Argument).doubleValue();

      if ((initialSize < 0.0) || (initialSize > (double) Integer.MAX_VALUE))
      throw new sunda.rhino.IllegalArgumentException(
        "invalid array size argument (" + initialSize + ")"
      );

      int ArraySize = (int) initialSize;

      Array = new byte[ArraySize];
      for (int i = 0; i < ArraySize; i++) {  // not really efficient, but simple
        Array[i] = 0;
      };

      return;
    };

  /**** an "Argument" of type "ByteArray" will be used to initialize this array ****/

    if (Argument instanceof ByteArray) {
      ByteArray initial = (ByteArray) Argument;          // just an abbreviation

      this.Array = new byte[initial.Array.length];
      java.lang.System.arraycopy(initial.Array,0, this.Array,0, this.Array.length);

      return;
    };

  /**** any other kind of "Argument" will throw an exception ****/

    throw new sunda.rhino.IllegalArgumentException(
      "invalid kind of \"Argument\" (" + Argument.getClass().getName() + ")"
    );
  };

/*******************************************************************************
*                                                                              *
* jsFunction_append                  appends the contents of a given ByteArray *
*                                                                              *
*******************************************************************************/

  public ByteArray jsFunction_append (Object aSource, Object aSourceOffset, Object aSourceCount) throws RhinoException {
    ByteArray Source = ByteArrayArgument(aSource, "Source");

    int SourceOffset = intArgument(aSourceOffset, "SourceOffset", 0);
    if (SourceOffset < 0) {
      if (SourceOffset < -Source.Array.length) {
        throw new sunda.rhino.IllegalArgumentException(
          "given \"SourceOffset\" (" + SourceOffset + ") exceeds source array boundaries"
        );
      } else {
        SourceOffset = Source.Array.length+SourceOffset;
      };
    } else {
      if (SourceOffset >= Source.Array.length) throw new sunda.rhino.IllegalArgumentException(
        "given \"SourceOffset\" (" + SourceOffset + ") exceeds source array boundaries"
      );
    };

    int SourceCount = intArgument(aSourceCount, "SourceCount", Source.Array.length-SourceOffset);
    if (SourceCount < 0) throw new sunda.rhino.IllegalArgumentException(
      "invalid \"SourceCount\" given (" + SourceCount + ")"
    );

    if (SourceOffset+SourceCount > Source.Array.length) throw new sunda.rhino.IllegalArgumentException(
      "\"SourceOffset\" and \"SourceCount\" exceed source array boundaries (" +
      SourceOffset + " + " + SourceCount + ")"
    );

  /**** and now perform the requested operation ****/

    return append(Source, SourceOffset, SourceCount);
  };

/*******************************************************************************
*                                                                              *
* jsFunction_clear                                      clears the whole array *
*                                                                              *
*******************************************************************************/

  public ByteArray jsFunction_clear () {
    Array = new byte[0];
    return this;                           // allows for cascading of operations
  };

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

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

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

    ByteArray Candidate = (ByteArray) CandidateObject;   // just an abbreviation

    if (this.Array.length != Candidate.Array.length) return false;
      for (int i = 0; i < this.Array.length; i++) {      // not really efficient
        if (this.Array[i] != Candidate.Array[i]) return false;
      };
    return true;                           // well, both arrays seem to be equal
  };

/*******************************************************************************
*                                                                              *
* jsFunction_fill           fills all or part of this array with a given value *
*                                                                              *
*******************************************************************************/

  public ByteArray jsFunction_fill (Object Arg1, Object Arg2, Object Arg3) throws RhinoException {
    int Offset = 0;
    int Count  = Array.length;
    int Value;

    boolean unknownCount = false;

    if (Arg3 == undefined) {
      if (Arg2 == undefined) {

      /**** arguments for fill(Value) ****/

        Value = intArgument(Arg1, "Value");
      } else {

      /**** arguments for fill(Offset, Value) ****/

        Offset = intArgument(Arg1, "Offset");
        Value  = intArgument(Arg2, "Value");

        unknownCount = true;
      };
    } else {

    /**** arguments for fill(Offset, Count, Value) ****/

      Offset = intArgument(Arg1, "Offset");
      Count  = intArgument(Arg2, "Count");
      Value  = intArgument(Arg3, "Value");
    };

  /**** check the given arguments ****/

    if (Offset < 0) {
      if (Offset < -Array.length) {
        throw new sunda.rhino.IllegalArgumentException(
          "given \"Offset\" exceeds array boundaries (" + Offset + ")"
        );
      } else {
        Offset = Array.length+Offset;
      };
    } else {
      if (Offset >= Array.length) throw new sunda.rhino.IllegalArgumentException(
        "given \"Offset\" exceeds array boundaries (" + Offset + ")"
      );
    };

    if (unknownCount) {
      Count = Array.length-Offset;
    } else {
      if (Count < 0) throw new sunda.rhino.IllegalArgumentException(
        "invalid \"Count\" given (" + Count + ")"
      );

      if (Offset+Count > Array.length) throw new sunda.rhino.IllegalArgumentException(
        "\"Offset\" and \"Count\" exceed array boundaries (" + Offset + " + " + Count + ")"
      );
    };

    if ((Value < -127) || (Value > +255)) throw new sunda.rhino.IllegalArgumentException(
      "invalid byte \"Value\" given (" + Value + ")"
    );

  /**** now perform the desired operation ****/

    return fill(Offset, Count, Value);
  };

/*******************************************************************************
*                                                                              *
* jsFunction_isEmpty                checks if this array contains any elements *
*                                                                              *
*******************************************************************************/

  public boolean jsFunction_isEmpty () {
    return (Array.length == 0);
  };

/*******************************************************************************
*                                                                              *
* jsFunction_insert         inserts all or part of another array into this one *
*                                                                              *
*******************************************************************************/

  public ByteArray jsFunction_insert (Object anOffset, Object aSource, Object aSourceOffset, Object aSourceCount) throws RhinoException {
    int Offset = intArgument(anOffset, "Offset");
    if (Offset < 0) {
      if (Offset < -Array.length) {
        throw new sunda.rhino.IllegalArgumentException(
          "given \"Offset\" exceeds array boundaries (" + Offset + ")"
        );
      } else {
        Offset = Array.length+Offset;
      };
    } else {
      if (Offset >= Array.length) throw new sunda.rhino.IllegalArgumentException(
        "given \"Offset\" exceeds array boundaries (" + Offset + ")"
      );
    };

    ByteArray Source = ByteArrayArgument(aSource, "Source");

    int SourceOffset = intArgument(aSourceOffset, "SourceOffset", 0);
    if (SourceOffset < 0) {
      if (SourceOffset < -Source.Array.length) {
        throw new sunda.rhino.IllegalArgumentException(
          "given \"SourceOffset\" exceeds source array boundaries (" + SourceOffset + ")"
        );
      } else {
        SourceOffset = Source.Array.length+SourceOffset;
      };
    } else {
      if (SourceOffset >= Source.Array.length) throw new sunda.rhino.IllegalArgumentException(
        "given \"SourceOffset\" exceeds source array boundaries (" + SourceOffset + ")"
      );
    };

    int SourceCount = intArgument(aSourceCount, "SourceCount", Source.Array.length-SourceOffset);
    if (SourceCount < 0) throw new sunda.rhino.IllegalArgumentException(
      "invalid \"SourceCount\" given (" + SourceCount + ")"
    );

    if (SourceOffset+SourceCount > Source.Array.length) throw new sunda.rhino.IllegalArgumentException(
      "\"SourceOffset\" and \"SourceCount\" exceed source array boundaries (" +
      SourceOffset + " + " + SourceCount + ")"
    );

    return insert(Offset, Source, SourceOffset, SourceCount);
  };

/*******************************************************************************
*                                                                              *
* jsFunction_overwrite   overwrites all or part of this array with another one *
*                                                                              *
*******************************************************************************/

  public ByteArray jsFunction_overwrite (Object anOffset, Object aSource, Object aSourceOffset, Object aSourceCount) throws RhinoException {
    int Offset = intArgument(anOffset, "Offset");
    if (Offset < 0) {
      if (Offset < -Array.length) {
        throw new sunda.rhino.IllegalArgumentException(
          "given \"Offset\" exceeds array boundaries (" + Offset + ")"
        );
      } else {
        Offset = Array.length+Offset;
      };
    } else {
      if (Offset >= Array.length) throw new sunda.rhino.IllegalArgumentException(
        "given \"Offset\" exceeds array boundaries (" + Offset + ")"
      );
    };

    ByteArray Source = ByteArrayArgument(aSource, "Source");

    int SourceOffset = intArgument(aSourceOffset, "SourceOffset", 0);
    if (SourceOffset < 0) {
      if (SourceOffset < -Source.Array.length) {
        throw new sunda.rhino.IllegalArgumentException(
          "given \"SourceOffset\" exceeds source array boundaries (" + SourceOffset + ")"
        );
      } else {
        SourceOffset = Source.Array.length+SourceOffset;
      };
    } else {
      if (SourceOffset >= Source.Array.length) throw new sunda.rhino.IllegalArgumentException(
        "given \"SourceOffset\" exceeds source array boundaries (" + SourceOffset + ")"
      );
    };

    int SourceCount = intArgument(aSourceCount, "SourceCount", Source.Array.length-SourceOffset);
    if (SourceCount < 0) throw new sunda.rhino.IllegalArgumentException(
      "invalid \"SourceCount\" given (" + SourceCount + ")"
    );

    if (SourceOffset+SourceCount > Source.Array.length) throw new sunda.rhino.IllegalArgumentException(
      "\"SourceOffset\" and \"SourceCount\" exceed source array boundaries (" +
      SourceOffset + " + " + SourceCount + ")"
    );

    return overwrite(Offset, Source, SourceOffset, SourceCount);
  };

/*******************************************************************************
*                                                                              *
* jsFunction_remove                               removes a part of this array *
*                                                                              *
*******************************************************************************/

  public ByteArray jsFunction_remove (Object anOffset, Object aCount) throws RhinoException {
    int Offset = intArgument(anOffset, "Offset");
    if (Offset < 0) {
      if (Offset < -Array.length) {
        throw new sunda.rhino.IllegalArgumentException(
          "given \"Offset\" exceeds array boundaries (" + Offset + ")"
        );
      } else {
        Offset = Array.length+Offset;
      };
    } else {
      if (Offset >= Array.length) throw new sunda.rhino.IllegalArgumentException(
        "given \"Offset\" exceeds array boundaries (" + Offset + ")"
      );
    };

    int Count = intArgument(aCount, "Count", Array.length-Offset);
    if (Count < 0) throw new sunda.rhino.IllegalArgumentException(
      "invalid \"Count\" given (" + Count + ")"
    );

    if (Offset+Count > Array.length) throw new sunda.rhino.IllegalArgumentException(
      "\"Offset\" and \"Count\" exceed array boundaries (" + Offset + " + " + Count + ")"
    );

    return remove (Offset, Count);
  };

/*******************************************************************************
*                                                                              *
* jsFunction_replace       replaces all or part of this array with another one *
*                                                                              *
*******************************************************************************/

  public ByteArray jsFunction_replace (Object Arg1, Object Arg2, Object Arg3, Object Arg4, Object Arg5) throws RhinoException {
    int       Offset       = 0;
    int       Count        = Array.length;
    int       SourceOffset = 0;
    int       SourceCount;
    ByteArray Source;

    boolean unknownSourceCount = true;

    if (Arg1 instanceof ByteArray) {

    /**** arguments for replace(Source[, SourceOffset[, SourceCount]]) ****/

      Source       = ByteArrayArgument(Arg1, "Source");
      SourceOffset = intArgument(Arg2, "SourceOffset", 0);
      SourceCount  = intArgument(Arg3, "SourceCount",  0);

      unknownSourceCount = (Arg3 == undefined);
    } else if (Arg2 instanceof ByteArray) {

    /**** arguments for replace(Offset, Source[, SourceOffset[, SourceCount]]) ****/

      Offset       = intArgument(Arg1, "Offset");
      Source       = ByteArrayArgument(Arg2, "Source");
      SourceOffset = intArgument(Arg3, "SourceOffset", 0);
      SourceCount  = intArgument(Arg4, "SourceCount",  0);

      unknownSourceCount = (Arg4 == undefined);
    } else {

    /**** arguments for replace(Offset, Count, Source[, SourceOffset[, SourceCount]]) ****/

      Offset       = intArgument(Arg1, "Offset");
      Count        = intArgument(Arg2, "Count");
      Source       = ByteArrayArgument(Arg3, "Source");
      SourceOffset = intArgument(Arg4, "SourceOffset", 0);
      SourceCount  = intArgument(Arg5, "SourceCount",  0);

      unknownSourceCount = (Arg5 == undefined);
    };

  /**** check the given arguments ****/

    if (Offset < 0) {
      if (Offset < -Array.length) {
        throw new sunda.rhino.IllegalArgumentException(
          "given \"Offset\" exceeds array boundaries (" + Offset + ")"
        );
      } else {
        Offset = Array.length+Offset;
      };
    } else {
      if (Offset >= Array.length) throw new sunda.rhino.IllegalArgumentException(
        "given \"Offset\" exceeds array boundaries (" + Offset + ")"
      );
    };

    if (Count < 0)
    throw new sunda.rhino.IllegalArgumentException("invalid \"Count\" given (" + Count + ")");

    if (Offset+Count > Array.length) throw new sunda.rhino.IllegalArgumentException(
      "\"Offset\" and \"Count\" exceed array boundaries (" + Offset + " + " + Count + ")"
    );

    if (Source == null)
    throw new sunda.rhino.IllegalArgumentException("no \"Source\" array given");

    if (SourceOffset < 0) {
      if (SourceOffset < -Source.Array.length) {
        throw new sunda.rhino.IllegalArgumentException(
          "given \"SourceOffset\" exceeds source array boundaries (" + SourceOffset + ")"
        );
      } else {
        SourceOffset = Source.Array.length+SourceOffset;
      };
    } else {
      if (SourceOffset >= Source.Array.length) throw new sunda.rhino.IllegalArgumentException(
        "given \"SourceOffset\" exceeds source array boundaries (" + SourceOffset + ")"
      );
    };

    if (unknownSourceCount) {
      SourceCount = Source.Array.length-SourceOffset;
    } else {
      if (SourceCount < 0) throw new sunda.rhino.IllegalArgumentException(
        "invalid \"SourceCount\" given (" + SourceCount + ")"
      );

      if (SourceOffset+SourceCount > Source.Array.length) throw new sunda.rhino.IllegalArgumentException(
        "\"SourceOffset\" and \"SourceCount\" exceed source array boundaries (" +
        SourceOffset + " + " + SourceCount + ")"
      );
    };

  /**** and now perform the requested operation ****/

    return replace(Offset, Count, Source, SourceOffset, SourceCount);
  };

/*******************************************************************************
*                                                                              *
* jsFunction_slice                       yields a copy of a part of this array *
*                                                                              *
*******************************************************************************/

  public ByteArray jsFunction_slice (Object anOffset, Object aCount) throws RhinoException {
    int Offset = intArgument(anOffset, "Offset");
    if (Offset < 0) {
      if (Offset < -Array.length) {
        throw new sunda.rhino.IllegalArgumentException(
          "given \"Offset\" exceeds array boundaries (" + Offset + ")"
        );
      } else {
        Offset = Array.length+Offset;
      };
    } else {
      if (Offset >= Array.length) throw new sunda.rhino.IllegalArgumentException(
        "given \"Offset\" exceeds array boundaries (" + Offset + ")"
      );
    };

    int Count = intArgument(aCount, "Count", Array.length-Offset);
    if (Count < 0)
    throw new sunda.rhino.IllegalArgumentException("invalid \"Count\" given (" + Count + ")");

    if (Offset+Count > Array.length) throw new sunda.rhino.IllegalArgumentException(
      "\"Offset\" and \"Count\" exceed array boundaries (" + Offset + " + " + Count + ")"
    );

    return Slice(Offset, Count);
  };

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

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

/*******************************************************************************
*                                                                              *
* jsGet_Size                yields the actual number of elements in this array *
*                                                                              *
*******************************************************************************/

  public int jsGet_Size () {
    return Array.length;
  };

/*******************************************************************************
*                                                                              *
* put                               updates the value of a given array element *
*                                                                              *
*******************************************************************************/

  public void put (int Index, Scriptable Scope, Object Value) /* throws RhinoException */ {
    if ((Index < 0) || (Index >= Array.length))
//  throw new sunda.rhino.IllegalArgumentException("invalid array \"Index\" given (" + Index + ")");
    return;

    if (!(Value instanceof java.lang.Number))
//  throw new sunda.rhino.IllegalArgumentException("non-numeric byte \"Value\" given (" + Value + ")");
    return;

    double newValue = ((java.lang.Number) Value).doubleValue();

    if ((newValue < -127.0) || (newValue > +255.0))
//  throw new sunda.rhino.IllegalArgumentException("invalid byte \"Value\" given (" + newValue + ")");
    return;

    Array[Index] = (byte) (((int) newValue) & 0xFF);
  };

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

  public String toString () {
    int ArrayLength = Array.length;
    if (ArrayLength == 0) return "(empty)";

    StringBuffer Result = new StringBuffer(2+2*ArrayLength);
      Result.append("0x");
      for (int i = 0; i < ArrayLength; i++) {
        Result.append(HexTable[(Array[i] >> 4) & 0x0F]);
        Result.append(HexTable[ Array[i]       & 0x0F]);
      };
    return Result.toString();
  };
};

