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);
      }
   }
}