# BinaryData.java

    /*
    ** Module   : BinaryData.java
    ** Abstract : Progress 4GL compatible base class for raw and memptr types
    **
    ** Copyright (c) 2005-2025, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- --JPRM-- ----------------------------------Description-----------------------------------
    ** 001 GES 20050906   @22562 Created initial version with support for a subset of raw and memptr
    **                           processing.
    ** 002 ECF 20051004   @22968 Implement hashCode() method. Required to be consistent with
    **                           BaseDataType's implementation of equals() method.
    ** 003 GES 20060206   @24296 Made compareTo() safe to process any object type (avoids
    **                           ClassCastException).
    ** 004 GES 20060208   @25572 Added a default format string getter.
    ** 005 GES 20060418   @25573 Added getString() function support.
    ** 006 GES 20060422   @25651 Changed assignment style syntax backing methods to return "this"
    **                           such that the result can be assigned back in the case of a database
    **                           field.
    ** 007 GES 20060427   @25768 New signatures for setString().
    ** 008 GES 20060724   @28157 Added a forced assign() abstract method.
    ** 009 GES 20060807   @28470 Moved to externalizable interface to optimize network performance.
    ** 010 GES 20060905   @29223 Fixed getString() to treat an over-large length value as array
    **                           length minus position.
    ** 011 GES 20080117   @36833 Added NPE protection code.
    ** 012 ECF 20080312   @37440 Fixed findNextNull(). It was running off end in the event of a
    **                           missing end null byte.
    ** 013 ECF 20080318   @37569 Fixed findNextNull() (again). It was *still* running off end when
    **                           trailing null byte was missing. Thanks (and apologies) to John
    **                           Ballard.
    ** 014 CS  20130124          Added conversion support for memptr related functions task #1635.
    ** 015 GES 20130322          Modified the assign(BDT,boolean) to be the common location for _POLY
    **                           morphing. This is safe because all current usage would have
    **                           generated a ClassCastException if that method was ever used with an
    **                           invalid type. Now, instead of the CCE, the code will morph the value
    **                           as is possible. The morphing code still needs to be checked for
    **                           validity.
    ** 016 OM  20130406          Implemented integer datatype manipulation, switch to 64 where needed.
    ** 017 GES 20130520          Major rewrite to implement a nearly complete implementation. Many
    **                           deviations from the 4GL were found and fixed. The actual data access
    **                           and storage is now in sub-classes instead of here. The sub-class
    **                           must implement a set of abstract worker methods to properly service
    **                           the core logic that is centralized here.
    ** 018 GES 20131212          Made isUninitialized() public.
    ** 019 GES 20140131          Added getUnprocessedDouble().
    ** 020 MAG 20140602          Move the message for incompatible types conversion to super class.
    ** 021 CA  20140811          Fixed clearing of unknown flag in assignment/output param.
    ** 022 GES 20141003          Modified getString() to return character instances with embedded
    **                           null characters (if they exist in the returned bytes). This is the
    **                           only known way such a result can occur in the 4GL as far as we know!
    **                           Fixed the length processing for get-string() to account for some
    **                           undocumented features (all negative values not just -1 are the
    **                           unspecified length and 0 returns empty string).
    ** 023 ECF 20150715          Replace StringBuffer with StringBuilder.
    ** 024 GES 20160617          Added unknown literal processing for the polymorphic assign case.
    ** 025 OM  20160706          Added static check() method in RangeCheck. Differentiate internal
    **                           buffer size from the figure the 4GL programmer sees. Use int64 for
    **                           return of length related methods.
    ** 026 GES 20171206          Changes in ErrorManager make silent error mode support unnecessary.
    ** 028 CA  20180514          PUT-STRING allows longchar sources, too.
    ** 029 CA  20181106          Fixed PUT-STRING - the length is computed from the String's bytes,
    **                           and this must use a single-byte charset.
    ** 030 CA  20190423          Added SET-SIZE() = POLY (emitted as setLength(BDT)) version.
    ** 031 IAS 20190617          Changed the signature of the writeByteRange method and the
    **                           logic of the setString.
    ** 032 CA  20190927          Added support for direct Java access from 4GL code.
    ** 033 ME  20200810          Lookup the JVM default codepage instead of a hard coded reference to ISO-8859-1.
    **     OM  20210203          OE quirk: length() method (GET-SIZE OE function) allows multiple arguments
    **                           but actually ignores them.
    **     OM  20210328          Improved COPY-LOB engine and validations.
    **     CA  20210429          An assignment to the same reference is a no-op.
    **     CA  20210818          Fixed memptr parameter runtime - they share the 'pointer structure' with the
    **                           passed argument.
    **     AL2 20220319          Added value proxy check in assign.
    **     TJD 20220504          Java 11 compatibility minor changes
    **     CA  20220513          Added the POLY version of PUT-BYTES() builtin function.
    **     CA  20220513          Added the POLY version of PUT-BYTES() statement.
    **     CA  20220514          Added the POLY version of PUT-STRING() statement.
    ** 034 CA  20231017          PUT-STRING with a negative numbytes argument will behave the same as '-1' (in 4GL
    **                           anything less than -1 will abend).
    **                           A zero numbytes value will not raise an ERROR condition and will just be a no-op.
    ** 035 DMM 20250214          Fixed error messages for putDouble.
    ** 036 ICP 20250123          Used int64.of to leverage caches instances.
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.util;

    import java.io.*;
    import java.nio.charset.*;
    import java.util.*;

    /**
     * A class that implements common services for Progress 4GL compatible raw and memptr data types
     * whose data is mutable.  The implementation is complete at the 10.2B level with the exception
     * of the RAW language statement and the RAW() built-in function.  All other functionality is
     * implemented.
     * <p>
     * The core idea of the class is to maximize the common code for subclasses, while allowing them
     * to implement the actual storage allocation, deallocation, reading and writing using abstract
     * workers.  4GL language features that are unique to a particular subclass are implemented in
     * that subclass.
     * <p>
     * In addition to the implementation of many built-in functions and operators, this class hides
     * the representation and processing of <code>unknown</code>.
     * <p>
     * The 4GL does not allow code to pass index positions or lengths as <code>decimal</code>
     * literals or expressions. Any such code will fail to compile. This means that all of the get
     * and set methods (including set-size and set-pointer-value) cannot take <code>decimal</code>
     * expressions (with the exception of the assigned data for the float/double getters/setters).
     * For this reason, all index and length parameters are <code>long</code> so that there is no
     * loss of precision (using <code>double</code> might result in such a precision loss even though
     * it would not result in a loss of magnitude).
     * <p>
     * Format string related processing is handled in the subclasses.
     * <p>
     * The following is the mapping of Progress language features to the corresponding feature in
     * this class:
     * <p>
     * <pre>
     * Progress Function             Java Method
     * ---------------------------   -----------------------
     * get-byte                      {@link #getByte}
     * get-bytes                     {@link #getBytes}
     * get-double                    {@link #getDouble}
     * get-float                     {@link #getFloat}
     * get-int64                     {@link #getInt64}
     * get-long                      {@link #getLong}
     * get-short                     {@link #getShort}
     * get-string                    {@link #getString}
     * get-unsigned-long             {@link #getUnsignedLong}
     * get-unsigned-short            {@link #getUnsignedShort}
     * length                        {@link #length}
     *
     * Progress Language Statement   Java Method
     * ---------------------------   -----------------------
     * length                        {@link #setLength}
     * put-byte                      {@link #setByte}
     * put-bytes                     {@link #setBytes}
     * put-double                    {@link #setDouble}
     * put-float                     {@link #setFloat}
     * put-int64                     {@link #setInt64}
     * put-long                      {@link #setLong}
     * put-short                     {@link #setShort}
     * put-string                    {@link #setString}
     * put-unsigned-long             {@link #setUnsignedLong}
     * put-unsigned-short            {@link #setUnsignedShort}
     *
     * Progress Operator             Java Method
     * ---------------------------   -----------------------
     * = (assignment)                {@link #assign}
     * comparisons                   {@link #compareTo} used by {@link CompareOps}
     * </pre>
     * <p>
     * The following is the hierarchy for error/failure processing.  The 4GL tests each of these
     * in the listed order and the first one to fail will generate the described result, aborting
     * any further checks.  All reading and writing conforms to these rules.
     * <p>
     * Read checks:
     * <ol>
     *    <li> If the instance is <code>unknown</code>, then <code>unknown</code> will be
     *         returned.
     *    <li> If the instance is uninitialized (the memory is not yet allocated), then
     *         an <code>ErrorConditionException</code> will be thrown.
     *    <li> If the given 1-based index position is &lt; 1, then an
     *         <code>ErrorConditionException</code> will be thrown.
     *    <li> If the given 1-based index position is greater than the size of the data OR if the
     *         starting position is inside the data but the ending position is outside of the data
     *         (which can only happen with multi-byte reads) then the caller is trying to read
     *         past the end. In this case <code>unknown</code> will be returned.
     * </ol>
     * <p>
     * Write checks:
     * <ol>
     *    <li> If the instance is <code>unknown</code>, then the write immediately returns without
     *         changing anything.
     *    <li> If the instance is uninitialized (the memory is not yet allocated), then
     *         an <code>ErrorConditionException</code> will be thrown.
     *    <li> If the given 1-based index position is &lt; 1, then an
     *         <code>ErrorConditionException</code> will be thrown.
     *    <li> If the method being used is {@link #setLong} then the data being
     *         written will be checked to ensure it is greater than or equal to -2147483648 and less
     *         than or equal to 2147483647.
     *    <li> If the method being used is {@link #setUnsignedLong} then the data
     *         being written will be checked to ensure it is greater than or equal to 0 and less
     *         than or equal to 4294967295.
     *    <li> If the given 1-based index position is greater than the size of the data OR if the
     *         starting position is inside the data but the ending position is outside of the data
     *         (which can only happen with multi-byte reads) then the caller is trying to write
     *         past the end. The result will depend on the specific behavior of the subclass. Any
     *         subclass where {@link #isAutoExtend} returns <code>true</code>, will attempt to extend
     *         its size automatically, up to the limit specified by {@link #lengthLimit}. Subclasses
     *         that do not auto-expand will throw an <code>ErrorConditionException</code>.
     * </ol>
     */
    public abstract class BinaryData
    extends BaseDataType
    {
       /**
        * Stores the state of whether or not this instance represents the
        * <code>unknown</code>, independent of the state of this
        * instance's data.
        */
       protected boolean unknown = false;

       /**
        * Default constructor, creates an instance that is uninitialized.
        */
       public BinaryData()
       {
       }

       /**
        * Constructor which creates an instance which is initialized based on
        * the given binary data.  The passed reference becomes the backing
        * reference for this instance (the data is not copied, only the
        * reference to the data is copied).
        *
        * @param    value
        *           The binary data to be used as the contents or if
        *           <code>null</code> then the instance will represent the
        *           <code>unknown</code>.
        */
       public BinaryData(byte[] value)
       {
          if (value == null)
          {
             unknown = true;
          }

          // the subclasses must initialize their own data because of the order of evaluation
          // since the data lives inside the subclass, that data will be initialized to defaults
          // after this returns
       }

       /**
        * Reports if this instance represents <code>unknown</code>.
        *
        * @return   <code>true</code> if this instance is set to the
        *           <code>unknown</code>.
        */
       public boolean isUnknown()
       {
          return unknown;
       }

       /**
        * Sets the state of this instance's <code>unknown</code> flag to <code>true</code>.
        * <p>
        * <b>Warning: the data stored in this instance WILL BE deallocated after calling this
        * method.</b>
        */
       public void setUnknown()
       {
          unknown = true;
          deallocate();
       }

       /**
        * Return the default display format string for this type.
        *
        * @return   The empty string (instances of this class cannot be rendered with
        *           custom formatting).
        */
       public String defaultFormatString()
       {
          return "";
       }

       /**
        * Accesses the state of this instance's binary data.
        *
        * @return   A copy of the binary data or <code>null</code> if this instance is
        *           <code>unknown</code>.  If uninitialized, the returned array will have 0
        *           elements. If the instance is initialized but the runtime has no knowledge
        *           of the buffer's size, <code>null</code> will be returned.
        */
       public byte[] getByteArray()
       {
          byte[] value = asByteArray();

          return (value == null) ? null : Arrays.copyOf(value, value.length);
       }

       /**
        * Sets the state (data and unknown value) of this instance based on the state of the passed
        * instance.
        * <p>
        * If the value is of type <code>BinaryData</code>, the result will be the same as calling
        * {@link #assign(BaseDataType)}.  If the type is the unknown literal the instance will be
        * set to unknown value.
        * <p>
        * If the value is not of type <code>BinaryData</code> an error condition will be thrown
        * because the 4GL does not support automatic type conversion for other BDT's
        * (even character and longchar are not accepted).
        * <p>
        * This variant is meant to handle the cases of built-in functions and methods in the 4GL
        * which have polymorphic return types (e.g. DYNAMIC-FUNCTION()). This should NOT be used for
        * non-polymorphic assignments.
        *
        * @param    data
        *           The instance from which to copy state.
        *
        * @throws   ErrorConditionException
        *           If the given data is not an instance of this class.
        */
       public void assign(BaseDataType data)
       {
          if (data == this)
          {
             return;
          }

          if (data == null)
          {
             setUnknown();
             return;
          }

          // the value may be a proxy; make sure to unwrap and work with the real value
          data = data.val();
          if (data.isUnknown())
          {
             setUnknown();
          }
          else if (data instanceof BinaryData)
          {
             assign((BinaryData) data);
          }
          else if (data instanceof jobject)
          {
             assign((jobject<?>) data);
          }
          else
          {
             incompatibleTypesOnConversion();
          }
       }

       /**
        * Sets the state (data and unknown value) of this instance based on the state of the passed
        * instance.
        * <p>
        * If the given instance is <code>null</code> or <code>unknown</code>, the instance will be
        * set to <code>unknown</code>.  If the length of the given data is 0 OR if the length of the
        * an initialized buffer is indeterminate, then the instance will be reset as uninitialized
        * (including deallocation of any current buffer). If the length of the array is greater than
        * 0, then any current buffer will be deallocated, a new buffer of the proper size will be
        * allocated and the data will be copied in. This is how the 4GL does it.
        *
        * @param    data
        *           The instance from which to copy state.
        */
       public void assign(BinaryData data)
       {
          if (data == this)
          {
             return;
          }

          // the value may be a proxy; make sure to unwrap and work with the real value
          if (BaseDataType.isProxy(data))
          {
             assign(data.val());
             return;
          }

          byte[] input = null;

          if (data != null && !data.isUnknown())
          {
             // if the initialized buffer is of indeterminate size, the result must deallocate
             // any current buffer, which is how the 4GL does it
             if (!data.isUninitialized() && data.internalLength() < 1)
             {
                input = new byte[0];
             }
             else
             {
                input = data.asByteArray();
             }
          }

          // centralize the assignment logic
          assign(input);
       }

       /**
        * Sets the state (data and unknown value) of this instance based on the state of the passed
        * instance.
        * <p>
        * If the given instance is <code>null</code> or <code>unknown</code>, the instance will be
        * set to <code>unknown</code>.  If the length of the given data is 0 OR if the length of the
        * an initialized buffer is indeterminate, then the instance will be reset as uninitialized
        * (including deallocation of any current buffer). If the length of the array is greater than
        * 0, then any current buffer will be deallocated, a new buffer of the proper size will be
        * allocated and the data will be copied in. This is how the 4GL does it.
        *
        * @param    value
        *           The instance from which to copy state.
        */
       public void assign(Undoable value)
       {
          assign((BinaryData) value);
       }

       /**
        * Sets the state (data and unknown value) of this instance based on the state of the passed
        * instance.
        * <p>
        * If the given instance is <code>null</code> or <code>unknown</code>, the instance will be
        * set to <code>unknown</code>.  If the length of the given data is 0 OR if the length of the
        * an initialized buffer is indeterminate, then the instance will be reset as uninitialized
        * (including deallocation of any current buffer). If the length of the array is greater than
        * 0, then any current buffer will be deallocated, a new buffer of the proper size will be
        * allocated and the data will be copied in. This is how the 4GL does it.
        *
        * @param    data
        *           The new contents.
        */
       public void assign(byte[] data)
       {
          if (data == null)
          {
             setUnknown();
          }
          else if (data.length == 0)
          {
             deallocate();

             // in assign, setting it to length 0 will clear the unknown flag
             clearUnknown();
          }
          else
          {
             replaceContents(Arrays.copyOf(data, data.length));
          }
       }

       /**
        * Compares this instance with the specified instance and returns a -1
        * if this instance is less than the specified, 0 if the two instances
        * are equal and 1 if this instance is greater than the specified
        * instance.  This is the implementation of the <code>Comparable</code>
        * interface.
        * <p>
        * The algorithm will fail to give meaningful results in the case where
        * one tries to sort against other objects that do not represent compatible
        * values.
        *
        * @param    obj
        *           The instance to compare against.
        *
        * @return   -1, 0 or 1 depending on if this instance is less than,
        *           equal to or greater (respectively) than the specified
        *           instance <code>obj</code>.
        */
       public int compareTo(Object obj)
       {
          BinaryData bd = null;

          if (obj instanceof BinaryData)
          {
             bd = (BinaryData) obj;
          }
          else
          {
             return -1;
          }

          byte[] value1 = asByteArray();
          byte[] value2 = bd.asByteArray();

          // handle null arrays (unknown value)
          if (value1 == null)
          {
             if (value2 == null)
             {
                return 0;
             }
             else
             {
                return -1;
             }
          }
          else
          {
             if (value2 == null)
             {
                return 1;
             }
          }

          // handle 0 length arrays (uninitialized memptr or raw that has never had anything PUT in)
          if (value1.length == 0)
          {
             if (value2.length == 0)
             {
                return 0;
             }
             else
             {
                return -1;
             }
          }
          else
          {
             if (value2.length == 0)
             {
                return 1;
             }
          }

          // we know that both arrays are non-null and have a non-zero length

          int minLen = Math.min(value2.length, value1.length);

          // compare overlapping portions, first difference defines the result
          for (int i = 0; i < minLen; i++)
          {
             int result = Byte.compare(value1[i],value2[i]);

             if (result != 0)
             {
                return result;
             }
          }

          // the overlapping content is equivalent, if length is the same then
          // the arrays are the same
          if (value1.length == value2.length)
          {
             return 0;
          }

          // unequal lengths, the larger array is greater
          return value1.length > value2.length ? 1 : -1;
       }

       /**
        * Hash code implementation which is consistent with {@link BaseDataType#equals}.
        * <p>
        * Performance is not stellar for large binary values. Likewise,
        * distribution of hash codes is fairly weak, since a maximum of 33
        * samples are used from the underlying byte array to differentiate
        * results. It is therefore recommended that large binary data values not
        * be used within or as hashing keys.
        *
        * @return  Hash code value for this object instance.
        */
       public int hashCode()
       {
          int result = 17;

          if (isUnknown())
          {
             return result;
          }

          byte[] value = asByteArray();

          if (value == null || value.length == 0)
          {
             return 0;
          }

          int len   = value.length;
          int upper = Math.min(100, len);

          for (int i = len % 3; i < upper; i += 3)
          {
             result = 37 * result + (int) value[i];
          }

          return result;
       }

       /**
        * Returns the byte at the given index position in the binary data.
        * <p>
        * This is equivalent to the 4GL <code>GET-BYTE</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to return, must be &gt; 0.
        *
        * @return   The byte at the given index position in the binary data or <code>unknown</code>
        *           if this instance is <code>unknown</code> or if the index position is greater
        *           than the size of the binary data or if the given index position is
        *           <code>unknown</code>.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public integer getByte(NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return new integer();
          }

          return getByte(pos.longValue());
       }

       /**
        * Returns the byte at the given index position in the binary data.
        * <p>
        * This is equivalent to the 4GL <code>GET-BYTE</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to return, must be &gt; 0.
        *
        * @return   The byte at the given index position in the binary data or <code>unknown</code>
        *           if this instance is <code>unknown</code> or if the index position is greater
        *           than the size of the binary data.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public integer getByte(long pos)
       throws ErrorConditionException
       {
          if (getMethodSetup(pos, 1, true))
          {
             // convert to 0-based array index
             pos--;

             return new integer(readByte(pos) & 0x000000FF);
          }

          return new integer();
       }

       /**
        * Sets the byte at the given index position in the binary data. Implements auto-extend and
        * length limitations for subclasses which require this support.  If any input is
        * <code>unknown</code> or if the instance itself is <code>unknown</code>, no change will
        * be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-BYTE</code> language statement.
        *
        * @param    val
        *           The byte value to assign to the given position.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data.
        */
       public BinaryData setByte(NumberType val, NumberType pos)
       throws ErrorConditionException
       {
          if (val == null || val.isUnknown() || pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setByte(val.longValue(), pos.longValue());
       }

       /**
        * Sets the byte at the given index position in the binary data. Implements auto-extend and
        * length limitations for subclasses which require this support.  If any input is
        * <code>unknown</code> or if the instance itself is <code>unknown</code>, no change will
        * be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-BYTE</code> language statement.
        *
        * @param    val
        *           The byte value to assign to the given position.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data.
        */
       public BinaryData setByte(NumberType val, long pos)
       throws ErrorConditionException
       {
          if (val == null || val.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setByte(val.longValue(), pos);
       }

       /**
        * Sets the byte at the given index position in the binary data. Implements auto-extend and
        * length limitations for subclasses which require this support.  If any input is
        * <code>unknown</code> or if the instance itself is <code>unknown</code>, no change will
        * be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-BYTE</code> language statement.
        *
        * @param    val
        *           The byte value to assign to the given position.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data.
        */
       public BinaryData setByte(long val, NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setByte(val, pos.longValue());
       }

       /**
        * Sets the byte at the given index position in the binary data. Implements auto-extend and
        * length limitations for subclasses which require this support.  If any input is
        * <code>unknown</code> or if the instance itself is <code>unknown</code>, no change will
        * be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-BYTE</code> language statement.
        *
        * @param    val
        *           The byte value to assign to the given position.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data.
        */
       public BinaryData setByte(long val, long pos)
       throws ErrorConditionException
       {
          if (setMethodSetup(pos, 1, null, true))
          {
             // convert to 0-based array index
             pos--;

             writeByte((byte) (val & 0x000000FF), pos);
          }

          return this;
       }

       /**
        * Gets the bytes at the given starting index position in the binary data up to but NOT the
        * including next <code>null</code> byte or the complete contents of the given buffer,
        * whichever comes first.  The resulting bytes will be interpreted as characters and turned
        * into a text string.
        * <p>
        * This is equivalent to the 4GL <code>GET-STRING</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The string read or <code>unknown</code> if the instance is <code>unknown</code>,
        *           or any input is <code>unknown</code> or if the index position is out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public character getString(NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return new character();
          }

          return getString(pos.longValue(), -1);
       }

       /**
        * Gets the bytes at the given starting index position in the binary data up to but NOT the
        * including next <code>null</code> byte or the complete contents of the given buffer,
        * whichever comes first.  The resulting bytes will be interpreted as characters and turned
        * into a text string.
        * <p>
        * This is equivalent to the 4GL <code>GET-STRING</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The string read or <code>unknown</code> if the instance is <code>unknown</code>,
        *           or any input is <code>unknown</code> or if the index position is out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public character getString(long pos)
       throws ErrorConditionException
       {
          return getString(pos, -1);
       }

       /**
        * Gets the bytes at the given starting index position in the binary data.
        * <p>
        * If the specified <code>len</code> is negative, this method will copy up to but NOT
        * including the first <code>null</code> byte or it will copy to the end of the string if no
        * <code>null</code> byte exists.  Otherwise, exactly the specified number of characters
        * will be copied, without regard to any embedded <code>null</code> bytes.
        * <p>
        * This is equivalent to the 4GL <code>GET-STRING</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;= the length of the
        *           buffer.
        * @param    len
        *           The number of bytes to copy.  Contrary to the 4GL documentation, all values less
        *           than 1 are allowed. Use any negative value (not just -1) to copy up to but NOT
        *           including the first <code>null</code> byte. A 0 value will cause an empty string
        *           to be returned.
        *
        * @return   The string read or <code>unknown</code> if the instance is <code>unknown</code>,
        *           or any input is <code>unknown</code> or if the index position is out of range
        *           or if the index plus len is out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public character getString(long pos, NumberType len)
       throws ErrorConditionException
       {
          if (len == null || len.isUnknown())
          {
             genUnknownArgError();
             return new character();
          }

          return getString(pos, len.longValue());
       }

       /**
        * Gets the bytes at the given starting index position in the binary data.
        * <p>
        * If the specified <code>len</code> is negative, this method will copy up to but NOT
        * including the first <code>null</code> byte or it will copy to the end of the string if no
        * <code>null</code> byte exists.  Otherwise, exactly the specified number of characters
        * will be copied, without regard to any embedded <code>null</code> bytes.
        * <p>
        * This is equivalent to the 4GL <code>GET-STRING</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;= the length of the
        *           buffer.
        * @param    len
        *           The number of bytes to copy.  Contrary to the 4GL documentation, all values less
        *           than 1 are allowed. Use any negative value (not just -1) to copy up to but NOT
        *           including the first <code>null</code> byte. A 0 value will cause an empty string
        *           to be returned.
        *
        * @return   The string read or <code>unknown</code> if the instance is <code>unknown</code>,
        *           or any input is <code>unknown</code> or if the index position is out of range
        *           or if the index plus len is out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public character getString(NumberType pos, long len)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return new character();
          }

          return getString(pos.longValue(), len);
       }

       /**
        * Gets the bytes at the given starting index position in the binary data.
        * <p>
        * If the specified <code>len</code> is negative, this method will copy up to but NOT
        * including the first <code>null</code> byte or it will copy to the end of the string if no
        * <code>null</code> byte exists.  Otherwise, exactly the specified number of characters
        * will be copied, without regard to any embedded <code>null</code> bytes.
        * <p>
        * This is equivalent to the 4GL <code>GET-STRING</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;= the length of the
        *           buffer.
        * @param    len
        *           The number of bytes to copy.  Contrary to the 4GL documentation, all values less
        *           than 1 are allowed. Use any negative value (not just -1) to copy up to but NOT
        *           including the first <code>null</code> byte. A 0 value will cause an empty string
        *           to be returned.
        *
        * @return   The string read or <code>unknown</code> if the instance is <code>unknown</code>,
        *           or any input is <code>unknown</code> or if the index position is out of range
        *           or if the index plus len is out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public character getString(NumberType pos, NumberType len)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown() || len == null || len.isUnknown())
          {
             genUnknownArgError();
             return new character();
          }

          return getString(pos.longValue(), len.longValue());
       }

       /**
        * Gets the bytes at the given starting index position in the binary data.
        * <p>
        * If the specified <code>len</code> is negative, this method will copy up to but NOT
        * including the first <code>null</code> byte or it will copy to the end of the string if no
        * <code>null</code> byte exists.  Otherwise, exactly the specified number of characters
        * will be copied, without regard to any embedded <code>null</code> bytes.
        * <p>
        * This is equivalent to the 4GL <code>GET-STRING</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;= the length of the
        *           buffer.
        * @param    len
        *           The number of bytes to copy.  Contrary to the 4GL documentation, all values less
        *           than 1 are allowed. Use any negative value (not just -1) to copy up to but NOT
        *           including the first <code>null</code> byte. A 0 value will cause an empty string
        *           to be returned.
        *
        * @return   The string read or <code>unknown</code> if the instance is <code>unknown</code>,
        *           or any input is <code>unknown</code> or if the index position is out of range
        *           or if the index plus len is out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public character getString(long pos, long len)
       throws ErrorConditionException
       {
          // because of the extra error processing in this method, we have an extra unknown
          // check that needs to be placed first (normally getMethodSetup() would handle all
          // error processing)
          if (isUnknown())
          {
             return new character();
          }

          // quick out for 0 length
          if (len == 0)
          {
             return new character("");
          }

          boolean toNull = false;

          // special case: all negative values are the unspecified length
          if (len < 0)
          {
             // this method doesn't generate an invalid index (1830) error in the 4GL
             toNull = true;
          }

          long sz = internalLength();

          // special case: length past end is reset to end instead of returning unknown
          if (sz > 0 && pos > 0 && len > 0 && ((len + pos - 1) > sz))
          {
             len = sz - (pos - 1);
          }

          if (getMethodSetup(pos, len, false))
          {
             // convert to 0-based array index
             pos--;

             // find ending location
             long end = toNull ? findNextNull(pos) : pos + len;

             character result = new character();

             result.forceBytes(readByteRange(pos, (end - pos), false));

             return result;
          }

          return new character();
       }

       /**
        * Sets the bytes at the given starting index position in the binary data to the partial or
        * complete contents of the given string. This method will copy up to and including the first
        * <code>null</code> byte or it will copy to the end of the string if no <code>null</code>
        * byte exists.
        * <p>
        * Implements auto-extend and length limitations for subclasses which require this support.
        * If the instance to be modified is <code>unknown</code> or if any of the inputs are
        * <code>unknown</code>, no change will be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-STRING</code> language statement.
        *
        * @param    data
        *           Source string from which to copy.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setString(Text data, NumberType pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown() || pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setString(data.getValue(), pos.longValue(), -1);
       }

       /**
        * Sets the bytes at the given starting index position in the binary data to the partial or
        * complete contents of the given string. This method will copy up to and including the first
        * <code>null</code> byte or it will copy to the end of the string if no <code>null</code>
        * byte exists.
        * <p>
        * Implements auto-extend and length limitations for subclasses which require this support.
        * If the instance to be modified is <code>unknown</code> or if any of the inputs are
        * <code>unknown</code>, no change will be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-STRING</code> language statement.
        *
        * @param    data
        *           Source string from which to copy.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setString(String data, NumberType pos)
       throws ErrorConditionException
       {
          if (data == null || pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setString(data, pos.longValue(), -1);
       }

       /**
        * Sets the bytes at the given starting index position in the binary data to the partial or
        * complete contents of the given string. This method will copy up to and including the first
        * <code>null</code> byte or it will copy to the end of the string if no <code>null</code>
        * byte exists.
        * <p>
        * Implements auto-extend and length limitations for subclasses which require this support.
        * If the instance to be modified is <code>unknown</code> or if any of the inputs are
        * <code>unknown</code>, no change will be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-STRING</code> language statement.
        *
        * @param    data
        *           Source string from which to copy.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setString(String data, long pos)
       throws ErrorConditionException
       {
          if (data == null)
          {
             genUnknownArgError();
             return this;
          }

          return setString(data, pos, -1);
       }

       /**
        * Sets the bytes at the given starting index position in the binary data to the partial or
        * complete contents of the given string. This method will copy up to and including the first
        * <code>null</code> byte or it will copy to the end of the string if no <code>null</code>
        * byte exists.
        * <p>
        * Implements auto-extend and length limitations for subclasses which require this support.
        * If the instance to be modified is <code>unknown</code> or if any of the inputs are
        * <code>unknown</code>, no change will be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-STRING</code> language statement.
        *
        * @param    data
        *           Source string from which to copy.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setString(Text data, long pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setString(data.getValue(), pos, -1);
       }

       /**
        * Sets the bytes at the given starting index position in the binary data to the partial or
        * complete contents of the given string. If the specified <code>len</code> is -1, this
        * method will copy up to and including the first <code>null</code> byte or it will copy to
        * the end of the string if no <code>null</code> byte exists. Otherwise, exactly the specified
        * number of characters will be copied, without regard to any embedded <code>null</code>
        * bytes.
        * <p>
        * Implements auto-extend and length limitations for subclasses which require this support.
        * If the instance to be modified is <code>unknown</code> or if any of the inputs are
        * <code>unknown</code>, no change will be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-STRING</code> language statement.
        *
        * @param    data
        *           Source string from which to copy.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        * @param    len
        *           The number of bytes to copy or -1 to copy up to and including the first
        *           <code>null</code> byte.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setString(String data, long pos, NumberType len)
       throws ErrorConditionException
       {
          if (data == null || len == null || len.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setString(data, pos, len.longValue());
       }

       /**
        * Sets the bytes at the given starting index position in the binary data to the partial or
        * complete contents of the given string. If the specified <code>len</code> is -1, this
        * method will copy up to and including the first <code>null</code> byte or it will copy to
        * the end of the string if no <code>null</code> byte exists. Otherwise, exactly the specified
        * number of characters will be copied, without regard to any embedded <code>null</code>
        * bytes.
        * <p>
        * Implements auto-extend and length limitations for subclasses which require this support.
        * If the instance to be modified is <code>unknown</code> or if any of the inputs are
        * <code>unknown</code>, no change will be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-STRING</code> language statement.
        *
        * @param    data
        *           Source string from which to copy.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        * @param    len
        *           The number of bytes to copy or -1 to copy up to and including the first
        *           <code>null</code> byte.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setString(Text data, long pos, long len)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setString(data.getValue(), pos, len);
       }

       /**
        * Sets the bytes at the given starting index position in the binary data to the partial or
        * complete contents of the given string. If the specified <code>len</code> is -1, this
        * method will copy up to and including the first <code>null</code> byte or it will copy to
        * the end of the string if no <code>null</code> byte exists. Otherwise, exactly the specified
        * number of characters will be copied, without regard to any embedded <code>null</code>
        * bytes.
        * <p>
        * Implements auto-extend and length limitations for subclasses which require this support.
        * If the instance to be modified is <code>unknown</code> or if any of the inputs are
        * <code>unknown</code>, no change will be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-STRING</code> language statement.
        *
        * @param    data
        *           Source string from which to copy.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        * @param    len
        *           The number of bytes to copy or -1 to copy up to and including the first
        *           <code>null</code> byte.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setString(String data, NumberType pos, long len)
       throws ErrorConditionException
       {
          if (data == null || pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setString(data, pos.longValue(), len);
       }

       /**
        * Sets the bytes at the given starting index position in the binary data to the partial or
        * complete contents of the given string. If the specified <code>len</code> is -1, this
        * method will copy up to and including the first <code>null</code> byte or it will copy to
        * the end of the string if no <code>null</code> byte exists. Otherwise, exactly the specified
        * number of characters will be copied, without regard to any embedded <code>null</code>
        * bytes.
        * <p>
        * Implements auto-extend and length limitations for subclasses which require this support.
        * If the instance to be modified is <code>unknown</code> or if any of the inputs are
        * <code>unknown</code>, no change will be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-STRING</code> language statement.
        *
        * @param    data
        *           Source string from which to copy.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        * @param    len
        *           The number of bytes to copy or -1 to copy up to and including the first
        *           <code>null</code> byte.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setString(Text data, long pos, NumberType len)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown() || len == null || len.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setString(data.getValue(), pos, len.longValue());
       }

       /**
        * Sets the bytes at the given starting index position in the binary data to the partial or
        * complete contents of the given string. If the specified <code>len</code> is -1, this
        * method will copy up to and including the first <code>null</code> byte or it will copy to
        * the end of the string if no <code>null</code> byte exists. Otherwise, exactly the specified
        * number of characters will be copied, without regard to any embedded <code>null</code>
        * bytes.
        * <p>
        * Implements auto-extend and length limitations for subclasses which require this support.
        * If the instance to be modified is <code>unknown</code> or if any of the inputs are
        * <code>unknown</code>, no change will be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-STRING</code> language statement.
        *
        * @param    data
        *           Source string from which to copy.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        * @param    len
        *           The number of bytes to copy or -1 to copy up to and including the first
        *           <code>null</code> byte.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setString(BaseDataType data, long pos, NumberType len)
       throws ErrorConditionException
       {
          UnimplementedFeature.missing("PUT-STRING(BDT, int, integer) is not implemented");
          return this;
       }

       /**
        * Sets the bytes at the given starting index position in the binary data to the partial or
        * complete contents of the given string. If the specified <code>len</code> is -1, this
        * method will copy up to and including the first <code>null</code> byte or it will copy to
        * the end of the string if no <code>null</code> byte exists. Otherwise, exactly the specified
        * number of characters will be copied, without regard to any embedded <code>null</code>
        * bytes.
        * <p>
        * Implements auto-extend and length limitations for subclasses which require this support.
        * If the instance to be modified is <code>unknown</code> or if any of the inputs are
        * <code>unknown</code>, no change will be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-STRING</code> language statement.
        *
        * @param    data
        *           Source string from which to copy.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        * @param    len
        *           The number of bytes to copy or -1 to copy up to and including the first
        *           <code>null</code> byte.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setString(Text data, NumberType pos, long len)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown() || pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setString(data.getValue(), pos.longValue(), len);
       }

       /**
        * Sets the bytes at the given starting index position in the binary data to the partial or
        * complete contents of the given string. If the specified <code>len</code> is -1, this
        * method will copy up to and including the first <code>null</code> byte or it will copy to
        * the end of the string if no <code>null</code> byte exists. Otherwise, exactly the specified
        * number of characters will be copied, without regard to any embedded <code>null</code>
        * bytes.
        * <p>
        * Implements auto-extend and length limitations for subclasses which require this support.
        * If the instance to be modified is <code>unknown</code> or if any of the inputs are
        * <code>unknown</code>, no change will be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-STRING</code> language statement.
        *
        * @param    data
        *           Source string from which to copy.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        * @param    len
        *           The number of bytes to copy or -1 to copy up to and including the first
        *           <code>null</code> byte.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setString(String data, NumberType pos, NumberType len)
       throws ErrorConditionException
       {
          if (data == null || pos == null || pos.isUnknown() || len == null || len.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setString(data, pos.longValue(), len.longValue());
       }

       /**
        * Sets the bytes at the given starting index position in the binary data to the partial or
        * complete contents of the given string. If the specified <code>len</code> is -1, this
        * method will copy up to and including the first <code>null</code> byte or it will copy to
        * the end of the string if no <code>null</code> byte exists. Otherwise, exactly the specified
        * number of characters will be copied, without regard to any embedded <code>null</code>
        * bytes.
        * <p>
        * Implements auto-extend and length limitations for subclasses which require this support.
        * If the instance to be modified is <code>unknown</code> or if any of the inputs are
        * <code>unknown</code>, no change will be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-STRING</code> language statement.
        *
        * @param    data
        *           Source string from which to copy.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        * @param    len
        *           The number of bytes to copy or -1 to copy up to and including the first
        *           <code>null</code> byte.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setString(Text data, NumberType pos, NumberType len)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown() ||
              pos == null  || pos.isUnknown()  ||
              len == null  || len.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setString(data.getValue(), pos.longValue(), len.longValue());
       }

       /**
        * Sets the bytes at the given starting index position in the binary data to the partial or
        * complete contents of the given string. If the specified <code>len</code> is -1, this
        * method will copy up to and including the first <code>null</code> byte or it will copy to
        * the end of the string if no <code>null</code> byte exists. Otherwise, exactly the specified
        * number of characters will be copied, without regard to any embedded <code>null</code>
        * bytes.
        * <p>
        * Implements auto-extend and length limitations for subclasses which require this support.
        * If the instance to be modified is <code>unknown</code> or if any of the inputs are
        * <code>unknown</code>, no change will be made.
        * <p>
        * This is equivalent to the 4GL <code>PUT-STRING</code> language statement.
        *
        * @param    data
        *           Source string from which to copy.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        * @param    len
        *           The number of bytes to copy or -1 to copy up to and including the first
        *           <code>null</code> byte.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setString(String data, long pos, long len)
       throws ErrorConditionException
       {
          // because of the extra error processing in this method, we have an extra unknown
          // check that needs to be placed first (normally setMethodSetup() would handle all
          // error processing)
          if (isUnknown())
          {
             return this;
          }

          if (data == null)
          {
             genUnknownArgError();
             return this;
          }

          boolean addNull = false;

          // special case (incorrect or unspecified length)
          if (len < 1)
          {
             // -1 is the "unspecified" length
             if (len <= -1)
             {
                // OK, just copy the whole thing (plus add a null byte at the end)
                len = data.getBytes(Charset.forName(I18nOps.getJavaCharset())).length + 1;
                addNull = true;
             }
             else
             {
                // TODO: this code was commented because in OE 11.x it was found no error condition was raised.
                // this may have been something specific to older OE versions.
                // invalid index!
                // genIndexError();
                return this;
             }
          }

          if (setMethodSetup(pos, len, null, false))
          {
             // convert to 0-based array index
             pos--;

             // obtain the data as a byte[]
             StringBuilder sb = new StringBuilder();
             sb.append(data);

             if (addNull)
             {
                sb.append('\0');
             }

             writeByteRange(sb.toString().getBytes(Charset.forName(I18nOps.getJavaCharset())), pos, len, false);
          }

          return this;
       }

       /**
        * Returns the signed 32-bit (4 byte) value from the given position. This reads exactly
        * 4 bytes and those bytes are directly interpreted as a signed integer value. This
        * cannot overflow and it does not have any range checking.  This is exactly the same
        * way that the 4GL implements the result.
        * <p>
        * This is equivalent to the 4GL <code>GET-LONG</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The 4 bytes from the given position. <code>unknown</code> will be returned if
        *           the instance is <code>unknown</code>, if any input is <code>unknown</code> or if
        *           any portion of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public integer getLong(long pos)
       throws ErrorConditionException
       {
          // integer constructor will result unknown instance if the worker returns null
          return new integer(getIntegerWorker(pos, 4, false));
       }

       /**
        * Returns the signed 32-bit (4 byte) value from the given position.
        * <p>
        * This is equivalent to the 4GL <code>GET-LONG</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The 4 bytes from the given position. <code>unknown</code> will be returned if
        *           the instance is <code>unknown</code>, if any input is <code>unknown</code> or if
        *           any portion of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public integer getLong(NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return new integer();
          }

          return getLong(pos.longValue());
       }

       /**
        * Writes the signed 32-bit (4 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-LONG</code> language statement.
        *
        * @param    data
        *           The 32-bit value to be written.  As in the 4GL, this must be greater than or
        *           equal to -2147483648 and less than or equal to 2147483647.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setLong(long data, long pos)
       throws ErrorConditionException
       {
          RangeCheck range = new RangeCheck(-2147483648, 2147483647, data);

          return setIntegerWorker(data, pos, 4, range, false);
       }

       /**
        * Writes the signed 32-bit (4 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-LONG</code> language statement.
        *
        * @param    data
        *           The 32-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setLong(NumberType data, long pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setLong(data.longValue(), pos);
       }

       /**
        * Writes the signed 32-bit (4 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-LONG</code> language statement.
        *
        * @param    data
        *           The 32-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setLong(long data, NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setLong(data, pos.longValue());
       }

       /**
        * Writes the signed 32-bit (4 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-LONG</code> language statement.
        *
        * @param    data
        *           The 32-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setLong(NumberType data, NumberType pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown() || pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setLong(data.longValue(), pos.longValue());
       }

       /**
        * Returns the signed 16-bit (2 byte) value from the given position.
        * <p>
        * This is equivalent to the 4GL <code>GET-SHORT</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The 2 bytes from the given position. <code>unknown</code> will be returned if
        *           the instance is <code>unknown</code>, if any input is <code>unknown</code> or if
        *           any portion of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public integer getShort(NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return new integer();
          }

          return getShort(pos.longValue());
       }

       /**
        * Returns the signed 16-bit (2 byte) value from the given position.
        * <p>
        * This is equivalent to the 4GL <code>GET-SHORT</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The 2 bytes from the given position. <code>unknown</code> will be returned if
        *           the instance is <code>unknown</code>, if any input is <code>unknown</code> or if
        *           any portion of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public integer getShort(long pos)
       throws ErrorConditionException
       {
          // integer constructor will result unknown instance if the worker returns null
          return new integer(getIntegerWorker(pos, 2, true));
       }

       /**
        * Writes the signed 16-bit (2 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-SHORT</code> language statement.
        *
        * @param    data
        *           The 16-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setShort(long data, long pos)
       throws ErrorConditionException
       {
          return setIntegerWorker(data, pos, 2, null, true);
       }

       /**
        * Writes the signed 16-bit (2 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-SHORT</code> language statement.
        *
        * @param    data
        *           The 16-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setShort(NumberType data, long pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setShort(data.longValue(), pos);
       }

       /**
        * Writes the signed 16-bit (2 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-SHORT</code> language statement.
        *
        * @param    data
        *           The 16-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setShort(long data, NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setShort(data, pos.longValue());
       }

       /**
        * Writes the signed 16-bit (2 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-SHORT</code> language statement.
        *
        * @param    data
        *           The 16-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setShort(NumberType data, NumberType pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown() || pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setShort(data.longValue(), pos.longValue());
       }

       /**
        * Returns the 64-bit (8 byte) binary floating point data at the given position.
        * <p>
        * This is equivalent to the 4GL <code>GET-DOUBLE</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to read from, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The 8 bytes from the given position, interpreted as a binary floating point
        *           value. <code>unknown</code> will be returned if the instance is
        *           <code>unknown</code>, if any input is <code>unknown</code> or if any portion
        *           of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public decimal getDouble(NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return new decimal();
          }

          return getDouble(pos.longValue());
       }

       /**
        * Returns the 64-bit (8 byte) binary floating point data at the given position.
        * <p>
        * This is equivalent to the 4GL <code>GET-DOUBLE</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to read from, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The 8 bytes from the given position, interpreted as a binary floating point
        *           value. <code>unknown</code> will be returned if the instance is
        *           <code>unknown</code>, or if any portion of the read bytes are in positions
        *           that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public decimal getDouble(long pos)
       throws ErrorConditionException
       {
          Double value = getUnprocessedDouble(pos);

          return (value == null) ? new decimal() : new decimal(value);
       }

       /**
        * Returns the 64-bit (8 byte) binary floating point data at the given position
        * without wrapping it in a decimal instance. This will potentially avoid issues
        * that may raise error conditions.
        *
        * @param    pos
        *           The 1-based index position to read from, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The 8 bytes from the given position, interpreted as a binary floating point
        *           value. <code>null</code> is returned if the instance is <code>unknown</code>,
        *           or if any portion of the read bytes are in positions that are out of range.
        */
       public Double getUnprocessedDouble(long pos)
       throws ErrorConditionException
       {
          Long value = getIntegerWorker(pos, 8, true);

          // the value is encoded in 64-bits which is best treated as a long
          return (value == null) ? null : Double.longBitsToDouble(value.longValue());
       }

       /**
        * Write the decimal as a 64-bit (8 byte) binary floating-point value at the specified
        * location.
        * <p>
        * This is equivalent to the 4GL <code>PUT-DOUBLE</code> language statement.
        *
        * @param    data
        *           The value to be written.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setDouble(double data, long pos)
       throws ErrorConditionException
       {
          // TODO: check if this encoding is right, check if raw version is right

          // Check if data is -0.0 in order to make it 0.0 because in 4GL both values have same
          // representations
          data = (data == -0.0) ? 0.0 : data;

          // the value is encoded in 64-bits which is best treated as a long
          return setIntegerWorker(Double.doubleToRawLongBits(data), pos, 8, null, true);
       }

       /**
        * Write the decimal as a 64-bit (8 byte) binary floating-point value at the specified
        * location.
        * <p>
        * This is equivalent to the 4GL <code>PUT-DOUBLE</code> language statement.
        *
        * @param    data
        *           The value to be written.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setDouble(double data, NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setDouble(data, pos.longValue());
       }

       /**
        * Write the decimal as a 64-bit (8 byte) binary floating-point value at the specified
        * location.
        * <p>
        * This is equivalent to the 4GL <code>PUT-DOUBLE</code> language statement.
        *
        * @param    data
        *           The value to be written.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setDouble(NumberType data, long pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setDouble(data.doubleValue(), pos);
       }

       /**
        * Write the decimal as a 64-bit (8 byte) binary floating-point value at the specified
        * location.
        * <p>
        * This is equivalent to the 4GL <code>PUT-DOUBLE</code> language statement.
        *
        * @param    data
        *           The value to be written.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setDouble(NumberType data, NumberType pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown() || pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setDouble(data.doubleValue(), pos.longValue());
       }

       /**
        * Returns the 32-bit (4 byte) binary floating point data at the given position.
        * <p>
        * This is equivalent to the 4GL <code>GET-FLOAT</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to read from, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The 4 bytes from the given position, interpreted as a binary floating point
        *           value. <code>unknown</code> will be returned if the instance is
        *           <code>unknown</code>, if any input is <code>unknown</code> or if any portion
        *           of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public decimal getFloat(NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return new decimal();
          }

          return getFloat(pos.longValue());
       }

       /**
        * Returns the 32-bit (4 byte) binary floating point data at the given position.
        * <p>
        * This is equivalent to the 4GL <code>GET-FLOAT</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to read from, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The 4 bytes from the given position, interpreted as a binary floating point
        *           value. <code>unknown</code> will be returned if the instance is
        *           <code>unknown</code>, if any input is <code>unknown</code> or if any portion
        *           of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public decimal getFloat(long pos)
       throws ErrorConditionException
       {
          Long value = getIntegerWorker(pos, 4, true);

          if (value == null)
             return new decimal();

          // the value is encoded in 32-bits which is best treated as an int
          return new decimal(Float.intBitsToFloat(value.intValue()));
       }

       /**
        * Write the decimal as a 32-bit (4 byte) binary floating-point value at the specified
        * location.
        * <p>
        * This is equivalent to the 4GL <code>PUT-FLOAT</code> language statement.
        *
        * @param    data
        *           The value to be written.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setFloat(double data, long pos)
       throws ErrorConditionException
       {
          // TODO: check if this encoding is right, check if raw version is right

          // the value is encoded in 32-bits which is best treated as an int
          return setIntegerWorker(Float.floatToRawIntBits((float) data), pos, 4, null, true);
       }

       /**
        * Write the decimal as a 32-bit (4 byte) binary floating-point value at the specified
        * location.
        * <p>
        * This is equivalent to the 4GL <code>PUT-FLOAT</code> language statement.
        *
        * @param    data
        *           The value to be written.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setFloat(double data, NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setFloat(data, pos.longValue());
       }

       /**
        * Write the decimal as a 32-bit (4 byte) binary floating-point value at the specified
        * location.
        * <p>
        * This is equivalent to the 4GL <code>PUT-FLOAT</code> language statement.
        *
        * @param    data
        *           The value to be written.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setFloat(NumberType data, long pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setFloat(data.doubleValue(), pos);
       }

       /**
        * Write the decimal as a 32-bit (4 byte) binary floating-point value at the specified
        * location.
        * <p>
        * This is equivalent to the 4GL <code>PUT-FLOAT</code> language statement.
        *
        * @param    data
        *           The value to be written.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setFloat(NumberType data, NumberType pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown() || pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setFloat(data.doubleValue(), pos.longValue());
       }

       /**
        * Returns the signed 64-bit (8 byte) value from the given position.
        * <p>
        * This is equivalent to the 4GL <code>GET-INT64</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The 8 bytes from the given position. <code>unknown</code> will be returned if
        *           the instance is <code>unknown</code>, if any input is <code>unknown</code> or if
        *           any portion of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public int64 getInt64(NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return new int64();
          }

          return getInt64(pos.longValue());
       }

       /**
        * Returns the signed 64-bit (8 byte) value from the given position.
        * <p>
        * This is equivalent to the 4GL <code>GET-INT64</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The 8 bytes from the given position. <code>unknown</code> will be returned if
        *           the instance is <code>unknown</code>, if any input is <code>unknown</code> or if
        *           any portion of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public int64 getInt64(long pos)
       throws ErrorConditionException
       {
          // int64 constructor will result unknown instance if the worker returns null
          return new int64(getIntegerWorker(pos, 8, false));
       }

       /**
        * Writes the signed 64-bit (8 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-INT64</code> language statement.
        *
        * @param    data
        *           The 64-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setInt64(long data, long pos)
       throws ErrorConditionException
       {
          return setIntegerWorker(data, pos, 8, null, false);
       }

       /**
        * Writes the signed 64-bit (8 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-INT64</code> language statement.
        *
        * @param    data
        *           The 64-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setInt64(long data, NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setInt64(data, pos.longValue());
       }

       /**
        * Writes the signed 64-bit (8 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-INT64</code> language statement.
        *
        * @param    data
        *           The 64-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setInt64(NumberType data, long pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setInt64(data.longValue(), pos);
       }

       /**
        * Writes the signed 64-bit (8 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-INT64</code> language statement.
        *
        * @param    data
        *           The 64-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setInt64(NumberType data, NumberType pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown() || pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setInt64(data.longValue(), pos.longValue());
       }

       /**
        * Returns the unsigned 16-bit (2 byte) value from the given position.
        * <p>
        * This is equivalent to the 4GL <code>GET-UNSIGNED-SHORT</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The 2 bytes from the given position. <code>unknown</code> will be returned if
        *           the instance is <code>unknown</code>, if any input is <code>unknown</code> or if
        *           any portion of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public integer getUnsignedShort(NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return new integer();
          }

          return getUnsignedShort(pos.longValue());
       }

       /**
        * Returns the unsigned 16-bit (2 byte) value from the given position.
        * <p>
        * This is equivalent to the 4GL <code>GET-UNSIGNED-SHORT</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The 2 bytes from the given position. <code>unknown</code> will be returned if
        *           the instance is <code>unknown</code>, if any input is <code>unknown</code> or if
        *           any portion of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public integer getUnsignedShort(long pos)
       throws ErrorConditionException
       {
          // integer constructor will result unknown instance if the worker returns null
          return new integer(getIntegerWorker(pos, 2, true));
       }

       /**
        * Writes the unsigned 16-bit (2 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-UNSIGNED-SHORT</code> language statement.
        *
        * @param    data
        *           The 16-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setUnsignedShort(long data, long pos)
       throws ErrorConditionException
       {
          return setIntegerWorker(data, pos, 2, null, true);
       }

       /**
        * Writes the unsigned 16-bit (2 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-UNSIGNED-SHORT</code> language statement.
        *
        * @param    data
        *           The 16-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setUnsignedShort(long data, NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setUnsignedShort(data, pos.longValue());
       }

       /**
        * Writes the unsigned 16-bit (2 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-UNSIGNED-SHORT</code> language statement.
        *
        * @param    data
        *           The 16-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setUnsignedShort(NumberType data, long pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setUnsignedShort(data.longValue(), pos);
       }

       /**
        * Writes the unsigned 16-bit (2 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-UNSIGNED-SHORT</code> language statement.
        *
        * @param    data
        *           The 16-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setUnsignedShort(NumberType data, NumberType pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown() || pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setUnsignedShort(data.longValue(), pos.longValue());
       }

       /**
        * Returns the unsigned 32-bit (4 byte) value from the given position. Since the returned
        * value is 8 bytes long, all possible 4 byte values safely fit inside, so there is no
        * range checking needed (the 4GL does not range check this).  But the bytes that are read
        * are treated as the least significant 4 bytes of the returned data, leaving the 4 most
        * significant bytes set as all 0 bits. The practical consequence of this implementation
        * is that the resulting value will never be a negative number and it will naturally only
        * ever contain values between 0 (0x0000000000000000) and 4294967295 (0x00000000FFFFFFFF)
        * inclusive.
        * <p>
        * This is equivalent to the 4GL <code>GET-UNSIGNED-SHORT</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The 4 bytes from the given position. <code>unknown</code> will be returned if
        *           the instance is <code>unknown</code>, if any input is <code>unknown</code> or if
        *           any portion of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public int64 getUnsignedLong(NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return new int64();
          }

          return getUnsignedLong(pos.longValue());
       }

       /**
        * Returns the unsigned 32-bit (4 byte) value from the given position. Since the returned
        * value is 8 bytes long, all possible 4 byte values safely fit inside, so there is no
        * range checking needed (the 4GL does not range check this).  But the bytes that are read
        * are treated as the least significant 4 bytes of the returned data, leaving the 4 most
        * significant bytes set as all 0 bits. The practical consequence of this implementation
        * is that the resulting value will never be a negative number and it will naturally only
        * ever contain values between 0 (0x0000000000000000) and 4294967295 (0x00000000FFFFFFFF)
        * inclusive.
        * <p>
        * This is equivalent to the 4GL <code>GET-UNSIGNED-SHORT</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        *
        * @return   The 4 bytes from the given position. <code>unknown</code> will be returned if
        *           the instance is <code>unknown</code>, if any input is <code>unknown</code> or if
        *           any portion of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public int64 getUnsignedLong(long pos)
       throws ErrorConditionException
       {
          // the int64 constructor will automatically handle null as unknown
          return new int64(getIntegerWorker(pos, 4, false));
       }

       /**
        * Writes the unsigned 32-bit (4 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-UNSIGNED-LONG</code> language statement.
        *
        * @param    data
        *           The 32-bit value to be written. As in the 4GL, this value must be greater than
        *           or equal to 0 and less than or equal to 4294967295.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setUnsignedLong(long data, long pos)
       throws ErrorConditionException
       {
          RangeCheck range = new RangeCheck(0, 4294967295L, (long) data);

          return setIntegerWorker(data, pos, 4, range, false);
       }

       /**
        * Writes the unsigned 32-bit (4 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-UNSIGNED-LONG</code> language statement.
        *
        * @param    data
        *           The 32-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setUnsignedLong(long data, NumberType pos)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setUnsignedLong(data, pos.longValue());
       }

       /**
        * Writes the unsigned 32-bit (4 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-UNSIGNED-LONG</code> language statement.
        *
        * @param    data
        *           The 32-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setUnsignedLong(NumberType data, long pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setUnsignedLong(data.longValue(), pos);
       }

       /**
        * Writes the unsigned 32-bit (4 byte) value into the binary data starting at the given
        * position.
        * <p>
        * This is equivalent to the 4GL <code>PUT-UNSIGNED-LONG</code> language statement.
        *
        * @param    data
        *           The 32-bit value to be written.
        * @param    pos
        *           The 1-based index position, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setUnsignedLong(NumberType data, NumberType pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown() || pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setUnsignedLong(data.longValue(), pos.longValue());
       }

       /**
        * Reads the given number of bytes starting at the given index position.
        * <p>
        * This is equivalent to the 4GL <code>GET-BYTES</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        * @param    len
        *           The number of bytes to read.
        *
        * @return   The binary data of the specified length. <code>unknown</code> will be returned if
        *           the instance is <code>unknown</code>, if any input is <code>unknown</code> or if
        *           any portion of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public BinaryData getBytes(NumberType pos, NumberType len)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown() || len == null || len.isUnknown())
          {
             genUnknownArgError();
             return (BinaryData) instantiateUnknown();
          }

          return getBytes(pos.longValue(), len.longValue());
       }

       /**
        * Reads the given number of bytes starting at the given index position.
        * <p>
        * This is equivalent to the 4GL <code>GET-BYTES</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        * @param    len
        *           The number of bytes to read.
        *
        * @return   The binary data of the specified length. <code>unknown</code> will be returned if
        *           the instance is <code>unknown</code>, if any input is <code>unknown</code> or if
        *           any portion of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public BinaryData getBytes(long pos, NumberType len)
       throws ErrorConditionException
       {
          if (len == null || len.isUnknown())
          {
             genUnknownArgError();
             return (BinaryData) instantiateUnknown();
          }

          return getBytes(pos, len.longValue());
       }

       /**
        * Reads the given number of bytes starting at the given index position.
        * <p>
        * This is equivalent to the 4GL <code>GET-BYTES</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        * @param    len
        *           The number of bytes to read.
        *
        * @return   The binary data of the specified length. <code>unknown</code> will be returned if
        *           the instance is <code>unknown</code>, if any input is <code>unknown</code> or if
        *           any portion of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public BinaryData getBytes(NumberType pos, long len)
       throws ErrorConditionException
       {
          if (pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return (BinaryData) instantiateUnknown();
          }

          return getBytes(pos.longValue(), len);
       }

       /**
        * Reads the given number of bytes starting at the given index position.
        * <p>
        * This is equivalent to the 4GL <code>GET-BYTES</code> built-in function.
        *
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0 and &lt;=
        *           the length of the buffer.
        * @param    len
        *           The number of bytes to read.
        *
        * @return   The binary data of the specified length. <code>unknown</code> will be returned if
        *           the instance is <code>unknown</code>, if any input is <code>unknown</code> or if
        *           any portion of the read bytes are in positions that are out of range.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1.
        */
       public BinaryData getBytes(long pos, long len)
       throws ErrorConditionException
       {
          if (getMethodSetup(pos, len, false))
          {
             pos--;

             byte[] data = readByteRange(pos, len, false);

             // normally, we would not ever want a reference to a child class from inside the parent
             // class, but in this case there is a major benefit to be gained; the idea here is that
             // since raw is implemented using byte[] on the Java heap and does not need to call down
             // to the client nor does it need to allocate a native buffer (especially since this is
             // a temporary instance that will generally be assigned immediately and then discarded

             // testcases written to check that this size behavior is right have shown that the
             // actual type returned in the 4GL is neither RAW nor MEMPTR, so this result should be
             // as good as anything else (I guess we could return the byte[] and have it be null
             // for unknown, but until this is shown to be insufficient, we will go with this

             return (len <= raw.MAX_SIZE) ? new raw(data) : new memptr(data);
          }

          return (BinaryData) instantiateUnknown();
       }

       /**
        * Writes the given data into the specified location of the current instance.
        * <p>
        * This is equivalent to the 4GL <code>PUT-BYTES</code> language statement.
        *
        * @param    data
        *           The binary data to be copied.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setBytes(BaseDataType data, long pos)
       throws ErrorConditionException
       {
          UnimplementedFeature.missing("PUT-BYTES(BDT) is not implemented.");
          return this;
       }

       /**
        * Writes the given data into the specified location of the current instance.
        * <p>
        * This is equivalent to the 4GL <code>PUT-BYTES</code> language statement.
        *
        * @param    data
        *           The binary data to be copied.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setBytes(BinaryData data, long pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          long sz = data.internalLength();

          if (setMethodSetup(pos, sz, null, false))
          {
             // if data is an initialized instance with an indeterminate size (this happens
             // with set-pointer-value() that isn't followed by a set-size(); the result will be
             // a no-op in that case
             if (sz < 1)
             {
                return this;
             }

             // convert to 0-based
             pos--;

             long len = internalLength();

             // don't overrun the destination buffer (may be negative if boundary checking is
             // disabled)
             long limit = len - pos;

             // normally, we would not ever want a reference to a child class from inside the parent
             // class, but in this case there is a major benefit to be gained; if copying memptr to
             // memptr, we copy in 1 trip down to the client instead of 2 trips (as an optimization)
             if (this instanceof memptr && data instanceof memptr)
             {
                memptr me = (memptr) this;
                me.copyByteRange((memptr) data, pos, (limit >= 0 && limit < sz) ? limit : sz);
             }
             else
             {
                // at this point data should not be unknown, uninitialized or of indeterminate
                // length so this should never be null
                byte[] input = data.asByteArray();

                // cut down the array if there is more data there than should be written out
                if (limit >= 0 && input.length > limit)
                {
                   input = Arrays.copyOf(input, (int) limit);
                }

                writeByteRange(input, pos, -1, false);
             }
          }

          return this;
       }

       /**
        * Writes the given data into the specified location of the current instance.
        * <p>
        * This is equivalent to the 4GL <code>PUT-BYTES</code> language statement.
        *
        * @param    data
        *           The binary data to be copied.
        * @param    pos
        *           The 1-based index position to set, must be &gt; 0.
        *
        * @return   The current instance is returned.
        *
        * @throws   ErrorConditionException
        *           If the instance is uninitialized or if the index position is &lt; 1. Subclasses
        *           which don't auto-extend will also cause an exception when the index position
        *           is past the end of the data or when the index position plus the length of the
        *           data to be written exceed the end of the destination buffer.
        */
       public BinaryData setBytes(BinaryData data, NumberType pos)
       throws ErrorConditionException
       {
          if (data == null || data.isUnknown() || pos == null || pos.isUnknown())
          {
             genUnknownArgError();
             return this;
          }

          return setBytes(data, pos.longValue());
       }

       /**
        * Returns the length of the given instance in bytes.
        *
        * @param   bd
        *          The instance to test.
        *
        * @return  The number of bytes in the given instance or {@code unknown}.
        */
       public static int64 length(BinaryData bd)
       {
          if (bd == null || bd.isUnknown())
          {
             return int64.UNKNOWN;
          }

          return int64.of(bd.visibleLength());
       }

       /**
        * Returns the length this instance's data in bytes.
        *
        * @param   ignored
        *          OE QUIRK! The {@code GET-SIZE} function normally takes only one parameter, that is the current
        *          object which the conversion switch from first parameter. However, the OE method silently
        *          accepts multiple other parameters which are ignored, regardless of their type.
        *
        * @return  The number of bytes in this instance or {@code unknown}.
        */
       public int64 length(Object... ignored)
       {
          if (isUnknown())
          {
             return int64.UNKNOWN;
          }

          return int64.of(visibleLength());
       }

       /**
        * Returns the length this instance's data in bytes.
        *
        * @return  The number of bytes in this instance or 0 if this instance is {@code unknown}.
        */
       public long lengthOf()
       {
          return isUnknown() ? 0 : visibleLength();
       }

       /**
        * Sets the length of the buffer/array to a given length.  Depending on the sub-class, this
        * may result in the allocation of memory, or the extending/truncating of the backing array.
        * It is up to the sub-class to determine if an already allocated buffer/array will be
        * resized.
        *
        * @param    len
        *           New length of the buffer/array.
        *
        * @return   The current instance is returned.
        */
       public abstract BinaryData setLength(long len);

       /**
        * Sets the length of the buffer/array to a given length.  Depending on the sub-class, this
        * may result in the allocation of memory, or the extending/truncating of the backing array.
        * It is up to the sub-class to determine if an already allocated buffer/array will be
        * resized.
        *
        * @param    len
        *           New length of the buffer/array. If this is <code>unknown</code> then the instance
        *           will be set to <code>unknown</code>.
        *
        * @return   The current instance is returned.
        */
       public BinaryData setLength(NumberType len)
       {
          if (len == null || len.isUnknown())
          {
             setUnknown();

             return this;
          }

          return setLength(len.longValue());
       }

       /**
        * Sets the length of the buffer/array to a given length.  Depending on the sub-class, this
        * may result in the allocation of memory, or the extending/truncating of the backing array.
        * It is up to the sub-class to determine if an already allocated buffer/array will be
        * resized.
        *
        * @param    len
        *           New length of the buffer/array. If this is <code>unknown</code> then the instance
        *           will be set to <code>unknown</code>.
        *
        * @return   The current instance is returned.
        */
       public BinaryData setLength(BaseDataType len)
       {
          if (len == null || len.isUnknown())
          {
             setUnknown();

             return this;
          }

          if (len instanceof NumberType)
          {
             return setLength(((NumberType) len).longValue());
          }
          else if (len instanceof date)
          {
             return setLength(new integer((date) len));
          }
          else if (len instanceof character)
          {
             return setLength(new integer((character) len));
          }
          else
          {
             UnimplementedFeature.unsupported("SET-SIZE(memptr) = POLY");

             setUnknown();
             return this;
          }
       }

       /**
        * Replacement for the default object reading method. The latest
        * state is read from the input source.
        *
        * @param    in
        *           The input source from which fields will be restored.
        *
        * @throws   IOException
        *           In case of I/O errors.
        * @throws   ClassNotFoundException
        *           If payload can't be instantiated.
        */
       public void readExternal(ObjectInput in)
       throws IOException,
              ClassNotFoundException
       {
          unknown = in.readBoolean();
       }

       /**
        * Replacement for the default object writing method. The latest
        * state is written to the output destination.
        *
        * @param    out
        *           The output destination to which fields will be saved.
        *
        * @throws   IOException
        *           In case of I/O errors.
        */
       public void writeExternal(ObjectOutput out)
       throws IOException
       {
          out.writeBoolean(unknown);
       }

       /**
        * Reports if this instance represents an uninitialized buffer. Being uninitialized is not the
        * same as being of zero length.  Rather it means that the buffer was never allocated. Being
        * <code>unknown</code> is also different from being uninitialized.
        *
        * @return    <code>true</code> if this instance is uninitialized.
        */
       public abstract boolean isUninitialized();

       /**
        * Sets the state of this instance's <code>unknown</code> flag to <code>false</code>.
        */
       protected void clearUnknown()
       {
          unknown = false;
       }

       /**
        * Return a copy of the input array with the order of the byte reversed.
        *
        * @param    data
        *           The input data to reverse.
        *
        * @return   The copy with reversed byte ordering.
        */
       protected byte[] reverseBytes(byte[] data)
       {
          byte[] copy = new byte[data.length];

          for (int i = 0; i < data.length; i++)
          {
             copy[data.length - (i + 1)] = data[i];
          }

          return copy;
       }

       /**
        * Generate the standard error message in the case where an access is
        * requested for a location past the end of an initialized
        * <code>RAW</code> variable.
        */
       protected void genLimitedLengthError()
       throws ErrorConditionException
       {
          String err = "PUTBYTE/LENGTH assignment too long -- increase -s parameter";
          ErrorManager.recordOrThrowError(1829, err);
       }

       /**
        * Release any resources associated with the instance.
        */
       protected abstract void deallocate();

       /**
        * Determines if the array of binary data stored in an instance of this
        * class must be limited in length.
        *
        * @return    <code>true</code> if the length must be limited.
        */
       protected abstract boolean isLengthLimited();

       /**
        * Determines the maximum length of the array of binary data stored in an
        * instance of this class.
        *
        * @return    The length limit for binary data or 0 if this class has no limit.
        */
       protected abstract long lengthLimit();

       /**
        * Determines if the array of binary data stored in an instance of this
        * class must be automatically extended in length when operations
        * write past the current length.  Such extensions will pad with
        * <code>null</code> bytes.
        *
        * @return   <code>true</code> if the length must automatically extended.
        */
       protected abstract boolean isAutoExtend();

       /**
        * Helper to create a new byte array with the same contents as the given
        * byte array and the specified length.  If the given target length
        * is smaller than the source array length, the output array is truncated
        * to the specified size.  If the target length is larger than the
        * source array length, the output array is extended to the given size
        * and any additional bytes are initialized to '\0'.
        * <p>
        * This is safe to use on 0 length arrays.
        *
        * @param    len
        *           The output array length.
        */
       protected abstract void extendBytes(long len);

       /**
        * Replace the entire contents of the current buffer with the given array.
        * <p>
        * The {@link BinaryData#unknown} flag is also cleared, only if <code>value</code> is not
        * <code>null</code> and if the length is greater than 0.
        *
        * @param    value
        *           The new contents.
        */
       protected abstract void replaceContents(byte[] value);

       /**
        * Return the entire contents of the current buffer as an array. This may be a copy of the
        * data, depending on the subclass' implementation.  DO NOT MODIFY the data that is returned,
        * since it is undefined as to whether the changes will or will not be reflected in the
        * actual storage.
        *
        * @return    The contents of the instance (possibly a copy). If the instance is uninitialized
        *            the returned value will be a 0 length array. If the instance is
        *            <code>unknown</code>, then <code>null</code> will be returned.
        */
       protected abstract byte[] asByteArray();

       /**
        * The actual size of the buffer (in bytes).
        *
        * @return  The length or 0 if uninitialized.
        */
       protected abstract long internalLength();

       /**
        * The length of the buffer as reported to P4GL application.
        *
        * @return  The length or 0 if uninitialized.
        */
       protected abstract long visibleLength();

       /**
        * Returns the next index position into the array which is the <code>null</code> byte,
        * starting at a given index position.
        *
        * @param    pos
        *           The 0-based index into the array at which to start searching.
        *
        * @return   The next index into the array which is the <code>null</code> byte or the length
        *           of the array if no <code>null</code> byte exists.
        */
       protected abstract long findNextNull(long pos);

       /**
        * Read the byte at the given location.  All boundary checking and error handling must have
        * already been done in the caller such that this is known to be a valid access. Any
        * unexpected failure may cause a platform-specific exception that terminates the process!
        *
        * @param    pos
        *           The 0-based index position at which to read the byte.
        *
        * @return   The byte at that location.
        */
       protected abstract byte readByte(long pos);

       /**
        * Write the byte into the given location. All boundary checking and error handling must have
        * already been done in the caller such that this is known to be a valid access. Any
        * unexpected failure may cause a platform-specific exception that terminates the process!
        *
        * @param    val
        *           The byte to write.
        * @param    pos
        *           The 0-based index position at which to read the byte.
        */
       protected abstract void writeByte(byte val, long pos);

       /**
        * Read the bytes at the given location.  All boundary checking and error handling must have
        * already been done in the caller such that this is known to be a valid access. Any
        * unexpected failure may cause a platform-specific exception that terminates the process!
        *
        * @param    pos
        *           The 0-based index position at which to read the bytes.
        * @param    len
        *           The number of bytes to read.
        * @param    endian
        *           <code>true</code> to force the returned data to be ordered by the endian-ness of the
        *           instance.
        *
        * @return   The byte range at that location.
        */
       protected abstract byte[] readByteRange(long pos, long len, boolean endian);

       /**
        * Write the bytes at the given location.  All boundary checking and error handling must have
        * already been done in the caller such that this is known to be a valid access. Any
        * unexpected failure may cause a platform-specific exception that terminates the process!
        *
        * @param    data
        *           The bytes to write.
        * @param    pos
        *           The 0-based index position at which to start writing the bytes.
        * @param    len
        *           How many bytes should be written. -1 means write everything to the end of the
        *           array.
        * @param    endian
        *           <code>true</code> to honor the endian-ness of the instance. The <code>data</code>
        *           array MUST be ordered in little-endian order if the endian flag is on.
        */
       protected abstract void writeByteRange(byte[] data, long pos, long len, boolean endian);

       /**
        * Common worker to handle the error checking/processing with no triggering
        * of auto-expand features.
        *
        * @param    first
        *           The 1-based index position of first access, must be &gt; 0.
        * @param    len
        *           The number of bytes to be accessed starting at <code>pos</code>, must be &gt; 0
        *           except for the "unspecified" length passed to GET-STRING, in which case this is
        *           given as -1.
        * @param    idxQuirk
        *           <code>true</code> if the caller disables boundary checking when the instance
        *           is initialized with NEGATIVE indeterminate size (as opposed to size zero). This
        *           occurs when SET-POINTER-VALUE is called without a subsquent call to SET-SIZE
        *           which would establish the boundary. Only certain callers disable this checking
        *           and when passed as <code>false</code> then the normal checking applies. Since
        *           the indeterminate size is always less than the first index position, this will
        *           always generate an error.
        *
        * @return   <code>true</code> if no errors occurred and the caller should continue
        *           processing. <code>false</code> if the caller should immediately return
        *           <code>unknown</code>.
        *
        * @throws   ErrorConditionException
        *           When any of the tested failure conditions are encountered.
        */
       private boolean getMethodSetup(long first, long len, boolean idxQuirk)
       throws ErrorConditionException
       {
          if (isUnknown())
          {
             return false;
          }

          if (isUninitialized())
          {
             genUninitializedError();
             return false;
          }

          if (first < 1)
          {
             genIndexError();
             return false;
          }

          long size = internalLength();

          // negative or zero size means we can't boundary check the end, but otherwise we check
          // and return unknown when the starting position is past the end of the buffer
          if (size > 0 && first > size)
          {
             return false;
          }

          // special case when the initialized var has a negative length AND an unspecified
          // GET-STRING length was passed
          if (size < 0 && len < 0)
          {
             return false;
          }

          // at this point the instance is known to be initialized, so if the size is
          // indeterminate (size < 1), then a negative size disables boundary checking
          // for those callers with the index quirk OR anytime the size is 0 the
          // checking is disabled OR when the size is positive and the unspecified length
          // was passed to GET-STRING
          boolean bypass = (idxQuirk && size < 0) || (size > 0 && len < 0) || (size == 0);

          // index of last byte to be accessed
          long last = (first + len - 1);

          // only in the non-bypass case do we return unknown for accessing past the end of
          // the buffer
          if (!bypass && last > size)
          {
             return false;
          }

          return true;
       }

       /**
        * Common worker to handle the error checking/processing and the honoring of length/auto-expand
        * features.
        *
        * @param    first
        *           The 1-based index position of first access, must be &gt; 0.
        * @param    len
        *           The number of bytes to be accessed starting at <code>pos</code>, must be &gt; 0.
        * @param    range
        *           A caller-encoded range check or <code>null</code> if no range check is needed.
        * @param    idxQuirk
        *           <code>true</code> if the caller disables boundary checking when the instance
        *           is initialized with NEGATIVE indeterminate size (as opposed to size zero). This
        *           occurs when SET-POINTER-VALUE is called without a subsequent call to SET-SIZE
        *           which would establish the boundary. Only certain callers disable this checking
        *           and when passed as <code>false</code> then the normal checking applies. Since
        *           the indeterminate size is always less than the first index position, this will
        *           always generate an error.
        *
        * @return   <code>true</code> if no errors occurred and the caller should continue
        *           processing. <code>false</code> if the caller should immediately return
        *           <code>unknown</code>.
        *
        * @throws   ErrorConditionException
        *           When any of the tested failure conditions are encountered.
        */
       private boolean setMethodSetup(long first, long len, RangeCheck range, boolean idxQuirk)
       throws ErrorConditionException
       {
          // the order of operations here must not be changed, it is implemented to exactly
          // match the order in the 4GL

          if (isUnknown())
          {
             return false;
          }

          if (isUninitialized())
          {
             genUninitializedError();
             return false;
          }

          if (first < 1)
          {
             genIndexError();
             return false;
          }

          if (range != null)
          {
             range.check();
          }

          // index of last byte to be accessed
          long last = (first + len - 1);

          long size = internalLength();

          // special fixup mode is possible here
          if (first > size || last > size)
          {
             if (isAutoExtend())
             {
                // error if this is too long
                if (isLengthLimited())
                {
                   long limit = lengthLimit();

                   if (first > limit || last > limit)
                   {
                      genLimitedLengthError();
                      return false;
                   }
                }

                // extend and pad with nulls
                extendBytes(last);
             }
             else
             {
                // should be only for MEMPTR

                // at this point the instance is known to be initialized, so if the size is
                // indeterminate (size < 1), then a negative size disables boundary checking
                // for those callers with the index quirk AND anytime the size is 0 the
                // checking is disabled
                boolean bypass = (idxQuirk && size < 0) || (size == 0);

                if (!bypass)
                {
                   genOutOfBoundsError();
                   return false;
                }
             }
          }

          return true;
       }

       /**
        * Generate the standard error message in the case where an invalid
        * index position (&lt; 1) is used.
        */
       public static void genIndexError()
       throws ErrorConditionException
       {
          String err = "Byte position for PUT-BYTE, GET-BYTE and other" +
                       " raw functions must be 1 or greater";

          ErrorManager.recordOrThrowError(1830, err, false);
       }

       /**
        * Generate the standard error message in the case where an access is
        * requested for an uninitialized variable.
        */
       protected void genUninitializedError()
       throws ErrorConditionException
       {
          String err = "Put/Get functions not allowed on uninitialized memory";
          ErrorManager.recordOrThrowError(2914, err, false);
       }

       /**
        * Generate the standard error message in the case where an access is
        * requested for a location past the end of an initialized
        * <code>MEMPTR</code> variable.
        */
       private void genOutOfBoundsError()
       throws ErrorConditionException
       {
          String err = "Can't PUT past the end of the MEMPTR";
          ErrorManager.recordOrThrowError(4791, err, false);
       }

       /**
        * Generate the standard error message in the case where a <code>unknown</code> is passed to
        * GET or PUT. The instance itself being <code>unknown</code> is a different issue and does
        * not cause the GET or PUT to fail.
        */
       private void genUnknownArgError()
       throws ErrorConditionException
       {
          String err = "Unable to evaluate expression with UNKNOWN value in argument";
          ErrorManager.recordOrThrowError(4391, err, false);
       }

       /**
        * Puts <code>len</code> bytes from <code>data</code> into internal storage starting at
        * <code>pos</code>. This method is called for int64 (len = 8), long (len = 4) and short
        * (len = 2).
        *
        * @param    data
        *           The bytes that need to be stored.
        * @param    pos
        *           The position in the storage.
        * @param    len
        *           The amount of bytes to be copied.
        * @param    range
        *           A caller-encoded range check or <code>null</code> if no range check is needed.
        * @param    idxQuirk
        *           <code>true</code> if the caller disables boundary checking when the instance
        *           is initialized with NEGATIVE indeterminate size (as opposed to size zero). This
        *           occurs when SET-POINTER-VALUE is called without a subsquent call to SET-SIZE
        *           which would establish the boundary. Only certain callers disable this checking
        *           and when passed as <code>false</code> then the normal checking applies. Since
        *           the indeterminate size is always less than the first index position, this will
        *           always generate an error.
        *
        * @return   The current instance.
        */
       private BinaryData setIntegerWorker(long       data,
                                           long       pos,
                                           int        len,
                                           RangeCheck range,
                                           boolean    idxQuirk)
       {
          if (setMethodSetup(pos, len, range, idxQuirk))
          {
             // convert to 0-based array index
             pos--;

             byte[] bin = new byte[len];

             // write each byte into the byte[]
             for (int k = 0; k < len; k++)
             {
                // copy the least significant byte
                bin[k] = (byte)(data & 0xFF);

                // shift to the right to eliminate the byte we just stored
                data >>>= 8;
             }

             writeByteRange(bin, pos, -1, true);
          }

          return this;
       }

       /**
        * Returns a number using <code>len</code> bytes at the <code>pos</code> position. If there are
        * no such bytes available a <code>null</code> object is returned.  The caller is responsible
        * for returning <code>unknown</code> when <code>null</code> is returned.
        *
        * @param    pos
        *           The position from where the bytes are user to compose the returning integer.
        * @param    len
        *           The amount of bytes to be used. Any amount of bytes can be specified limited only
        *           by the 8 bytes of returning <code>Long</code>, but practical values are 8, 4 and 2.
        * @param    idxQuirk
        *           <code>true</code> if the caller disables boundary checking when the instance
        *           is initialized with NEGATIVE indeterminate size (as opposed to size zero). This
        *           occurs when SET-POINTER-VALUE is called without a subsquent call to SET-SIZE
        *           which would establish the boundary. Only certain callers disable this checking
        *           and when passed as <code>false</code> then the normal checking applies. Since
        *           the indeterminate size is always less than the first index position, this will
        *           always generate an error.
        *
        * @return   A <code>Long</code> that contains a number obtained from the specified bytes or
        *           <code>null</code> if specified bytes are not available.
        */
       private Long getIntegerWorker(long pos, int len, boolean idxQuirk)
       {
          if (!getMethodSetup(pos, len, idxQuirk))
          {
             return null;
          }

          // convert to 0-based index
          pos--;

          long retVal = 0;

          byte[] value = readByteRange(pos, len, true);

          // the operation must be carried out in reverse order from the setter ((len - 1) - k)
          for (int k = 0; k < len; k++)
          {
             // shift the current value to make space for the currently processed byte
             retVal <<= 8;

             // the current byte must be masked for negative values (when converting negative byte
             // values to 64-bit long the first 7 bytes will be 0xFF, so they need to be eliminated)
             retVal |= (value[(len - 1) - k] & 0xFF);
          }

          return retVal;
       }

       /**
        * Encapsulates a range check and allows the execution of it to be delegated to other code.
        */
       protected static class RangeCheck
       {
          /** The lower bound of the range (inclusive). */
          private long low;

          /** The upper bound of the range (inclusive). */
          private long high;

          /** The value to be tested. */
          private long value;

          /**
           * Create an instance.
           *
           * @param    low
           *           The lower bound of the range (inclusive).
           * @param    high
           *           The upper bound of the range (inclusive).
           * @param    value
           *           The value to be tested.
           */
          public RangeCheck(long low, long high, long value)
          {
             this.low   = low;
             this.high  = high;
             this.value = value;
          }

          /**
           * Check that the value is greater than or equal to the lower bound and less than or equal
           * to the upper bound. Does the same as the object {@link RangeCheck} except it does not
           * create additional objects on the heap.
           *
           * @param    low
           *           The lower bound of the range (inclusive).
           * @param    high
           *           The upper bound of the range (inclusive).
           * @param    value
           *           The value to be tested.
           *
           * @throws    ErrorConditionException
           *            If the range check fails.
           */
          public static void check(long value, long low, long high)
          {
             if (value < low || value > high)
             {
                String msg = String.format("Value %d too large to fit in INTEGER", value);
                ErrorManager.recordOrThrowError(15747, msg);
             }
          }

          /**
           * Check that the value is greater than or equal to the lower bound and less than or equal
           * to the upper bound.
           *
           * @throws    ErrorConditionException
           *            If the range check fails.
           */
          public void check()
          throws ErrorConditionException
          {
             check(value, low, high);
          }
       }
    }