# TempRecord.java

    /*
    ** Module   : TempRecord.java
    ** Abstract : The abstract base class for all business data model object classes which represent temp-tables.
    **
    ** Copyright (c) 2019-2024, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
    ** 001 ECF 20191001 Created initial version with basic data and accessors.
    **     OM  20200110 Added constants and method implementations.
    **     CA  20200714 Refactored to keep the reserved properties in the record's data, to be persisted in the
    **                  table.
    ** 002 OM  20201218 Fixed implementation for error and rejected attributes/hidden fields.
    **     OM  20211020 Added _DATASOURCE_ROWID property.
    **     CA  20220110 Refactored the temp-table's row-meta state to use constants for the indexes in the array
    **                  used for serialization.
    ** 003 OM  20230228 The updateObjectRefCount() must use the same key as the delete (getAnnotatedInterface()).
    ** 004 CA  20240918 Removed default methods from interfaces and moved them as concrete implementations, as
    **                  default methods cause the Java metaspace to spike one order of magnitude.
    ** 005 AS  20241009 Refactored activeOffset into activeOffsets(Bitset).
    */

    /*
    ** 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 com.goldencode.p2j.persist.orm.*;
    import com.goldencode.p2j.util.object;

    import java.util.BitSet;

    /**
     * The abstract base class for all business data model object classes which represent temp-tables.
     * Subclasses are intended to be generated at runtime, rather than coded by hand.
     */
    public abstract class TempRecord
    extends Record
    implements TempTableRecord
    {
       /** The property/column name of "__error-flag__" field. */
       public static final String _ERROR_FLAG = "_errorFlag";

       /** The property/column name of "__origin-rowid__" field. */
       public static final String _ORIGIN_ROWID = "_originRowid";

       /** The property/column name of data-source field. */
       public static final String _DATASOURCE_ROWID = "_datasourceRowid";

       /** The property/column name of "__error-string__" field. */
       public static final String _ERROR_STRING = "_errorString";

       /** The property/column name of "__after-rowid__" field. */
       public static final String _PEER_ROWID = "_peerRowid";

       /** The property/column name of "__row-state__" field. */
       public static final String _ROW_STATE = "_rowState";

       /**
        * The index in the {@link BaseRecord#data} and {@link TempRecord#toArray()} for the {@link #_ERROR_FLAG}
        * field.
        */
       public static final int _ERROR_FLAG_DATA_INDEX = 0;

       /**
        * The index in the {@link BaseRecord#data} and {@link TempRecord#toArray()} for the {@link #_ORIGIN_ROWID}
        * field.
        */
       public static final int _ORIGIN_ROWID_DATA_INDEX = _ERROR_FLAG_DATA_INDEX + 1;

       /**
        * The index in the {@link BaseRecord#data} and {@link TempRecord#toArray()} for the
        * {@link #_DATASOURCE_ROWID} field.
        */
       public static final int _DATASOURCE_ROWID_INDEX = _ORIGIN_ROWID_DATA_INDEX + 1;

       /**
        * The index in the {@link BaseRecord#data} and {@link TempRecord#toArray()} for the {@link #_ERROR_STRING}
        * field.
        */
       public static final int _ERROR_STRING_DATA_INDEX = _DATASOURCE_ROWID_INDEX + 1;

       /**
        * The index in the {@link BaseRecord#data} and {@link TempRecord#toArray()} for the {@link #_PEER_ROWID}
        * field.
        */
       public static final int _PEER_ROWID_DATA_INDEX = _ERROR_STRING_DATA_INDEX + 1;

       /**
        * The index in the {@link BaseRecord#data} and {@link TempRecord#toArray()} for the {@link #_ROW_STATE}
        * field.
        */
       public static final int _ROW_STATE_DATA_INDEX = _PEER_ROWID_DATA_INDEX + 1;

       /** The index in the {@link TempRecord#toArray()} for the {@link BaseRecord#id} field; always last. */
       public static final int _PK_DATA_INDEX = _ROW_STATE_DATA_INDEX + 1;

       /**
        * The number of meta properties, used when transferring meta properties via {@link TempRecord#toArray()}.
        */
       public static final int _META_PROPERTIES_LENGTH = _PK_DATA_INDEX + 1;

       /**
        * Multiplex ID. It must be {@code protected} or else an {@code IllegalAccessError} is thrown
        * when Impl constructor is called.
        * TODO: this does not seem normal, the constructor does not access the field from super class,
        *       it merely calls its super, see {@code DmoClass.createConstructor} :(.
        */
       protected Integer _multiplex = null;

       /** The default constructor is protected. */
       protected TempRecord()
       {
          super();
       }

       /**
        * Get an array with this record's meta information.
        *
        * @param    ttr
        *           The temp-table record.
        *
        * @return   See above.
        */
       public static Object[] asArray(TempTableRecord ttr)
       {
          // this order is hard-linked with the constants defined in TempRecord.
          Object[] res = new Object[TempRecord._META_PROPERTIES_LENGTH];
          res[TempRecord._PK_DATA_INDEX]            = ttr.primaryKey();
          res[TempRecord._ROW_STATE_DATA_INDEX]     = ttr._rowState();
          res[TempRecord._PEER_ROWID_DATA_INDEX]    = ttr._peerRowid();
          res[TempRecord._ORIGIN_ROWID_DATA_INDEX]  = ttr._originRowid();
          res[TempRecord._DATASOURCE_ROWID_INDEX]   = ttr._datasourceRowid();
          res[TempRecord._ERROR_FLAG_DATA_INDEX]    = ttr._errorFlags();
          res[TempRecord._ERROR_STRING_DATA_INDEX]  = ttr._errorString();

          return res;
       }

       /**
        * Gets the rowid (as {@code Long} value) of the peer record. Exclusively, for a BEFORE-TABLE
        * record this is the {@code after-rowid} and {@code before-rowid} for AFTER-TABLE record.
        *
        * @return  The rowid value of the peer record or {@code null} if there is none.
        *
        * @see     #_PEER_ROWID
        */
       @Override
       public final Long _peerRowid()
       {
          return (Long) data[_PEER_ROWID_DATA_INDEX];
       }

       /**
        * Sets the rowid (as {@code Long} value) of the peer record. Exclusively, for a BEFORE-TABLE
        * record this is the {@code after-rowid} and {@code before-rowid} for AFTER-TABLE record.
        *
        * @param   rowid
        *          The new long rowid value of the peer record or {@code null}.
        *
        * @see     #_PEER_ROWID
        */
       @Override
       public final void _peerRowid(Long rowid)
       {
          BitSet savedActiveOffset = (BitSet) this.activeOffsets.clone();
          try
          {
             setDatum(_PEER_ROWID_DATA_INDEX, rowid);
          }
          finally
          {
             // restore the activeOffset
             this.activeOffsets = savedActiveOffset;
          }
       }

       /**
        * Gets the current value of the {@code ROW-STATE} property for this record. Valid values
        * are defined as constants in {@link Buffer} interface. Additionally, {@code null} value is
        * returned if property is not available.
        *
        * @return  The current value of the {@code ROW-STATUS} property for this record as
        *          explained above.
        *
        * @see     #_ROW_STATE
        */
       @Override
       public final Integer _rowState()
       {
          return (Integer) data[_ROW_STATE_DATA_INDEX];
       }

       /**
        * Sets the value of the {@code ROW-STATUS} property for this record. Valid values are defined
        * as constants in {@link Buffer} interface. Additionally {@code null} value is allowed if
        * property is not available.
        *
        * @param   state
        *          The new value of the {@code ROW-STATE} property for this record as explained above.
        *
        * @see     #_ROW_STATE
        */
       @Override
       public final void _rowState(Integer state)
       {
          BitSet savedActiveOffset = (BitSet) this.activeOffsets.clone();
          try
          {
             setDatum(_ROW_STATE_DATA_INDEX, state);
          }
          finally
          {
             // restore the activeOffset
             this.activeOffsets = savedActiveOffset;
          }
       }

       /**
        * Gets the current value of the {@code ORIGIN-ROWID} property for this record. This property
        * of a record in a buffer of a CHANGE DATASET points to original record of the change.
        * Additionally, {@code null} value is returned if property is not available.
        *
        * @return  The current value of the {@code ORIGIN-ROWID} property for this record as
        *          explained above.
        *
        * @see     #_ORIGIN_ROWID
        */
       @Override
       public final Long _originRowid()
       {
          return (Long) data[_ORIGIN_ROWID_DATA_INDEX];
       }

       /**
        * Sets the current value of the {@code ORIGIN-ROWID} property for this record. This property
        * of a record in a buffer of a CHANGE DATASET points to original record of the change.
        * Additionally, {@code null} value is returned if property is not available.
        *
        * @param   rowid
        *          The new value of the {@code ORIGIN-ROWID} property for this record as explained above.
        *
        * @see     #_ORIGIN_ROWID
        */
       @Override
       public final void _originRowid(Long rowid)
       {
          BitSet savedActiveOffset = (BitSet) this.activeOffsets.clone();
          try
          {
             setDatum(_ORIGIN_ROWID_DATA_INDEX, rowid);
          }
          finally
          {
             // restore the activeOffset
             this.activeOffsets = savedActiveOffset;
          }
       }

       /**
        * Gets the current value of the {@code DATA_SOURCE_ROWID} property for this record. This property of a
        * record in a buffer of a CHANGE DATASET points to original record of the change. Additionally,
        * {@code null} value is returned if property is not available.
        *
        * @return  The current value of the {@code DATA_SOURCE_ROWID} property for this record as explained above.
        *
        * @see     #_DATASOURCE_ROWID
        */
       @Override
       public final Long _datasourceRowid()
       {
          return (Long) data[_DATASOURCE_ROWID_INDEX];
       }

       /**
        * Sets the current value of the {@code DATA_SOURCE_ROWID} property for this record. This property of a
        * record in a buffer of a CHANGE DATASET points to original record of the change. Additionally,
        * {@code null} value is returned if property is not available.
        *
        * @param   rowid
        *          The new value of the {@code DATA_SOURCE_ROWID} property for this record as explained above.
        *
        * @see     #_DATASOURCE_ROWID
        */
       @Override
       public final void _datasourceRowid(Long rowid)
       {
          BitSet savedActiveOffset = (BitSet) this.activeOffsets.clone();
          try
          {
             setDatum(_DATASOURCE_ROWID_INDEX, rowid);
          }
          finally
          {
             // restore the activeOffset
             this.activeOffsets = savedActiveOffset;
          }
       }

       /**
        * Gets the current value of the {@code __error-flag__} for this record.
        *
        * @return  The current value of the {@code __error-flag__} for this record.
        *
        * @see     #_ERROR_FLAG
        */
       @Override
       public final Integer _errorFlags()
       {
          return (Integer) data[_ERROR_FLAG_DATA_INDEX];
       }

       /**
        * Sets the current value of the {@code __error-flag__} for this record.
        *
        * @param   flag
        *          The current value of the {@code __error-flag__} for this record.
        *
        * @see     #_ERROR_FLAG
        */
       @Override
       public final void _errorFlags(Integer flag)
       {
          BitSet savedActiveOffset = (BitSet) this.activeOffsets.clone();
          try
          {
             setDatum(_ERROR_FLAG_DATA_INDEX, flag);
          }
          finally
          {
             // restore the activeOffset
             this.activeOffsets = savedActiveOffset;
          }
       }

       /**
        * Gets the current value of the {@code __error-string__} for this record.
        *
        * @return  The current value of the {@code __error-string__} for this record.
        *
        * @see     #_ERROR_STRING
        */
       @Override
       public final String _errorString()
       {
          return (String) data[_ERROR_STRING_DATA_INDEX];
       }

       /**
        * Sets the current value of the {@code __error-string__} for this record.
        *
        * @param   error
        *          The current value of the {@code __error-string__} for this record.
        *
        * @see     #_ERROR_STRING
        */
       @Override
       public final void _errorString(String error)
       {
          BitSet savedActiveOffset = (BitSet) this.activeOffsets.clone();
          try
          {
             setDatum(_ERROR_STRING_DATA_INDEX, error);
          }
          finally
          {
             // restore the activeOffset
             this.activeOffsets = savedActiveOffset;
          }
       }

       /**
        * Creates a snapshot copy of the current record. Since this is a {@code TempRecord}, the
        * returned object is also a {@code TempRecord}.
        *
        * @return  a new {@code TempRecord} instance of this record with all fields set to values of
        *          the respective fields in this {@code TempRecord} at the moment of the call.
        */
       @Override
       public TempRecord snapshot()
       {
          return (TempRecord) super.snapshot();
       }

       /**
        * Makes this a copy of a source {@code BaseRecord} object. For _temp databases, the
        * {@code _multiplex} identifier, internal {@code data} and the {@code id} of the
        * {@code BaseRecord} are copied.
        * <p>
        * TODO: check whether {@code _rowState}, {@code _peerRowid}, {@code _originRowid},
        *       {@code _errorFlag} and  {@code _errorString} should also be copied.
        *
        * @param   from
        *          The source to copy data from.
        */
       @Override
       public void copy(BaseRecord from)
       {
          super.copy(from);
          this._multiplex = ((TempRecord) from)._multiplex;
       }

       /**
        * Obtain the {@code _multiplex} identifier of this record.
        *
        * @return  the {@code _multiplex} identifier of this record.
        */
       public final Integer _multiplex()
       {
          return _multiplex;
       }

       /**
        * Sets the {@code _multiplex} identifier for this record.
        *
        * @param   newMultiplex
        *          The new {@code _multiplex} identifier of this record.
        */
       public final void _multiplex(int newMultiplex)
       {
          _multiplex = newMultiplex;
       }

       /**
        * Return a string representation of this object, primarily for debug purposes.
        *
        * @return  String representation of this object.
        */
       @Override
       public String toString()
       {
          return super.toString() + System.lineSeparator() + "multiplex: " + _multiplex;
       }

       /**
        * Get an array with this record's meta information.
        *
        * @return   See above.
        */
       @Override
       public Object[] toArray()
       {
          return asArray(this);
       }

       /**
        * Update the object reference count.  It will decrement the old value reference and increment the new
        * value reference.
        *
        * @param    oldVal
        *           The old value.
        * @param    newVal
        *           The new value.
        */
       @Override
       protected final void updateObjectRefCount(object<?> oldVal, object<?> newVal)
       {
          Class<?> implIFace = _recordMeta().getDmoMeta().getAnnotatedInterface();
          TemporaryBuffer.updateObjectRefCount(implIFace, oldVal, newVal);
       }
    }