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