gt:         if (cs != null) {
context: package org.ringojs.wrappers;
  
  import java.io.UnsupportedEncodingException;
  import java.lang.reflect.Method;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.List;
  import java.util.Map;
  
  import org.mozilla.javascript.BaseFunction;
  import org.mozilla.javascript.Context;
  import org.mozilla.javascript.Function;
  import org.mozilla.javascript.FunctionObject;
  import org.mozilla.javascript.NativeArray;
  import org.mozilla.javascript.NativeJavaArray;
  import org.mozilla.javascript.ScriptRuntime;
  import org.mozilla.javascript.Scriptable;
  import org.mozilla.javascript.ScriptableObject;
  import org.mozilla.javascript.Undefined;
  import org.mozilla.javascript.Wrapper;
  import org.mozilla.javascript.annotations.JSConstructor;
  import org.mozilla.javascript.annotations.JSFunction;
  import org.mozilla.javascript.annotations.JSGetter;
  import org.mozilla.javascript.annotations.JSSetter;
  import org.ringojs.util.ScriptUtils;
  
  /**
   * <p>A wrapper around a Java byte array compliant to the Binary/ByteArray/ByteString
   * classes defined in the <a href="http://wiki.commonjs.org/wiki/Binary/B">Binary/B proposal</a>.
   * To register Binary, ByteArray and ByteString as a host objects in Rhino call the
   * <code>defineClass()</code> function with this class as argument.</p>
   *
   * <pre><code>defineClass(org.ringojs.wrappers.Binary);</code></pre>
   *
   * <p>The JavaScript Binary class serves as common base class for ByteArray and ByteString
   * and can't be instantiated. ByteArray implements a modifiable and resizable byte buffer,
   * while ByteString implements an immutable byte sequence. The ByteArray and ByteString
   * constructors can take several arguments. Have a look at the proposal for details.</p>
   *
   * <p>When passed to a Java method that expects a byte array, instances of thes class
   * are automatically unwrapped. Use the {@link #unwrap()} method to explicitly get the
   * wrapped Java byte array.</p>
   */
  public class Binary extends ScriptableObject implements Wrapper {
  
      private byte[] bytes;
      private int length;
      private final Type type;
  
      enum Type {
          Binary, ByteArray, ByteString
      }
  
      public Binary() {
          type = Type.Binary;
      }
  
      public Binary(Type type) {
          this.type = type;
      }
  
      public Binary(Scriptable scope, Type type, int length) {
          super(scope, ScriptUtils.getClassOrObjectProto(scope, type.toString()));
          this.type = type;
          this.bytes = new byte[Math.max(length, 8)];
          this.length = length;
      }
  
      public Binary(Scriptable scope, Type type, byte[] bytes) {
          this(scope, type, bytes, 0, bytes.length);
      }
  
      public Binary(Scriptable scope, Type type, byte[] bytes, int offset, int length) {
          super(scope, ScriptUtils.getClassOrObjectProto(scope, type.toString()));
          this.bytes = new byte[length];
          this.length = length;
          this.type = type;
          System.arraycopy(bytes, offset, this.bytes, 0, length);
      }
  
      @JSConstructor
      public static Object construct(Context cx, Object[] args, Function ctorObj, boolean inNewExpr) {
          ScriptUtils.checkArguments(args, 0, 2);
          Scriptable scope = ctorObj.getParentScope();
          Type type = Type.valueOf((String) ctorObj.get("name", ctorObj));
          if (type == Type.Binary) {
              throw ScriptRuntime.constructError("Error", "cannot instantiate Binary base class");
          }
          if (args.length == 0) {
              return new Binary(scope, type, 0);
          }
          Object arg = args[0];
          if (arg instanceof Wrapper) {
              arg = ((Wrapper) arg).unwrap();
          }
          if (args.length == 2) {
              if (!(arg instanceof String)) {
                  throw ScriptRuntime.constructError("Error", "Expected string as first argument");
              } else if (!(args[1] instanceof String)) {
                  throw ScriptRuntime.constructError("Error", "Expected string as second argument");
              }
              try {
                  return new Binary(scope, type, ((String) arg).getBytes((String) args[1]));
              } catch (UnsupportedEncodingException uee) {
                  throw ScriptRuntime.constructError("Error", "Unsupported encoding: " + args[1]);
              }
          } else if (arg instanceof Number && type == Type.ByteArray) {
              return new Binary(scope, type, ((Number) arg).intValue());
          } else if (ScriptRuntime.isArrayObject(arg)) {
              Scriptable object = (Scriptable) arg;
              long longLength =  ScriptRuntime.toUint32(
                      ScriptRuntime.getObjectProp(object, "length", cx));
              if (longLength > Integer.MAX_VALUE) {
                  throw new IllegalArgumentException();
              }
              int length = (int) longLength;
              Binary bytes = new Binary(scope, type, length);
              for (int i = 0; i < length; i++) {
                  Object value = ScriptableObject.getProperty(object, i);
                  bytes.putInternal(i, value);
              }
              return bytes;
          } else if (arg instanceof byte[]) {
              return new Binary(scope, type, (byte[]) arg);
          } else if (arg instanceof Binary) {
              return new Binary(scope, type, ((Binary) arg).getBytes());
          } else if (arg == Undefined.instance) {
              return new Binary(scope, type, 0);
          } else {
              throw ScriptRuntime.constructError("Error", "Unsupported argument: " + arg);
          }
      }
  
      // Called after the host class has been defined.
      public static void finishInit(Scriptable scope, FunctionObject ctor, Scriptable prototype)
              throws NoSuchMethodException{
          initClass(scope, prototype, Type.ByteArray);
          initClass(scope, prototype, Type.ByteString);
      }
  
      private static void initClass(Scriptable scope, Scriptable parentProto, Type type)
              throws NoSuchMethodException {
          Binary prototype = new Binary(type);
          prototype.setPrototype(parentProto);
          Method ctorMember = Binary.class.getMethod("construct", new Class[] {
                  Context.class, Object[].class, Function.class, Boolean.TYPE
          });
          FunctionObject constructor = new FunctionObject(type.toString(), ctorMember, scope);
          constructor.addAsConstructor(scope, prototype);
          constructor.defineProperty("wrap", new Wrap(scope, prototype, type),
                  DONTENUM | READONLY | PERMANENT);
      }
  
      public Type getType() {
          return type;
      }
  
      @Override
      public Object get(int index, Scriptable start) {
          if (index < 0 || index >= length) {
              return Undefined.instance;
          }
          return Integer.valueOf(0xff & bytes[index]);
      }
  
      @Override
      public boolean has(int index, Scriptable start) {
          return index >= 0 && index < length;
      }
  
      @Override
      public void put(int index, Scriptable start, Object value) {
          if (type != Type.ByteArray) {
              return;
          }
          putInternal(index, value);
      }
  
      private void putInternal(int index, Object value) {
          if (index < 0) {
              throw ScriptRuntime.constructError("Error", "Negative ByteArray index");
          }
          if (!(value instanceof Number)) {
              throw ScriptRuntime.constructError("Error", "Non-numeric ByteArray member: " + value);
          }
          if (index >= length) {
              setLength(index + 1);
          }
          int n = ((Number) value).intValue();
          bytes[index] = (byte) (0xff & n);
      }
  
      @JSGetter
      public int getLength() {
          return length;
      }
  
      @JSSetter
      public synchronized void setLength(Object length) {
          int l = ScriptUtils.toInt(length, -1);
          if (l < 0) {
              throw ScriptRuntime.constructError("Error", "Inappropriate ByteArray length");
          }
          setLength(l);
      }
  
      protected synchronized void setLength(int newLength) {
          if (type != Type.ByteArray) {
              return;
          }
          if (newLength < length) {
              // if shrinking clear the old buffer
              Arrays.fill(bytes, newLength, length, (byte) 0);
          } else if (newLength > bytes.length) {
              // if growing make sure the buffer is large enough
              int newSize = Math.max(newLength, bytes.length * 2);
              byte[] b = new byte[newSize];
              System.arraycopy(bytes, 0, b, 0, length);
              bytes = b;
          }
          length = newLength;
      }
  
      @JSFunction
      public Object get(Object index) {
          int i = ScriptUtils.toInt(index, -1);
          if (i < 0 || i >= length) {
              return Undefined.instance;
          }
          return Integer.valueOf(0xff & bytes[i]);
      }
  
      @JSFunction
      public Object charCodeAt(Object index) {
          return get(index);
      }
  
      @JSFunction
      public Object byteAt(Object index) {
          int i = ScriptUtils.toInt(index, -1);
          if (i < 0 || i >= length) {
              return new Binary(getParentScope(), type, 0);
          }
          return new Binary(getParentScope(), type, new byte[] {bytes[i]});
      }
  
      @JSFunction
      public Object charAt(Object index) {
          return byteAt(index);
      }
  
      @JSFunction
      public void set(Object index, int value) {
          if (type != Type.ByteArray) {
              return;
          }
          int i = ScriptUtils.toInt(index, -1);
          if (i > -1) {
              if (i >= length) {
                  setLength(i + 1);
              }
              bytes[i] = (byte) (0xff & value);
          }
      }
  
      @JSFunction
      public synchronized Object toByteArray(Object sourceCharset, Object targetCharset)
              throws UnsupportedEncodingException {
          return makeCopy(Type.ByteArray, sourceCharset, targetCharset);
      }
      @JSFunction
      public synchronized Object toByteString(Object sourceCharset, Object targetCharset)
              throws UnsupportedEncodingException {
          return makeCopy(Type.ByteString, sourceCharset, targetCharset);
      }
  
      private Binary makeCopy(Type targetType, Object sourceCharset, Object targetCharset)
              throws UnsupportedEncodingException {
          String source = toCharset(sourceCharset);
          String target = toCharset(targetCharset);
          if (source != null && target != null) {
              String str = new String(bytes, 0, length, source);
              return new Binary(getParentScope(), targetType, str.getBytes(target));
          }
          if (this.type == Type.ByteString && this.type == targetType) {
              return this;
          }
          return new Binary(getParentScope(), targetType, bytes, 0, length);
      }
  
      @JSFunction
      public void copy(int srcStartIndex, int srcEndIndex, Binary target, Object targetIndex) {
          if (target.type != Type.ByteArray) {
              throw ScriptRuntime.constructError("Error", "Target object is not writable");
          } else if (srcStartIndex < 0 || srcStartIndex >= length) {
              throw ScriptRuntime.constructError("Error", "Invalid start index: " + srcStartIndex);
          } else if (srcEndIndex < srcStartIndex || srcEndIndex > length) {
              throw ScriptRuntime.constructError("Error", "Invalid end index: " + srcEndIndex);
          }
          int targetIdx = ScriptUtils.toInt(targetIndex, 0);
          if (targetIdx < 0) {
              throw ScriptRuntime.constructError("Error", "Invalid target index: " + targetIndex);
          }
          int size = srcEndIndex - srcStartIndex;
          target.copyFrom(bytes, srcStartIndex, size, targetIdx);
  
      }
  
      private synchronized void copyFrom(byte[] source, int srcIndex, int length, int targetIndex) {
          ensureLength(targetIndex + length);
          System.arraycopy(source, srcIndex, bytes, targetIndex,  length);
      }
  
      @JSFunction
      public synchronized Object toArray(Object charset)
              throws UnsupportedEncodingException {
          Object[] elements;
          String cs = toCharset(charset);
  