Record.java
/*
** Module : Record.java
** Abstract : The abstract base class for all business data model object classes.
**
** Copyright (c) 2019-2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
** 001 ECF 20191001 Created initial version with basic data and accessors.
** OM 20200110 Added support various data types and setter/getter signatures.
** 002 AIL 20200826 Handle getters should use a string to handle conversion without zero default
** for invalid resources.
** 003 OM 20201213 Added utility method used for debugging.
** CA 20210305 The object and handle fields have a Long representation at the SQL table field.
** OM 20210404 CLOB fields are created and returned with their CP set.
** ECF 20210519 Added APIs to increment/decrement reference count for "in-use" tracking.
** OM 20210618 Adjusted toString2() so that the method can be used to report broken records detected
** at import time.
** OM 20210806 Fixed getter for unknown raw values.
** CA 20220329 Added methods which get the record's property to a map (with keys the legacy field names),
** and which allows to populate the record from such a map. This is used by REST services
** written directly in Java, to serialize any record parameters.
** OM 20220713 Added support for converting to/from POJOs.
** OM 20220718 Fixed looping in getPropertyValues(). Added to the returned list the denormalized props.
** TJD 20220504 Upgrade do Java 11 minor changes
** CA 20221109 Implemented runtime for EXCEPT/FIELDS options - only NO-LOCK records are loaded as
** 'partial/incomplete'.
** 004 SBI 20260226 Extracted getPropertyValues into PropertiesDescriptor interface.
** 005 CA 20230928 Force the type of an unknown object field to 'Progress.Lang.Object'.
** 006 CA 20240324 Use logical constants for TRUE, FALSE, UNKNOWN, within internal FWD runtime.
** 007 CA 20240403 Backed out CA/006 - the returned getter instance can be used by internal runtime.
** 008 AL2 20240409 Added isMultiplyReferenced and reworked the reference counting.
** 009 TJD 20240123 Java 17 compatibility updates
** 010 CA 20241030 Avoid 'Text.javaSpacifyNull' if the string is known to already have been processed.
** 011 AP 20250226 Added method for fast equality checking of data arrays.
*/
/*
** 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.persist;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.math.*;
import java.sql.*;
import java.sql.Date;
import java.time.*;
import java.util.*;
import java.util.function.*;
import com.goldencode.p2j.oo.lang.BaseObject;
import com.goldencode.p2j.persist.orm.*;
import com.goldencode.p2j.util.*;
/**
* The abstract base class for all business data model object classes. Provides an API for a
* concrete implementation to access the low level data of this object as FWD data wrapper types.
* Subclasses are intended to be generated at runtime, rather than coded by hand.
*/
public abstract class Record
extends BaseRecord
implements PropertiesDescriptor
{
/**
* Default constructor needed by DMOs extending this class.
*/
protected Record()
{
super();
}
/**
* Creates a snapshot copy of the current record.
*
* @return a new {@code Record} instance of this record with all fields set to values of the
* respective fields in this {@code Record} at the moment of the call.
*/
public Record snapshot()
{
try
{
Record shot = getClass().getDeclaredConstructor().newInstance();
shot.copy(this);
shot.state.state |= COPY;
return shot;
}
catch (ReflectiveOperationException |
IllegalArgumentException |
SecurityException e)
{
throw new RuntimeException(e); // PersistenceException (?)
}
}
/**
* Get an {@code integer} value for the datum at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset from which to retrieve the value from the property array.
*
* @return The datum as an {@code integer}; will represent unknown value if the datum is
* {@code null}.
*/
public final integer _getInteger(int offset)
{
checkIncomplete(offset);
Integer datum = (Integer) data[offset];
return datum != null ? new integer(datum) : new integer();
}
/**
* Set an {@code integer} value into the data at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset at which to store the given datum in the property array.
* @param w
* FWD wrapper type for the integral value.
*/
protected final void _setInteger(int offset, NumberType w)
{
Integer datum = null;
if (w != null)
{
integer num = (w instanceof integer) ? (integer) w : new integer(w);
if (!num.isUnknown())
{
datum = num.toJavaIntegerType();
}
}
setDatum(offset, datum);
}
/**
* Bulk getter for a {@code integer} extent field. All components are returned as a single
* array of {@code integer}.
*
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*
* @return An array of {@code integer} representing the elements of an extent field.
*/
protected final integer[] _getInteger(int extent, int offset)
{
integer[] ret = new integer[extent];
for (int k = 0; k < extent; k++)
{
ret[k] = _getInteger(offset + k);
}
return ret;
}
/**
* Bulk setter for a {@code NumberType} extent field. Each components from {@code val}
* parameter will initialize their correspondent of the extent field in {@code data} array.
*
* @param val
* An array of {@code NumberType} values to be used for setting the extent field.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setInteger(NumberType[] val, int extent, int offset)
{
int len = Math.min(val.length, extent);
for (int k = 0; k < len; k++)
{
_setInteger(offset + k, val[k]);
}
}
/**
* Mass setter for a {@code NumberType} extent field. All components of the extent field will
* be set to {@code val} in {@code data} array.
*
* @param val
* A {@code NumberType} value to be used for setting the extent field components.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setInteger(NumberType val, int extent, int offset)
{
for (int k = 0; k < extent; k++)
{
_setInteger(offset + k, val);
}
}
/**
* Get an {@code datetimetz} value for the datum at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset from which to retrieve the value from the property array.
*
* @return The datum as an {@code datetimetz}; will represent unknown value if the datum is
* {@code null}.
*/
public final datetimetz _getDatetimetz(int offset)
{
checkIncomplete(offset);
OffsetDateTime datum = (OffsetDateTime) data[offset];
return datum != null ? new datetimetz(datum) : new datetimetz();
}
/**
* Set an {@code datetimetz} value into the data at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset at which to store the given datum in the property array.
* @param w
* FWD wrapper type for the integral value.
*/
protected final void _setDatetimetz(int offset, date w)
{
OffsetDateTime datum = null;
if (w != null && !w.isUnknown())
{
datetimetz dtz = w instanceof datetimetz ? (datetimetz) w : new datetimetz(w);
datum = dtz.offsetDateTimeValue();
}
setDatum(offset, datum);
}
/**
* Bulk getter for a {@code datetimetz} extent field. All components are returned as a single
* array of {@code datetimetz}.
*
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*
* @return An array of {@code datetimetz} representing the elements of an extent field.
*/
protected final datetimetz[] _getDatetimetz(int extent, int offset)
{
datetimetz[] ret = new datetimetz[extent];
for (int k = 0; k < extent; k++)
{
ret[k] = _getDatetimetz(offset + k);
}
return ret;
}
/**
* Bulk setter for a {@code datetimetz} extent field. Each components from {@code val}
* parameter will initialize their correspondent of the extent field in {@code data} array.
*
* @param val
* An array of {@code date} values to be used for setting the extent field.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setDatetimetz(date[] val, int extent, int offset)
{
int len = Math.min(val.length, extent);
for (int k = 0; k < len; k++)
{
_setDatetimetz(offset + k, val[k]);
}
}
/**
* Mass setter for a {@code datetimetz} extent field. All components of the extent field will
* be set to {@code val} in {@code data} array.
*
* @param val
* A {@code date} value to be used for setting the extent field components.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setDatetimetz(date val, int extent, int offset)
{
for (int k = 0; k < extent; k++)
{
_setDatetimetz(offset + k, val);
}
}
/**
* Get an {@code datetime} value for the datum at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset from which to retrieve the value from the property array.
*
* @return The datum as an {@code datetime}; will represent unknown value if the datum is
* {@code null}.
*/
public final datetime _getDatetime(int offset)
{
checkIncomplete(offset);
Timestamp datum = (Timestamp) data[offset];
return datum != null ? new datetime(datum) : new datetime();
}
/**
* Set an {@code datetime} value into the data at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset at which to store the given datum in the property array.
* @param w
* FWD wrapper type for the integral value.
*/
protected final void _setDatetime(int offset, date w)
{
Timestamp datum = null;
if (w != null && !w.isUnknown())
{
datum = new Timestamp(w.dateValue().getTime());
}
setDatum(offset, datum);
}
/**
* Bulk getter for a {@code datetime} extent field. All components are returned as a single
* array of {@code datetime}.
*
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*
* @return An array of {@code datetime} representing the elements of an extent field.
*/
protected final datetime[] _getDatetime(int extent, int offset)
{
datetime[] ret = new datetime[extent];
for (int k = 0; k < extent; k++)
{
ret[k] = _getDatetime(offset + k);
}
return ret;
}
/**
* Bulk setter for a {@code datetime} extent field. Each components from {@code val}
* parameter will initialize their correspondent of the extent field in {@code data} array.
*
* @param val
* An array of {@code datetime} values to be used for setting the extent field.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setDatetime(date[] val, int extent, int offset)
{
int len = Math.min(val.length, extent);
for (int k = 0; k < len; k++)
{
_setDatetime(offset + k, val[k]);
}
}
/**
* Mass setter for a {@code datetime} extent field. All components of the extent field will
* be set to {@code val} in {@code data} array.
*
* @param val
* A {@code date} value to be used for setting the extent field components.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setDatetime(date val, int extent, int offset)
{
for (int k = 0; k < extent; k++)
{
_setDatetime(offset + k, val);
}
}
/**
* Get an {@code date} value for the datum at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset from which to retrieve the value from the property array.
*
* @return The datum as an {@code date}; will represent unknown value if the datum is
* {@code null}.
*/
public final date _getDate(int offset)
{
checkIncomplete(offset);
Date datum = (Date) data[offset];
return datum != null ? new date(datum) : new date();
}
/**
* Set an {@code date} value into the data at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset at which to store the given datum in the property array.
* @param d
* FWD wrapper type for the integral value.
*/
protected final void _setDate(int offset, date d)
{
Date datum = null;
if (d != null && !d.isUnknown())
{
datum = new Date(d.dateValue(null).getTime());
}
setDatum(offset, datum);
}
/**
* Bulk getter for a {@code date} extent field. All components are returned as a single
* array of {@code date}.
*
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*
* @return An array of {@code date} representing the elements of an extent field.
*/
protected final date[] _getDate(int extent, int offset)
{
date[] ret = new date[extent];
for (int k = 0; k < extent; k++)
{
ret[k] = _getDate(offset + k);
}
return ret;
}
/**
* Bulk setter for a {@code date} extent field. Each components from {@code val}
* parameter will initialize their correspondent of the extent field in {@code data} array.
*
* @param val
* An array of {@code date} values to be used for setting the extent field.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setDate(date[] val, int extent, int offset)
{
int len = Math.min(val.length, extent);
for (int k = 0; k < len; k++)
{
_setDate(offset + k, val[k]);
}
}
/**
* Mass setter for a {@code date} extent field. All components of the extent field will
* be set to {@code val} in {@code data} array.
*
* @param val
* A {@code date} value to be used for setting the extent field components.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setDate(date val, int extent, int offset)
{
for (int k = 0; k < extent; k++)
{
_setDate(offset + k, val);
}
}
/**
* Get an {@code integer} value for the datum at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset from which to retrieve the value from the property array.
*
* @return The datum as an {@code integer}; will represent unknown value if the datum is
* {@code null}.
*/
public final logical _isLogical(int offset)
{
checkIncomplete(offset);
Boolean datum = (Boolean) data[offset];
return datum != null ? new logical(datum) : new logical();
}
/**
* Set an {@code logical} value into the data at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset at which to store the given datum in the property array.
* @param w
* FWD wrapper type for the logical value.
*/
protected final void _setLogical(int offset, logical w)
{
Boolean datum = null;
if (w != null && !w.isUnknown())
{
datum = w.booleanValue();
}
setDatum(offset, datum);
}
/**
* Bulk getter for a {@code logical} extent field. All components are returned as a single
* array of {@code logical}.
*
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*
* @return An array of {@code logical} representing the elements of an extent field.
*/
protected final logical[] _isLogical(int extent, int offset)
{
logical[] ret = new logical[extent];
for (int k = 0; k < extent; k++)
{
ret[k] = _isLogical(offset + k);
}
return ret;
}
/**
* Bulk setter for a {@code logical} extent field. Each components from {@code val} parameter
* will initialize their correspondent of the extent field in {@code data} array.
*
* @param val
* An array of {@code logical} values to be used for setting the extent field.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setLogical(logical[] val, int extent, int offset)
{
int len = Math.min(val.length, extent);
for (int k = 0; k < len; k++)
{
_setLogical(offset + k, val[k]);
}
}
/**
* Mass setter for a {@code logical} extent field. All components of the extent field will be
* set to {@code val} in {@code data} array.
*
* @param val
* A {@code logical} value to be used for setting the extent field components.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setLogical(logical val, int extent, int offset)
{
for (int k = 0; k < extent; k++)
{
_setLogical(offset + k, val);
}
}
/**
* Get an {@code int64} value for the datum at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset from which to retrieve the value from the property array.
*
* @return The datum as an {@code int64}; will represent unknown value if the datum is
* {@code null}.
*/
public final int64 _getInt64(int offset)
{
Long datum = (Long) data[offset];
return datum != null ? new int64(datum) : new int64();
}
/**
* Set an {@code integer} value into the data at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset at which to store the given datum in the property array.
* @param w
* FWD wrapper type for the integral value.
*/
protected final void _setInt64(int offset, NumberType w)
{
Long datum = null;
if (w != null)
{
int64 num = (w instanceof int64) ? (int64) w : new int64(w);
if (!num.isUnknown())
{
datum = num.toJavaLongType();
}
}
setDatum(offset, datum);
}
/**
* Bulk getter for a {@code int64} extent field. All components are returned as a single
* array of {@code int64}.
*
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*
* @return An array of {@code int64} representing the elements of an extent field.
*/
protected final int64[] _getInt64(int extent, int offset)
{
int64[] ret = new int64[extent];
for (int k = 0; k < extent; k++)
{
ret[k] = _getInt64(offset + k);
}
return ret;
}
/**
* Bulk setter for a {@code NumberType} extent field. Each components from {@code val}
* parameter will initialize their correspondent of the extent field in {@code data} array.
*
* @param val
* An array of {@code NumberType} values to be used for setting the extent field.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setInt64(NumberType[] val, int extent, int offset)
{
int len = Math.min(val.length, extent);
for (int k = 0; k < len; k++)
{
_setInt64(offset + k, val[k]);
}
}
/**
* Mass setter for a {@code NumberType} extent field. All components of the extent field will
* be set to {@code val} in {@code data} array.
*
* @param val
* A {@code NumberType} value to be used for setting the extent field components.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setInt64(NumberType val, int extent, int offset)
{
for (int k = 0; k < extent; k++)
{
_setInt64(offset + k, val);
}
}
/**
* Get a {@code decimal} value for the datum at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset from which to retrieve the value from the property array.
*
* @return The datum as a {@code decimal}; will represent unknown value if the datum is
* {@code null}.
*/
public final decimal _getDecimal(int offset)
{
checkIncomplete(offset);
BigDecimal datum = (BigDecimal) data[offset];
return datum != null ? new decimal(datum) : new decimal();
}
/**
* Set a {@code decimal} value into the data at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset at which to store the given datum in the property array.
* @param w
* FWD wrapper type for the decimal value.
*/
protected final void _setDecimal(int offset, NumberType w)
{
BigDecimal datum = null;
if (w != null)
{
decimal num = (w instanceof decimal) ? (decimal) w : new decimal(w);
if (!num.isUnknown())
{
datum = num.toJavaType();
int scale = num.getPrecision();
int propScale = _recordMeta().getPropertyMeta(false)[offset].getDecimals();
if (propScale != scale)
{
datum = datum.setScale(propScale, RoundingMode.HALF_UP);
}
}
}
setDatum(offset, datum);
}
/**
* Bulk getter for a {@code integer} extent field. All components are returned as a single
* array of {@code integer}.
*
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*
* @return An array of {@code integer} representing the elements of an extent field.
*/
protected final decimal[] _getDecimal(int extent, int offset)
{
decimal[] ret = new decimal[extent];
for (int k = 0; k < extent; k++)
{
ret[k] = _getDecimal(offset + k);
}
return ret;
}
/**
* Bulk setter for a {@code NumberType} extent field. Each components from {@code val}
* parameter will initialize their correspondent of the extent field in {@code data} array.
*
* @param val
* An array of {@code NumberType} values to be used for setting the extent field.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setDecimal(NumberType[] val, int extent, int offset)
{
int len = Math.min(val.length, extent);
for (int k = 0; k < len; k++)
{
_setDecimal(offset + k, val[k]);
}
}
/**
* Mass setter for a {@code NumberType} extent field. All components of the extent field will
* be set to {@code val} in {@code data} array.
*
* @param val
* A {@code NumberType} value to be used for setting the extent field components.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setDecimal(NumberType val, int extent, int offset)
{
for (int k = 0; k < extent; k++)
{
_setDecimal(offset + k, val);
}
}
/**
* Get a {@code character} value for the datum at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset from which to retrieve the value from the property array.
*
* @return The datum as a {@code character}; will represent unknown value if the datum is
* {@code null}. The value will have the case-sensitivity inherent to the
* corresponding field.
*/
public final character _getCharacter(int offset)
{
checkIncomplete(offset);
String datum = (String) data[offset];
if (datum != null)
{
return new character(datum, _recordMeta().getPropertyMeta(false)[offset].isCaseSensitive(), false);
}
return new character();
}
/**
* Set a {@code character} value into the data at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset at which to store the given datum in the property array.
* @param w
* FWD wrapper type for the character value. The case-sensitivity property of this
* wrapper instance is not preserved.
*/
protected final void _setCharacter(int offset, Text w)
{
String datum = null;
if (w != null && !w.isUnknown())
{
datum = w.toJavaType();
}
setDatum(offset, datum);
}
/**
* Bulk getter for a {@code character} extent field. All components are returned as a single
* array of {@code character}.
*
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*
* @return An array of {@code character} representing the elements of an extent field.
*/
protected final character[] _getCharacter(int extent, int offset)
{
character[] ret = new character[extent];
for (int k = 0; k < extent; k++)
{
ret[k] = _getCharacter(offset + k);
}
return ret;
}
/**
* Bulk setter for a {@code Text} extent field. Each components from {@code val}
* parameter will initialize their correspondent of the extent field in {@code data} array.
*
* @param val
* An array of {@code Text} values to be used for setting the extent field.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setCharacter(Text[] val, int extent, int offset)
{
int len = Math.min(val.length, extent);
for (int k = 0; k < len; k++)
{
_setCharacter(offset + k, val[k]);
}
}
/**
* Mass setter for a {@code Text} extent field. All components of the extent field will
* be set to {@code val} in {@code data} array.
*
* @param val
* A {@code Text} value to be used for setting the extent field components.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setCharacter(Text val, int extent, int offset)
{
for (int k = 0; k < extent; k++)
{
_setCharacter(offset + k, val);
}
}
/**
* Get a {@code rowid} value for the datum at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset from which to retrieve the value from the property array.
*
* @return The datum as a {@code rowid}; will represent unknown value if the datum is
* {@code null}. The value will have the case-sensitivity inherent to the
* corresponding field.
*/
public final rowid _getRowid(int offset)
{
checkIncomplete(offset);
Long datum = (Long) data[offset];
return datum != null ? new rowid(datum) : new rowid();
}
/**
* Set a {@code rowid} value into the data at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset at which to store the given datum in the property array.
* @param w
* FWD wrapper type for the rowid value. The case-sensitivity property of this
* wrapper instance is not preserved.
*/
protected final void _setRowid(int offset, rowid w)
{
Long datum = null;
if (w != null && !w.isUnknown())
{
datum = w.getValue();
}
setDatum(offset, datum);
}
/**
* Bulk getter for a {@code rowid} extent field. All components are returned as a single
* array of {@code rowid}.
*
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*
* @return An array of {@code rowid} representing the elements of an extent field.
*/
protected final rowid[] _getRowid(int extent, int offset)
{
rowid[] ret = new rowid[extent];
for (int k = 0; k < extent; k++)
{
ret[k] = _getRowid(offset + k);
}
return ret;
}
/**
* Bulk setter for a {@code rowid} extent field. Each components from {@code val}
* parameter will initialize their correspondent of the extent field in {@code data} array.
*
* @param val
* An array of {@code rowid} values to be used for setting the extent field.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setRowid(rowid[] val, int extent, int offset)
{
int len = Math.min(val.length, extent);
for (int k = 0; k < len; k++)
{
_setRowid(offset + k, val[k]);
}
}
/**
* Mass setter for a {@code rowid} extent field. All components of the extent field will
* be set to {@code val} in {@code data} array.
*
* @param val
* A {@code rowid} value to be used for setting the extent field components.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setRowid(rowid val, int extent, int offset)
{
for (int k = 0; k < extent; k++)
{
_setRowid(offset + k, val);
}
}
/**
* Get a {@code handle} value for the datum at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset from which to retrieve the value from the property array.
*
* @return The datum as a {@code handle}; will represent unknown value if the datum is
* {@code null}. The value will have the case-sensitivity inherent to the
* corresponding field.
*/
public final handle _getHandle(int offset)
{
checkIncomplete(offset);
Long datum = (Long) data[offset];
return datum != null ? handle.fromResourceId(datum, false) : new handle();
}
/**
* Set a {@code handle} value into the data at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset at which to store the given datum in the property array.
* @param w
* FWD wrapper type for the handle value. The case-sensitivity property of this
* wrapper instance is not preserved.
*/
protected final void _setHandle(int offset, handle w)
{
Long datum = null;
if (w != null && !w.isUnknown())
{
datum = w.getResourceId();
}
setDatum(offset, datum);
}
/**
* Bulk getter for a {@code handle} extent field. All components are returned as a single
* array of {@code handle}.
*
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*
* @return An array of {@code handle} representing the elements of an extent field.
*/
protected final handle[] _getHandle(int extent, int offset)
{
handle[] ret = new handle[extent];
for (int k = 0; k < extent; k++)
{
ret[k] = _getHandle(offset + k);
}
return ret;
}
/**
* Bulk setter for a {@code handle} extent field. Each components from {@code val}
* parameter will initialize their correspondent of the extent field in {@code data} array.
*
* @param val
* An array of {@code handle} values to be used for setting the extent field.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setHandle(handle[] val, int extent, int offset)
{
int len = Math.min(val.length, extent);
for (int k = 0; k < len; k++)
{
_setHandle(offset + k, val[k]);
}
}
/**
* Mass setter for a {@code handle} extent field. All components of the extent field will
* be set to {@code val} in {@code data} array.
*
* @param val
* A {@code handle} value to be used for setting the extent field components.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setHandle(handle val, int extent, int offset)
{
for (int k = 0; k < extent; k++)
{
_setHandle(offset + k, val);
}
}
/**
* Get a {@code recid} value for the datum at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset from which to retrieve the value from the property array.
*
* @return The datum as a {@code recid}; will represent unknown value if the datum is
* {@code null}. The value will have the case-sensitivity inherent to the
* corresponding field.
*/
public final recid _getRecid(int offset)
{
checkIncomplete(offset);
Integer datum = (Integer) data[offset];
return datum != null ? new recid(datum) : new recid();
}
/**
* Set a {@code recid} value into the data at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset at which to store the given datum in the property array.
* @param w
* FWD wrapper type for the {@code recid} value. The case-sensitivity property of this
* wrapper instance is not preserved.
*/
protected final void _setRecid(int offset, NumberType w)
{
Integer datum = null;
if (w != null && !w.isUnknown())
{
datum = w.intValue();
}
setDatum(offset, datum);
}
/**
* Bulk getter for a {@code recid} extent field. All components are returned as a single
* array of {@code recid}.
*
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*
* @return An array of {@code recid} representing the elements of an extent field.
*/
protected final recid[] _getRecid(int extent, int offset)
{
recid[] ret = new recid[extent];
for (int k = 0; k < extent; k++)
{
ret[k] = _getRecid(offset + k);
}
return ret;
}
/**
* Bulk setter for a {@code recid} extent field. Each components from {@code val}
* parameter will initialize their correspondent of the extent field in {@code data} array.
*
* @param val
* An array of {@code recid} values to be used for setting the extent field.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setRecid(recid[] val, int extent, int offset)
{
int len = Math.min(val.length, extent);
for (int k = 0; k < len; k++)
{
_setRecid(offset + k, val[k]);
}
}
/**
* Mass setter for a {@code recid} extent field. All components of the extent field will
* be set to {@code val} in {@code data} array.
*
* @param val
* A {@code recid} value to be used for setting the extent field components.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setRecid(NumberType val, int extent, int offset)
{
for (int k = 0; k < extent; k++)
{
_setRecid(offset + k, val);
}
}
/**
* Get an {@code blob} value for the datum at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset from which to retrieve the value from the property array.
*
* @return The datum as an {@code blob}; will represent unknown value if the datum is
* {@code null}.
*/
public final blob _getBlob(int offset)
{
checkIncomplete(offset);
byte[] datum = (byte[]) data[offset];
return datum != null ? new blob(datum) : new blob();
}
/**
* Bulk getter for a {@code blob} extent field. All components are returned as a single
* array of {@code blob}.
*
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*
* @return An array of {@code blob} representing the elements of an extent field.
*/
protected final blob[] _getBlob(int extent, int offset)
{
blob[] ret = new blob[extent];
for (int k = 0; k < extent; k++)
{
ret[k] = _getBlob(offset + k);
}
return ret;
}
/**
* Set an {@code blob} value into the data at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset at which to store the given datum in the property array.
* @param w
* FWD wrapper type for the value.
*/
protected final void _setBlob(int offset, blob w)
{
byte[] datum = null;
if (w != null && !w.isUnknown())
{
datum = w.getByteArray();
}
setDatum(offset, datum);
}
/**
* Bulk setter for a {@code blob} extent field. Each components from {@code val}
* parameter will initialize their correspondent of the extent field in {@code data} array.
*
* @param val
* An array of {@code blob} values to be used for setting the extent field.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setBlob(blob[] val, int extent, int offset)
{
int len = Math.min(val.length, extent);
for (int k = 0; k < len; k++)
{
_setBlob(offset + k, val[k]);
}
}
/**
* Mass setter for a {@code blob} extent field. All components of the extent field will
* be set to {@code val} in {@code data} array.
*
* @param val
* A {@code blob} value to be used for setting the extent field components.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setBlob(blob val, int extent, int offset)
{
for (int k = 0; k < extent; k++)
{
_setBlob(offset + k, val);
}
}
/**
* Get an {@code clob} value for the datum at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic, and the low-level
* data storage in the record.
* <p>
* Note: in order for this method to work, the {@code activeBuffer} on parent object must be set in
* advance. After the method was invoked, it is recommended to clear its value.
*
* @param offset
* Zero-based offset from which to retrieve the value from the property array.
*
* @return The datum as an {@code clob}; will represent unknown value if the datum is {@code null}.
*/
public final clob _getClob(int offset)
{
checkIncomplete(offset);
return new clob((String) data[offset], getCodePage(offset));
}
/**
* Bulk getter for a {@code clob} extent field. All components are returned as a single
* array of {@code clob}.
*
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*
* @return An array of {@code clob} representing the elements of an extent field.
*/
protected final clob[] _getClob(int extent, int offset)
{
clob[] ret = new clob[extent];
for (int k = 0; k < extent; k++)
{
ret[k] = _getClob(offset + k);
}
return ret;
}
/**
* Set an {@code clob} value into the data at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset at which to store the given datum in the property array.
* @param w
* FWD wrapper type for the value.
*/
protected final void _setClob(int offset, clob w)
{
String datum = null;
if (w != null && !w.isUnknown())
{
datum = w.toStringExport();
}
setDatum(offset, datum);
}
/**
* Bulk setter for a {@code clob} extent field. Each components from {@code val}
* parameter will initialize their correspondent of the extent field in {@code data} array.
*
* @param val
* An array of {@code clob} values to be used for setting the extent field.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setClob(clob[] val, int extent, int offset)
{
int len = Math.min(val.length, extent);
for (int k = 0; k < len; k++)
{
_setClob(offset + k, val[k]);
}
}
/**
* Mass setter for a {@code clob} extent field. All components of the extent field will
* be set to {@code val} in {@code data} array.
*
* @param val
* A {@code clob} value to be used for setting the extent field components.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setClob(clob val, int extent, int offset)
{
for (int k = 0; k < extent; k++)
{
_setClob(offset + k, val);
}
}
/**
* Get an {@code comhandle} value for the datum at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset from which to retrieve the value from the property array.
*
* @return The datum as an {@code comhandle}; will represent unknown value if the datum is
* {@code null}.
*/
public final comhandle _getComhandle(int offset)
{
checkIncomplete(offset);
String datum = (String) data[offset];
return datum != null ? comhandle.fromString(datum) : new comhandle();
}
/**
* Bulk getter for a {@code comhandle} extent field. All components are returned as a single
* array of {@code comhandle}.
*
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*
* @return An array of {@code comhandle} representing the elements of an extent field.
*/
protected final comhandle[] _getComhandle(int extent, int offset)
{
comhandle[] ret = new comhandle[extent];
for (int k = 0; k < extent; k++)
{
ret[k] = _getComhandle(offset + k);
}
return ret;
}
/**
* Set an {@code comhandle} value into the data at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset at which to store the given datum in the property array.
* @param w
* FWD wrapper type for the value.
*/
protected final void _setComhandle(int offset, comhandle w)
{
String datum = null;
if (w != null && !w.isUnknown())
{
datum = w.toStringExport();
}
setDatum(offset, datum);
}
/**
* Bulk setter for a {@code comhandle} extent field. Each components from {@code val}
* parameter will initialize their correspondent of the extent field in {@code data} array.
*
* @param val
* An array of {@code comhandle} values to be used for setting the extent field.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setComhandle(comhandle[] val, int extent, int offset)
{
int len = Math.min(val.length, extent);
for (int k = 0; k < len; k++)
{
_setComhandle(offset + k, val[k]);
}
}
/**
* Mass setter for a {@code comhandle} extent field. All components of the extent field will
* be set to {@code val} in {@code data} array.
*
* @param val
* A {@code comhandle} value to be used for setting the extent field components.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setComhandle(comhandle val, int extent, int offset)
{
for (int k = 0; k < extent; k++)
{
_setComhandle(offset + k, val);
}
}
/**
* Get an {@code object} value for the datum at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset from which to retrieve the value from the property array.
*
* @return The datum as an {@code object}; will represent unknown value if the datum is
* {@code null}.
*/
public final object<?> _getObject(int offset)
{
checkIncomplete(offset);
Long datum = (Long) data[offset];
// the instance must be typed to Progress.Lang.Object
object<?> res = new object<>(BaseObject.class);
if (datum != null)
{
res.assign(object.fromResourceId(datum));
}
return res;
}
/**
* Bulk getter for a {@code object} extent field. All components are returned as a single
* array of {@code object}.
*
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*
* @return An array of {@code object} representing the elements of an extent field.
*/
protected final object<?>[] _getObject(int extent, int offset)
{
object<?>[] ret = new object[extent];
for (int k = 0; k < extent; k++)
{
ret[k] = _getObject(offset + k);
}
return ret;
}
/**
* Set an {@code object} value into the data at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset at which to store the given datum in the property array.
* @param w
* FWD wrapper type for the value.
*/
protected final void _setObject(int offset, object<?> w)
{
Long datum = null;
if (w != null && !w.isUnknown())
{
datum = object.resourceId(w);
}
object<?> oldValue = _getObject(offset);
setDatum(offset, datum);
updateObjectRefCount(oldValue, w);
}
/**
* Bulk setter for a {@code object} extent field. Each components from {@code val}
* parameter will initialize their correspondent of the extent field in {@code data} array.
*
* @param val
* An array of {@code object} values to be used for setting the extent field.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setObject(object<?>[] val, int extent, int offset)
{
int len = Math.min(val.length, extent);
for (int k = 0; k < len; k++)
{
_setObject(offset + k, val[k]);
}
}
/**
* Mass setter for a {@code object} extent field. All components of the extent field will
* be set to {@code val} in {@code data} array.
*
* @param val
* A {@code object} value to be used for setting the extent field components.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setObject(object<?> val, int extent, int offset)
{
for (int k = 0; k < extent; k++)
{
_setObject(offset + k, val);
}
}
/**
* Get an {@code raw} value for the datum at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset from which to retrieve the value from the property array.
*
* @return The datum as an {@code raw}; will represent unknown value if the datum is
* {@code null}.
*/
public final raw _getRaw(int offset)
{
checkIncomplete(offset);
byte[] datum = (byte[]) data[offset];
return datum != null ? new raw(datum) : raw.instantiateUnknownRaw();
}
/**
* Bulk getter for a {@code raw} extent field. All components are returned as a single
* array of {@code raw}.
*
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*
* @return An array of {@code raw} representing the elements of an extent field.
*/
protected final raw[] _getRaw(int extent, int offset)
{
raw[] ret = new raw[extent];
for (int k = 0; k < extent; k++)
{
ret[k] = _getRaw(offset + k);
}
return ret;
}
/**
* Set an {@code raw} value into the data at the given offset in the property array.
* <p>
* This method serves as a bridge between the upper level DMO API used by business logic,
* and the low-level data storage in the record.
*
* @param offset
* Zero-based offset at which to store the given datum in the property array.
* @param w
* FWD wrapper type for the value.
*/
protected final void _setRaw(int offset, BinaryData w)
{
byte[] datum = null;
if (w != null && !w.isUnknown())
{
datum = w.getByteArray();
}
setDatum(offset, datum);
}
/**
* Bulk setter for a {@code raw} extent field. Each components from {@code val}
* parameter will initialize their correspondent of the extent field in {@code data} array.
*
* @param val
* An array of {@code BinaryData} values to be used for setting the extent field.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setRaw(BinaryData[] val, int extent, int offset)
{
int len = Math.min(val.length, extent);
for (int k = 0; k < len; k++)
{
_setRaw(offset + k, val[k]);
}
}
/**
* Mass setter for a {@code raw} extent field. All components of the extent field will
* be set to {@code val} in {@code data} array.
*
* @param val
* A {@code BinaryData} value to be used for setting the extent field components.
* @param extent
* The size of the extent field (the number of elements).
* @param offset
* Zero-based offset of the first element of the extent field.
*/
protected final void _setRaw(BinaryData val, int extent, int offset)
{
for (int k = 0; k < extent; k++)
{
_setRaw(offset + k, val);
}
}
/**
* Update the object reference count. It will decrement the old value reference and increment the new
* value reference.
* <p>
* No-op by default.
*
* @param oldVal
* The old value.
* @param newVal
* The new value.
*/
protected void updateObjectRefCount(object<?> oldVal, object<?> newVal)
{
}
/**
* Check if the record is referenced by multiple buffers in the same time.
*
* @param includeTransient
* Include the transient references (used by presort queries).
*
* @return {@code true} if this record is loaded in more than one buffer.
*/
public boolean isMultiplyReferenced(boolean includeTransient)
{
long nonTransientRefs = (references & ((1L << 32) - 1));
long refs = includeTransient ? (references >>> 32) + nonTransientRefs : nonTransientRefs;
return refs > 1;
}
/**
* Increment the count of references to this object. While positive, this record must remain in the
* current session's cache.
*
* @param transient_
* {@code true} if this is a transient reference (usually from presort queries).
*
* @return The record's reference count <em>after</em> being incremented.
*/
final long incrementReferences(boolean transient_)
{
references = references + (transient_ ? (1L << 32) : 1);
return references;
}
/**
* Decrement the count of references to this object. While positive, this record must remain in the
* current session's cache.
*
* @param transient_
* {@code true} if this is a transient reference (usually from presort queries).
*
* @return The record's reference count <em>after</em> being decremented.
*/
final long decrementReferences(boolean transient_)
{
references = references - (transient_ ? (1L << 32) : 1);
return references;
}
/**
* Package private access to the subclass' record metadata for low level operations on this
* record.
*
* @return The metadata for the the concrete implementation of this class.
*/
final RecordMeta _getRecordMeta()
{
return _recordMeta();
}
/**
* Utility method used for debugging and logging.
*
* @param oneLine
* Simplify the result to make it fit on a single line. Some details are skipped and column names.
*
* @return the description of the object as configured by parameter(s).
*/
public String toString2(boolean oneLine)
{
StringBuilder sb = new StringBuilder();
RecordMeta meta = _recordMeta();
if (oneLine)
{
int last = data.length;
int start = (this instanceof TempTableRecord) ? 5 : 0;
sb.append(meta.legacyName).append('/').append(meta.tables[0]).append(':').append(id).append(" {");
for (int i = start; i < last; i++)
{
if (i > 0)
{
sb.append(", ");
}
boolean isChar = data[i] instanceof String;
if (isChar)
{
sb.append("\"");
}
boolean incomplete = readFields != null && !readFields.get(i);
sb.append(data[i] == null ? incomplete ? "n/a" : "?" : data[i]);
if (isChar)
{
sb.append("\"");
}
}
sb.append('}');
}
else
{
sb.append("Record(").append(meta.legacyName).append(":").append(id).append("){\n");
PropertyMeta[] pms = meta.getPropertyMeta(false);
for (int i = 0; i < pms.length; i++)
{
if (i != 0)
{
sb.append(",\n");
}
sb.append('\t').append(pms[i].getLegacyName()).append(": ");
int extent = pms[i].getExtent();
if (extent == 0)
{
sb.append(data[i]);
}
else
{
for (int k = 0; k < extent; k++)
{
sb.append(k == 0 ? "[" : "");
sb.append(data[i + k]);
}
i += extent - 1;
sb.append("]");
}
}
sb.append("\n}");
}
return sb.toString();
}
/**
* Given a map which contains legacy property names to their values, populate this record by deserializing
* the expected value using the given function.
*
* @param properties
* The properties for this record, which may be serialized as a Java type or as a String (in case
* of extents).
* @param f
* The function used to deserialize the values. The first argument is the {@link PropertyMeta},
* the second argument is the property value from the <code>properties</code> map and the returned
* value is the deserialized value of this property, compatible with
* {@link PropertyMeta#getDataHandler()}.
*/
public void setPropertyValues(Map<String, Object> properties, BiFunction<PropertyMeta, Object, Object> f)
{
RecordMeta recMeta = _recordMeta();
PropertyMeta[] props = recMeta.getPropertyMeta(false);
for (int i = 0; i < props.length; i++)
{
PropertyMeta prop = props[i];
Object val = properties.get(prop.getLegacyName());
val = f.apply(prop, val);
if (prop.getExtent() > 0)
{
for (int j = 0; j < prop.getExtent(); j++, i++)
{
prop = props[i];
BaseDataType fval = (BaseDataType) Array.get(val, j);
prop.getDataHandler().setField(data, fval, i, prop);
}
}
else
{
prop.getDataHandler().setField(data, (BaseDataType) val, i, prop);
}
}
}
/**
* Get all the properties from this record, in a map using as keys their legacy name.
* <p>
* Any extent properties will be contained as a Java array.
*
* @return See above.
*/
@Override
public Map<String, Object> getPropertyValues()
{
Map<String, Object> res = new HashMap<>();
RecordMeta recMeta = _recordMeta();
PropertyMeta[] props = recMeta.getPropertyMeta(false);
for (int i = 0; i < props.length; i++)
{
PropertyMeta prop = props[i];
int extent = prop.getExtent();
String legacyName = prop.getLegacyName();
if (extent > 0)
{
Object arr = null;
for (int j = 0; j < extent; j++, i++)
{
prop = props[i];
BaseDataType val = prop.getDataHandler().getField(this, i);
if (arr == null)
{
arr = Array.newInstance(val.getClass(), extent);
}
Array.set(arr, j, val);
}
res.put(legacyName, arr);
i--; // 'i' was already incremented in the main loop once
}
else
{
BaseDataType val = prop.getDataHandler().getField(this, i);
if (!res.containsKey(legacyName))
{
res.put(legacyName, val);
}
else
{
// denormalized extent fields?
res.put(prop.getName(), val);
}
}
}
return res;
}
/**
* Check if two {@link Record} objects are equal from the point of view of the {@code data} array.
* <p>
* <strong>To be noted</strong>, that this method accesses the {@code data} arrays directly without using
* any getter. This provides faster access, however should be changed if not all fields are hydrated and
* access is mandatory from a getter.
*
* @param otherRecord
* The instance to compare against.
*
* @return True if the {@code data} arrays are equal, false - otherwise
*/
public boolean equalData(Record otherRecord)
{
return Arrays.equals(this.data, otherRecord.data);
}
/**
* Converts this {@code Record} into a POJO, if POJO class is available.
* To be implemented by DMO implementations.
*
* @return a POJO object with same data as current object.
*/
public abstract Object toPOJO();
/**
* Populates this object with data from a POJO. This is called by {@code fromPOJO()}.
* To be implemented by DMO implementations.
*
* @param pojo
* The source for data.
*/
protected abstract void populateFromPOJO(Object pojo);
/**
* Create a new DMO {@code Record} starting from a POJO. To work, the application must be converted with
* POJO generation enabled.
*
* @param pojo
* The source for the new DMO Object.
*
* @return The DMO object created using data from POJO.
*/
public static DataModelObject fromPOJO(Object pojo)
{
DmoMeta dmoMeta = DmoMetadataManager.byPojoClass(pojo.getClass());
if (dmoMeta == null)
{
return null;
}
Record dmo;
try
{
dmo = dmoMeta.getImplementationClass().getDeclaredConstructor().newInstance();
}
catch (InstantiationException | IllegalAccessException | IllegalArgumentException |
InvocationTargetException | NoSuchMethodException | SecurityException e)
{
return null;
}
dmo.populateFromPOJO(pojo);
return (DataModelObject) dmo;
}
/**
* Helper function to convert form a {@code java.sql.Date} to a {@code GregorianCalendar} instance.
*
* @param date
* The {@code date} to be converted.
*
* @return The converted date as a {@code GregorianCalendar} instance.
*/
protected static GregorianCalendar toPojo(Date date)
{
if (date == null)
{
return null;
}
GregorianCalendar gc = new GregorianCalendar();
gc.setTime(date);
return gc;
}
/**
* Helper function to convert form a {@code java.sql.Timestamp} to a {@code GregorianCalendar} instance.
*
* @param datetime
* The {@code datetime} instant to be converted.
*
* @return The converted date as a {@code GregorianCalendar} instance.
*/
protected static GregorianCalendar toPojo(Timestamp datetime)
{
if (datetime == null)
{
return null;
}
GregorianCalendar gc = new GregorianCalendar();
gc.setTimeInMillis(datetime.getTime());
return gc;
}
/**
* Helper function to convert form a {@code java.time.OffsetDateTime} to a {@code GregorianCalendar}
* instance.
*
* @param datetimetz
* The {@code datetimetz} instant to be converted.
*
* @return The converted date as a {@code GregorianCalendar} instance.
*/
protected static GregorianCalendar toPojo(OffsetDateTime datetimetz)
{
if (datetimetz == null)
{
return null;
}
return GregorianCalendar.from(datetimetz.toZonedDateTime());
}
/**
* Helper function to convert form a {@code GregorianCalendar} to a {@code java.sql.Date} instance.
*
* @param date
* The {@code GregorianCalendar} to be converted.
*
* @return The converted date as a {@code Date} instance.
*/
protected static Date toDmoDate(GregorianCalendar date)
{
if (date == null)
{
return null;
}
return new Date(date.getTime().getTime());
}
/**
* Helper function to convert form a {@code GregorianCalendar} to a {@code java.sql.Timestamp} instance.
*
* @param datetime
* The {@code GregorianCalendar} instant to be converted.
*
* @return The converted date as a {@code datetime} instance.
*/
protected static Timestamp toDmoDatetime(GregorianCalendar datetime)
{
if (datetime == null)
{
return null;
}
return new Timestamp(datetime.getTimeInMillis());
}
/**
* Helper function to convert form a {@code GregorianCalendar} to a {@code java.time.OffsetDateTime}
* instance.
*
* @param datetimetz
* The {@code GregorianCalendar} instant to be converted.
*
* @return The converted date as a {@code datetimetz} instance.
*/
protected static OffsetDateTime toDmoDatetimetz(GregorianCalendar datetimetz)
{
if (datetimetz == null)
{
return null;
}
return datetimetz.toZonedDateTime().toOffsetDateTime();
}
}