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