# ReservedProperty.java

    /*
    ** Module   : ReservedProperty.java
    ** Abstract : Pseudo-annotations for special temp-table fields in order to handle all DMO properties in a
    **            unified way.
    **
    ** Copyright (c) 2020-2022, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
    ** 001 OM  20200110 Added constants and method implementations.
    ** 002 CA  20200922 Added getByName(), to resolve the reserved property for a 'hidden field' in 4GL.
    **     OM  20201001 Improved DMO manipulation performance by caching slow Property annotation access.
    **     OM  20201218 Fixed implementation for error and rejected attributes/hidden fields.
    **     CA  20210831 Track the getter method for TempRecord reserved properties.
    **     OM  20211020 Added _DATASOURCE_ROWID property.
    **     OM  20220212 Added isReservedProperty static utility method for testing reserved properties names.
    **     OM  20220713 Decreased constructor's visibility to private.
    */

    /*
    ** 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.orm;

    import com.goldencode.p2j.persist.*;
    import com.goldencode.p2j.util.*;
    import java.lang.reflect.*;
    import java.util.*;

    /**
     * These are reserved {@code Property}/es which may take part of a SQL record of a temporary table:
     * <ul>
     *    <li>{@code id} - the record identifier. FWD-specific;</li>
     *    <li>{@code _multiplex} - the temp-table multiplexer. FWD-specific, allows multiple users
     *          to store same-structure records in a single TEMP-TABLE;</li>
     *    <li>{@code parent__id} - the foreign-key for composite table to its parent;</li>
     *    <li>{@code list__index} - extent index for composite table;</li>
     *
     *    <li>{@code _errorFlag}, {@code _originRowid}, {@code _dataSourceRowid}, {@code _errorString},
     *        {@code _peerRowid}, and {@code _rowState} - the before-table specific hidden fields.</li>
     * </ul>
     */
    public class ReservedProperty
    extends Property
    {
       /** The id of the {@code parent__id} reserved property. */
       public static final int ID_PARENT__ID = -2;

       /** The id of the {@code list__index} reserved property. */
       public static final int ID_LIST__INDEX = -3;

       /** The id of the {@code _row_state} reserved property. */
       public static final int ID_ROW_STATE = -4;

       /** The id of the {@code _peer_rowid} reserved property. */
       public static final int ID_PEER_ROWID = -5;

       /** The id of the {@code _error_string} reserved property. */
       public static final int ID_ERROR_STRING = -6;

       /** The id of the {@code _datasource-rowid} reserved property. */
       public static final int ID_DATASOURCE_ROWID = -7;

       /** The id of the {@code _origin_rowid} reserved property. */
       public static final int ID_ORIGIN_ROWID = -8;

       /** The id of the {@code _error_flag} reserved property. */
       public static final int ID_ERROR_FLAG = -9;

       /** The id of the {@code _multiplex} reserved property. */
       public static final int ID_MULTIPLEX = -10;

       /** The id of the {@code primary_key} reserved property. */
       public static final int ID_PRIMARY_KEY = -11;

       /** The {@code id} reserved (by FWD) property. */
       public static final ReservedProperty ID = new ReservedProperty(
             ID_PRIMARY_KEY, Session.PK, null, Session.PK, true, Long.class, false);

       /** The {@code _multiplex} reserved (by FWD) property. */
       public static final ReservedProperty _MULTIPLEX = new ReservedProperty(
             ID_MULTIPLEX, TemporaryBuffer.MULTIPLEX_FIELD_NAME, null, TemporaryBuffer.MULTIPLEX_FIELD_NAME, true, Integer.class, false);

       /** The {@code __error-flag__} reserved (by P4GL) property. */
       public static final ReservedProperty _ERRORFLAG = new ReservedProperty(
             ID_ERROR_FLAG, TempRecord._ERROR_FLAG, Buffer.__ERROR_FLAG__, TempRecord._ERROR_FLAG, false, integer.class, true);

       /** The {@code __origin-rowid__} reserved (by P4GL) property. */
       public static final ReservedProperty _ORIGINROWID = new ReservedProperty(
             ID_ORIGIN_ROWID, TempRecord._ORIGIN_ROWID, Buffer.__ORIGIN_ROWID__, TempRecord._ORIGIN_ROWID, false, rowid.class, true);

       /** The {@code __datasource-rowid__} reserved (by P4GL) property. */
       public static final ReservedProperty _DATASOURCEROWID = new ReservedProperty(
             ID_DATASOURCE_ROWID, TempRecord._DATASOURCE_ROWID, Buffer.__DATA_SOURCE_ROWID__, TempRecord._DATASOURCE_ROWID, false, rowid.class, true);

       /** The {@code _errorString} reserved (by P4GL) property. */
       public static final ReservedProperty _ERRORSTRING = new ReservedProperty(
             ID_ERROR_STRING, TempRecord._ERROR_STRING, Buffer.__ERROR_STRING__, TempRecord._ERROR_STRING, false, character.class, true);

       /** The {@code __after-rowid__} reserved (by P4GL) property. */
       public static final ReservedProperty _PEERROWID = new ReservedProperty(
             ID_PEER_ROWID, TempRecord._PEER_ROWID, Buffer.__AFTER_ROWID__, TempRecord._PEER_ROWID, false, rowid.class, true);

       /** The {@code __row-state__} reserved (by P4GL) property. */
       public static final ReservedProperty _ROWSTATE = new ReservedProperty(
             ID_ROW_STATE, TempRecord._ROW_STATE, Buffer.__ROW_STATE__, TempRecord._ROW_STATE, false, integer.class, true);

       /** The {@code list__index} reserved (by FWD) property. */
       public static final ReservedProperty LIST__INDEX = new ReservedProperty(
             ID_LIST__INDEX, "list__index", "index", "list__index", true, Integer.class, false);  // or integer ?

       /** The {@code list__index} reserved (by FWD) property. */
       public static final ReservedProperty PARENT__ID = new ReservedProperty(
             ID_PARENT__ID, "parent__id", "fk_parent", "parent__id", true, Long.class, false);    // or int64 ?

       /** The map of reserved properties which exist in 4GL, but are hidden. */
       private static final Map<String, ReservedProperty> HIDDEN_PROPERTIES = new HashMap<>();

       static
       {
          HIDDEN_PROPERTIES.put(_ERRORFLAG.legacy, _ERRORFLAG);
          HIDDEN_PROPERTIES.put(_ORIGINROWID.legacy, _ORIGINROWID);
          HIDDEN_PROPERTIES.put(_DATASOURCEROWID.legacy, _DATASOURCEROWID);
          HIDDEN_PROPERTIES.put(_ERRORSTRING.legacy, _ERRORSTRING);
          HIDDEN_PROPERTIES.put(_PEERROWID.legacy, _PEERROWID);
          HIDDEN_PROPERTIES.put(_ROWSTATE.legacy, _ROWSTATE);
       }

       /**
        * Creates a new {@code ReservedProperty} object
        *
        * @param   id
        *          The id of this field in the sequence of the fields of the buffer.
        * @param   name
        *          The name of the DMO property.
        * @param   legacy
        *          The name of the legacy field associated with the DMO property.
        * @param   column
        *          The name of the column in the SQL database, associated with the DMO property.
        * @param   mandatory
        *          Flag for not-null fields.
        * @param   type
        *          property type
        * @param   initialNull
        *          The property has {@code null} as default value.
        */
       private ReservedProperty(int id,
                               String name,
                               String legacy,
                               String column,
                               boolean mandatory,
                               Class<?> type,
                               boolean initialNull)
       {
          super(id, name, legacy, column, mandatory, type, initialNull, getMethod(id));
       }

       /**
        * Get the specified hidden property.
        *
        * @param    name
        *           The name of the reserved property.
        *
        * @return   The {@link ReservedProperty} instance for it.
        */
       public static ReservedProperty getByName(String name)
       {
          return HIDDEN_PROPERTIES.get(name.toLowerCase());
       }

       /**
        * Check whether the {@code name} argument is the name of a reserved property.
        *
        * @param   name
        *          The name to test
        *
        * @return  {@code true} if {@code name} is the name of a reserved property.
        */
       public static boolean isReservedProperty(String name)
       {
          if (!name.startsWith("__"))
          {
             // the reserved property names always start with double underscore. If not, quick out.
             return false;
          }

          switch (name)
          {
             case Buffer.__AFTER_ROWID__:
             case Buffer.__DATA_SOURCE_ROWID__:
             case Buffer.__ERROR_FLAG__:
             case Buffer.__ERROR_STRING__:
             case Buffer.__ORIGIN_ROWID__:
             case Buffer.__ROW_STATE__:
                return true;
          }

          return false;
       }

       /**
        * Get the method for the specified reserved property.
        *
        * @param    id
        *           The reserved property ID.
        *
        * @return   The method, or <code>null</code> if this is not one of the {@link TempRecord} methods.
        *
        * @throws   RuntimeException
        *           If the method can't be found, as this is a FWD error.
        */
       private static Method getMethod(int id)
       {
          try
          {
             switch (id)
             {
                case ID_ROW_STATE:
                   return TempRecord.class.getDeclaredMethod("_rowState");
                case ID_PEER_ROWID:
                   return TempRecord.class.getDeclaredMethod("_peerRowid");
                case ID_ERROR_STRING:
                   return TempRecord.class.getDeclaredMethod("_errorString");
                case ID_ORIGIN_ROWID:
                   return TempRecord.class.getDeclaredMethod("_originRowid");
                case ID_DATASOURCE_ROWID:
                   return TempRecord.class.getDeclaredMethod("_datasourceRowid");
                case ID_ERROR_FLAG:
                   return TempRecord.class.getDeclaredMethod("_errorFlags");
                default:
                   return null;
             }
          }
          catch (NoSuchMethodException exc)
          {
             throw new RuntimeException(exc);
          }
       }
    }