# ObjectVar.java

    /*
    ** Module   : ObjectVar.java
    ** Abstract : wrapper class for all OO 4GL variable definitions.
    **
    ** Copyright (c) 2019-2023, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
    ** 001 CA  20190122 First version.
    ** 002 HC  20190206 Added default ctor. It is needed internally during construction of default
    **                  instances and prevents unexpected runtime errors.
    ** 003 CA  20190719 Plain object instance does not do reference counting.
    ** 004 CA  20191116 Fixed a regression from moving object.assign(BDT) logic to object.assign(object).
    ** 005 CA  20210609 Reworked INPUT/INPUT-OUTPUT parameters to a new approach, where they are explicitly
    **                  initialized at the method's execution, and not at the caller's arguments.
    ** 006 AL2 20220328 Added proxy checks for BDT.
    ** 007 CA  20230110 Cache the helper for ProcedureManager, ObjectOps and others (as needed), to reduce
    **                  context-local lookup.
    ** 008 CA  20230929 Javadoc fixes.
    ** 009 CA  20231031 Check if type for the current instance is set, in 'assign'.
    */

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

    import com.goldencode.p2j.oo.lang.*;

    /**
     * Wrapper class for all OO 4GL variable definitions.  This is needed for compatibility with
     * unknown value, assignment, parameter passing, DELETE, SESSION:FIRST-OBJECT/LAST-OBJECT and
     * UNDO.
     * <p>
     * This implementation takes care of counting the references of assigned legacy 4GL class instances.
     * <p>
     * Object variables are instantiated as {@link ObjectVar} (by the {@link TypeFactory} or
     * {@link ArrayAssigner}); this is required to enable the reference counting ONLY for the instances
     * which were defined as variables in the legacy code.  Also, they are registered at the defining
     * top-level block via {@link ObjectOps#registerPending} when they are defined via {@link TypeFactory}, so
     * that when they get out of scope, their referenced object can update the counter.
     * <p>
     * This type can't be sub-classed, as Hibernate types are dependent on it.
     */
    public class ObjectVar<T extends _BaseObject_>
    extends object<T>
    {
       /**
        * Default ctor. Internally used during construction of default instances.
        */
       public ObjectVar()
       {
       }

       /**
        * Initialize this object reference, allowing only assignment to objects compatible with the
        * specified type.
        *
        * @param    cls
        *           The type of references this object can hold.
        */
       public ObjectVar(Class<T> cls)
       {
          super(cls);
       }

       /**
        * Create a copy of the specified object variable.
        * <p>
        * Reference counting will be performed.
        *
        * @param    var
        *           The variable to copy.
        */
       public ObjectVar(ObjectVar var)
       {
          super(var.type());

          assign(var.val());
       }

       /**
        * Sets the state (data and unknown value) of this instance based on the state of the passed
        * instance.
        * <p>
        * Reference management is performed for the old and new values.
        *
        * @param    value
        *           The instance from which to copy state.
        */
       public void assign(object value)
       {
          // the value may be a proxy; make sure to unwrap and work with the real value
          if (BaseDataType.isProxy(value))
          {
             assign(value.val());
             return;
          }
          _BaseObject_ vref = value.ref;
          if (ref == vref)
          {
             // same reference or both unknown, don't do anything else
             return;
          }

          if (vref == null)
          {
             setUnknown();
             return;
          }

          Class<T> type = type();
          if (type != null && !type.isInstance(vref))
          {
             // must be a sub-class or the same as this one
             String lt1 = ObjectOps.getLegacyName(vref.getClass());
             String lt2 = ObjectOps.getLegacyName(type());
             ErrorManager.recordOrThrowError(13448,
                                             "A variable of class '" + lt1 + "' cannot be assigned " +
                                             "to a variable of class '" + lt2 + "'");

             // reference remains unchanged
             return;
          }

          // reference inc/dec must be called only for real variables or DMO fields! internal
          // usage must not affect the reference count
          if (!isUnknown())
          {
             decrement();
          }

          try
          {
             super.assign(value);
          }
          finally
          {
             if (!isUnknown())
             {
                increment();
             }
          }
       }

       /**
        * Sets the state of this instance's <code>unknown value</code> flag or state to
        * <code>true</code>.
        * <p>
        * <b>Warning: the data stored in this instance will be invalid after calling this method.</b>
        */
       public void setUnknown()
       {
          if (!isUnknown())
          {
             decrement();
          }

          super.setUnknown();
       }

       /**
        * Decrement the reference counter for this instance.
        */
       @Override
       protected void decrement()
       {
          helper.decrement(ref);
       }

       /**
        * Increment the reference counter for this instance.
        */
       @Override
       protected void increment()
       {
          helper.increment(ref);
       }
    }