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 < 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 < 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 > 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 < 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 > 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 < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0 and <=
* 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 < 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 > 0 and <=
* 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 < 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 > 0 and <= 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 < 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 > 0 and <= 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 < 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 > 0 and <= 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 < 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 > 0 and <= 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 < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 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 < 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 > 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 < 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 > 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 < 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 > 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 < 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 > 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 < 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 > 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 < 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 > 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 < 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 > 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 < 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 > 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 < 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 > 0 and <=
* 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 < 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 > 0 and <=
* 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 < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0 and <=
* 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 < 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 > 0 and <=
* 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 < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0 and <=
* 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 < 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 > 0 and <=
* 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 < 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 > 0 and <=
* 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0 and <=
* 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 < 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 > 0 and <=
* 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 < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0 and <=
* 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 < 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 > 0 and <=
* 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 < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0 and <=
* 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 < 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 > 0 and <=
* 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 < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0 and <=
* 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 < 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 > 0 and <=
* 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 < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0 and <=
* 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 < 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 > 0 and <=
* 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 < 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 > 0 and <=
* 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 < 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 > 0 and <=
* 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 < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
*
* @return The current instance is returned.
*
* @throws ErrorConditionException
* If the instance is uninitialized or if the index position is < 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 > 0.
* @param len
* The number of bytes to be accessed starting at <code>pos</code>, must be > 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 > 0.
* @param len
* The number of bytes to be accessed starting at <code>pos</code>, must be > 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 (< 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);
}
}
}