# OutputParameter.java

    /*
    ** Module   : OutputParameter.java
    ** Abstract : Data wrapper which associates with a variable or a DMO property
    **
    ** Copyright (c) 2016-2024, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ---------------------------------Description---------------------------------
    ** 001 OM  20160114 Created initial version.
    ** 002 CA  20181229 Added wrap which forces the BDT type expected by the parameter definition.
    ** 003 CA  20190508 OutputParameter.wrap must be fully generic.
    **     CA  20190526 Fixed runtime and conversion support for legacy class properties used as
    **                  OUTPUT or INPUT-OUTPUT arguments.
    ** 004 CA  20190927 Added support for direct Java access from 4GL code.
    ** 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.
    **     CA  20210818 Fixed memptr parameter runtime - they share the 'pointer structure' with the passed
    **                  argument.
    ** 006 DDF 20240408 Pass the type of the parameter and use it to wrap it directly to avoid generating
    **                  a constructor for that type.
    */
    /*
    ** 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 java.util.function.*;

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

    /**
     * A utility class which associates a variable or DMO property with a data wrapper object.
     * Static methods of this class are invoked by business logic to manage the hidden assignment of
     * an INPUT-OUTPUT or an OUTPUT parameter to original variable or the database field with which
     * it is associated, upon return from a procedure or function.
     */
    public final class OutputParameter
    {
       /**
        * Creates wrapper for a legacy instance property and returns instance of T type. This form can
        * be used only with both OUTPUT and INPUT-OUTPUT parameters (depending on {@code io} parameter).
        *
        * @param   propType
        *          The legacy property's data type.
        * @param   instance
        *          The legacy object instance.
        * @param   property
        *          The property's name.
        * @param   io
        *          Specifies an INPUT-OUTPUT parameter if {@code true}. In this case, the local
        *          variable in the called function will be initialized with the current value of the
        *          original variable. Otherwise (simple OUTPUT), the parameter is initialized to
        *          default value for respective datatype (follows the semantics of OUTPUT parameter
        *          passing from 4GL).
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(Class<T>                       propType,
                                                     object<? extends _BaseObject_> instance,
                                                     String                         property,
                                                     boolean                        io)
       {
          return new PropertyReference(instance, property).wrap(io);
       }

       /**
        * Creates wrapper for a legacy instance property and returns instance of T type. This form can
        * be used only with OUTPUT parameters.
        *
        * @param   propType
        *          The legacy property's data type.
        * @param   instance
        *          The legacy object instance.
        * @param   property
        *          The property's name.
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(Class<T>                       propType,
                                                     object<? extends _BaseObject_> instance,
                                                     String                         property)
       {
          return new PropertyReference(instance, property).wrap(false);
       }

       /**
        * Creates wrapper for a legacy static property and returns instance of T type. This form can
        * be used only with both OUTPUT and INPUT-OUTPUT parameters (depending on {@code io} parameter).
        *
        * @param   propType
        *          The legacy property's data type.
        * @param   className
        *          The fully qualified legacy class name.
        * @param   property
        *          The property's name.
        * @param   io
        *          Specifies an INPUT-OUTPUT parameter if {@code true}. In this case, the local
        *          variable in the called function will be initialized with the current value of the
        *          original variable. Otherwise (simple OUTPUT), the parameter is initialized to
        *          default value for respective datatype (follows the semantics of OUTPUT parameter
        *          passing from 4GL).
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(Class<T> propType,
                                                     String   className,
                                                     String   property,
                                                     boolean  io)
       {
          return new PropertyReference(className, property).wrap(io);
       }

       /**
        * Creates wrapper for a legacy static property and returns instance of T type. This form can
        * be used only with OUTPUT parameters.
        *
        * @param   propType
        *          The legacy property's data type.
        * @param   className
        *          The fully qualified legacy class name.
        * @param   property
        *          The property's name.
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(Class<T> propType,
                                                     String   className,
                                                     String   property)
       {
          return new PropertyReference(className, property).wrap(false);
       }

       /**
        * Creates wrapper for a legacy instance property and returns instance of T type. This form can
        * be used only with both OUTPUT and INPUT-OUTPUT parameters (depending on {@code io} parameter).
        *
        * @param   propType
        *          The legacy property's data type.
        * @param   instance
        *          The legacy object instance.
        * @param   property
        *          The property's name.
        * @param   index
        *          The extent property's subscript.
        * @param   io
        *          Specifies an INPUT-OUTPUT parameter if {@code true}. In this case, the local
        *          variable in the called function will be initialized with the current value of the
        *          original variable. Otherwise (simple OUTPUT), the parameter is initialized to
        *          default value for respective datatype (follows the semantics of OUTPUT parameter
        *          passing from 4GL).
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(Class<T>                       propType,
                                                     object<? extends _BaseObject_> instance,
                                                     String                         property,
                                                     NumberType                     index,
                                                     boolean                        io)
       {
          return new PropertyReference(instance, property, index).wrap(io);
       }

       /**
        * Creates wrapper for a legacy instance property and returns instance of T type. This form can
        * be used only with OUTPUT parameters.
        *
        * @param   propType
        *          The legacy property's data type.
        * @param   instance
        *          The legacy object instance.
        * @param   property
        *          The property's name.
        * @param   index
        *          The extent property's subscript.
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(Class<T>                       propType,
                                                     object<? extends _BaseObject_> instance,
                                                     String                         property,
                                                     NumberType                     index)
       {
          return new PropertyReference(instance, property, index).wrap(false);
       }

       /**
        * Creates wrapper for a legacy static property and returns instance of T type. This form can
        * be used only with both OUTPUT and INPUT-OUTPUT parameters (depending on {@code io} parameter).
        *
        * @param   propType
        *          The legacy property's data type.
        * @param   className
        *          The fully qualified legacy class name.
        * @param   property
        *          The property's name.
        * @param   index
        *          The extent property's subscript.
        * @param   io
        *          Specifies an INPUT-OUTPUT parameter if {@code true}. In this case, the local
        *          variable in the called function will be initialized with the current value of the
        *          original variable. Otherwise (simple OUTPUT), the parameter is initialized to
        *          default value for respective datatype (follows the semantics of OUTPUT parameter
        *          passing from 4GL).
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(Class<T>   propType,
                                                     String     className,
                                                     String     property,
                                                     NumberType index,
                                                     boolean    io)
       {
          return new PropertyReference(className, property, index).wrap(io);
       }

       /**
        * Creates wrapper for a legacy static property and returns instance of T type. This form can
        * be used only with OUTPUT parameters.
        *
        * @param   propType
        *          The legacy property's data type.
        * @param   className
        *          The fully qualified legacy class name.
        * @param   property
        *          The property's name.
        * @param   index
        *          The extent property's subscript.
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(Class<T> propType,
                                                     String   className,
                                                     String   property,
                                                     NumberType index)
       {
          return new PropertyReference(className, property, index).wrap(false);
       }

       /**
        * Creates wrapper for a legacy instance property and returns instance of T type. This form can
        * be used only with both OUTPUT and INPUT-OUTPUT parameters (depending on {@code io} parameter).
        *
        * @param   propType
        *          The legacy property's data type.
        * @param   instance
        *          The legacy object instance.
        * @param   property
        *          The property's name.
        * @param   index
        *          The extent property's subscript.
        * @param   io
        *          Specifies an INPUT-OUTPUT parameter if {@code true}. In this case, the local
        *          variable in the called function will be initialized with the current value of the
        *          original variable. Otherwise (simple OUTPUT), the parameter is initialized to
        *          default value for respective datatype (follows the semantics of OUTPUT parameter
        *          passing from 4GL).
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(Class<T>                       propType,
                                                     object<? extends _BaseObject_> instance,
                                                     String                         property,
                                                     int                            index,
                                                     boolean                        io)
       {
          return new PropertyReference(instance, property, index).wrap(io);
       }

       /**
        * Creates wrapper for a legacy instance property and returns instance of T type. This form can
        * be used only with OUTPUT parameters.
        *
        * @param   propType
        *          The legacy property's data type.
        * @param   instance
        *          The legacy object instance.
        * @param   property
        *          The property's name.
        * @param   index
        *          The extent property's subscript.
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(Class<T>                       propType,
                                                     object<? extends _BaseObject_> instance,
                                                     String                         property,
                                                     int                            index)
       {
          return new PropertyReference(instance, property, index).wrap(false);
       }

       /**
        * Creates wrapper for a legacy static property and returns instance of T type. This form can
        * be used only with both OUTPUT and INPUT-OUTPUT parameters (depending on {@code io} parameter).
        *
        * @param   propType
        *          The legacy property's data type.
        * @param   className
        *          The fully qualified legacy class name.
        * @param   property
        *          The property's name.
        * @param   index
        *          The extent property's subscript.
        * @param   io
        *          Specifies an INPUT-OUTPUT parameter if {@code true}. In this case, the local
        *          variable in the called function will be initialized with the current value of the
        *          original variable. Otherwise (simple OUTPUT), the parameter is initialized to
        *          default value for respective datatype (follows the semantics of OUTPUT parameter
        *          passing from 4GL).
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(Class<T>   propType,
                                                     String     className,
                                                     String     property,
                                                     int        index,
                                                     boolean    io)
       {
          return new PropertyReference(className, property, index).wrap(io);
       }

       /**
        * Creates wrapper for a legacy static property and returns instance of T type. This form can
        * be used only with OUTPUT parameters.
        *
        * @param   propType
        *          The legacy property's data type.
        * @param   className
        *          The fully qualified legacy class name.
        * @param   property
        *          The property's name.
        * @param   index
        *          The extent property's subscript.
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(Class<T> propType,
                                                     String   className,
                                                     String   property,
                                                     int      index)
       {
          return new PropertyReference(className, property, index).wrap(false);
       }

       /**
        * Creates wrapper and returns instance of T type. This form can be used only with both
        * OUTPUT and INPUT-OUTPUT parameters (depending on {@code io} parameter).
        * <p>
        * This will convert the BDT instance to a Java-compatible reference, and wrap it into a
        * {@link jobject} instance.
        *
        * @param   type
        *          The expected Java type to be wrapped in {@link jobject}
        * @param   variable
        *          The variable to be wrapped.
        *
        * @return  instance of the T type.
        */
       public static <T> jobject<T> wrapToJava(Class<T> type, BaseDataType variable)
       {
          return wrapToJava(type, variable, false);
       }

       /**
        * Creates wrapper and returns instance of T type. This form can be used only with both
        * OUTPUT and INPUT-OUTPUT parameters (depending on {@code io} parameter).
        * <p>
        * This will convert the BDT instance to a Java-compatible reference, and wrap it into a
        * {@link jobject} instance.
        *
        * @param   type
        *          The expected Java type to be wrapped in {@link jobject}
        * @param   variable
        *          The variable to be wrapped.
        * @param   io
        *          Specifies an INPUT-OUTPUT parameter if {@code true}. In this case, the local
        *          variable in the called function will be initialized with the current value of the
        *          original variable. Otherwise (simple OUTPUT), the parameter is initialized to
        *          default value for respective datatype (follows the semantics of OUTPUT parameter
        *          passing from 4GL).
        *
        * @return  instance of the T type.
        */
       public static <T> jobject<T> wrapToJava(Class<T> type, BaseDataType variable, boolean io)
       {
          jobject<T> init = jobject.toJava(type, variable);

          // TODO: input-output ???
          jobject<T> wrapper = createVariable(jobject.class,
                                              init,
                                              variable,
                                              (bdt) -> new VariableAssigner(bdt, variable));

          return wrapper;
       }

       /**
        * Creates wrapper and returns instance of T type. This form can be used only with both
        * OUTPUT and INPUT-OUTPUT parameters (depending on {@code io} parameter).
        * <p>
        * This will convert the {@link jobject} instance to a legacy BDT reference, only if the
        * variable holds a Java instance which can be converted to a legacy BDT value.
        *
        * @param   type
        *          The expected BDT type.
        * @param   variable
        *          The variable to be wrapped.
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrapFromJava(Class<T> type, jobject<?> variable)
       {
          return wrapFromJava(type, variable, false);
       }

       /**
        * Creates wrapper and returns instance of T type. This form can be used only with both
        * OUTPUT and INPUT-OUTPUT parameters (depending on {@code io} parameter).
        * <p>
        * This will convert the {@link jobject} instance to a legacy BDT reference, only if the
        * variable holds a Java instance which can be converted to a legacy BDT value.
        *
        * @param   type
        *          The expected BDT type.
        * @param   variable
        *          The variable to be wrapped.
        * @param   io
        *          Specifies an INPUT-OUTPUT parameter if {@code true}. In this case, the local
        *          variable in the called function will be initialized with the current value of the
        *          original variable. Otherwise (simple OUTPUT), the parameter is initialized to
        *          default value for respective datatype (follows the semantics of OUTPUT parameter
        *          passing from 4GL).
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrapFromJava(Class<T>   type,
                                                             jobject<?> variable,
                                                             boolean    io)
       {
          T init = jobject.fromJava(type,  variable);

          // TODO: input-output ???
          T wrapper = createVariable(type, init, variable, (bdt) -> new VariableAssigner(bdt, variable));

          return wrapper;
       }

       /**
        * Creates variable wrapper and returns instance of T type. This form can be used only with
        * OUTPUT parameters.
        *
        * @param   type
        *          The {@link BaseDataType} of the parameter definition. The returned value will have
        *          this datatype.
        * @param   variable
        *          The variable to be wrapped.
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(Class<T> type, BaseDataType variable)
       {
          return wrap(type, variable, false);
       }

       /**
        * Creates wrapper and returns instance of T type. This form can be used only with both
        * OUTPUT and INPUT-OUTPUT parameters (depending on {@code io} parameter).
        *
        * @param   type
        *          The {@link BaseDataType} of the parameter definition. The returned value will have
        *          this datatype.
        * @param   variable
        *          The variable to be wrapped.
        * @param   io
        *          Specifies an INPUT-OUTPUT parameter if {@code true}. In this case, the local
        *          variable in the called function will be initialized with the current value of the
        *          original variable. Otherwise (simple OUTPUT), the parameter is initialized to
        *          default value for respective datatype (follows the semantics of OUTPUT parameter
        *          passing from 4GL).
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(Class<T> type, BaseDataType variable, boolean io)
       {
          BaseDataType init = io ? variable : null;

          T wrapper = createVariable(type, init, variable, (bdt) -> new VariableAssigner(bdt, variable));

          return wrapper;
       }

       /**
        * Creates wrapper and returns instance of T type.
        *
        * @param   dmo
        *          Proxy for record whose field is being accessed.
        * @param   property
        *          Name of dmo field.
        * @param   bdt
        *          The {@link BaseDataType} of the field. The returned value will have this datatype.
        *          This argument can be detected dynamically at runtime but, for performance reasons,
        *          it is computed and passed in at conversion time.
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(DataModelObject dmo, String property, Class<T> bdt)
       {
          return wrap(dmo, property, bdt, null);
       }

       /**
        * Creates wrapper and returns instance of T type.
        *
        * @param   dmo
        *          Proxy for record whose field is being accessed.
        * @param   property
        *          Name of dmo field.
        * @param   bdt
        *          The {@link BaseDataType} of the field. The returned value will have this datatype.
        *          This argument can be detected dynamically at runtime but, for performance reasons,
        *          it is computed and passed in at conversion time.
        * @param   index
        *          Index to access, if property represents an extent field.
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(DataModelObject dmo,
                                                     String property,
                                                     Class<T> bdt,
                                                     int index)
       {
          return wrap(dmo, property, bdt, new integer(index));
       }

       /**
        * Creates wrapper and returns instance of T type.
        *
        * @param   dmo
        *          Proxy for record whose field is being accessed.
        * @param   property
        *          Name of dmo field.
        * @param   bdt
        *          The {@link BaseDataType} of the field. The returned value will have this datatype.
        *          This argument can be detected dynamically at runtime but, for performance reasons,
        *          it is computed and passed in at conversion time.
        * @param   index
        *          Index to access, if property represents an extent field.
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(DataModelObject dmo,
                                                     String property,
                                                     Class<T> bdt,
                                                     NumberType index)
       {
          return wrap(dmo, property, bdt, index, false);
       }

       /**
        * Creates wrapper and returns instance of T type.
        *
        * @param   dmo
        *          Proxy for record whose field is being accessed.
        * @param   property
        *          Name of dmo field.
        * @param   bdt
        *          The {@link BaseDataType} of the field. The returned value will have this datatype.
        *          This argument can be detected dynamically at runtime but, for performance reasons,
        *          it is computed and passed in at conversion time.
        * @param   io
        *          Specifies an INPUT-OUTPUT parameter if {@code true}. In this case, the local
        *          variable in the called function will be initialized with the current value of the
        *          original variable. Otherwise (simple OUTPUT), the parameter is initialized to
        *          default value for respective datatype (follows the semantics of OUTPUT parameter
        *          passing from 4GL).
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(DataModelObject dmo,
                                                     String property,
                                                     Class<T> bdt,
                                                     boolean io)
       {
          return wrap(dmo, property, bdt, null, io);
       }

       /**
        * Creates wrapper and returns instance of T type.
        *
        * @param   dmo
        *          Proxy for record whose field is being accessed.
        * @param   property
        *          Name of dmo field.
        * @param   bdt
        *          The {@link BaseDataType} of the field. The returned value will have this datatype.
        *          This argument can be detected dynamically at runtime but, for performance reasons,
        *          it is computed and passed in at conversion time.
        * @param   index
        *          Index to access, if property represents an extent field.
        * @param   io
        *          Specifies an INPUT-OUTPUT parameter if {@code true}. In this case, the local
        *          variable in the called function will be initialized with the current value of the
        *          original variable. Otherwise (simple OUTPUT), the parameter is initialized to
        *          default value for respective datatype (follows the semantics of OUTPUT parameter
        *          passing from 4GL).
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(DataModelObject dmo,
                                                     String property,
                                                     Class<T> bdt,
                                                     int index,
                                                     boolean io)
       {
          return wrap(dmo, property, bdt, new integer(index), io);
       }

       /**
        * Creates wrapper and returns instance of T type.
        *
        * @param   dmo
        *          Proxy for record whose field is being accessed.
        * @param   property
        *          Name of dmo field.
        * @param   type
        *          The {@link BaseDataType} of the field. The returned value will have this datatype.
        *          This argument can be detected dynamically at runtime but, for performance reasons,
        *          it is computed and passed in at conversion time.
        * @param   index
        *          Index to access, if property represents an extent field.
        * @param   io
        *          Specifies an INPUT-OUTPUT parameter if {@code true}. In this case, the local
        *          variable in the called function will be initialized with the current value of the
        *          original variable. Otherwise (simple OUTPUT), the parameter is initialized to
        *          default value for respective datatype (follows the semantics of OUTPUT parameter
        *          passing from 4GL).
        *
        * @return  instance of the T type.
        */
       public static <T extends BaseDataType> T wrap(DataModelObject dmo,
                                                     String property,
                                                     Class<T> type,
                                                     NumberType index,
                                                     boolean io)
       {
          FieldReference field = new FieldReference(dmo, property, index);
          return wrap(field, io, type);
       }

       /**
         * Creates variable wrapper and returns instance of T type. This form can be used only with
         * OUTPUT parameters.
         *
         * @param   variable
         *          The variable to be wrapped.
         *
         * @return  instance of the T type.
         */
        static <T extends BaseDataType> T wrap(T variable)
        {
           return wrap(variable, false);
        }

        /**
         * Creates wrapper and returns instance of T type. This form can be used only with both
         * OUTPUT and INPUT-OUTPUT parameters (depending on {@code io} parameter).
         *
         * @param   variable
         *          The variable to be wrapped.
         * @param   io
         *          Specifies an INPUT-OUTPUT parameter if {@code true}. In this case, the local
         *          variable in the called function will be initialized with the current value of the
         *          original variable. Otherwise (simple OUTPUT), the parameter is initialized to
         *          default value for respective datatype (follows the semantics of OUTPUT parameter
         *          passing from 4GL).
         *
         * @return  instance of the T type.
         */
        static <T extends BaseDataType> T wrap(T variable, boolean io)
        {
           T init = (T) (io ? variable : null);

           T wrapper = (T) createVariable(variable.getClass(),
                                          init,
                                          variable,
                                          (bdt) -> new VariableAssigner(bdt, variable));

           return wrapper;
        }

        /**
         * Creates wrapper and returns instance of T type.
         *
         * @param   field
         *          The field reference.
         * @param   io
         *          Specifies an INPUT-OUTPUT parameter if {@code true}. In this case, the local variable in the
         *          called function will be initialized with the current value of the original variable. Otherwise
         *          (simple OUTPUT), the parameter is initialized to default value for respective datatype
         *          (follows the semantics of OUTPUT parameter passing from 4GL).
         * @param   type
         *          The {@link BaseDataType} of the field. The returned value will have this datatype.
         *
         * @return  instance of the T type.
         */
        static <T extends BaseDataType> T wrap(FieldReference field, boolean io, Class<?> type)
        {
           BaseDataType init = io ? field.get() : null;

           T wrapper = createVariable((Class<T>) type,
                                      init,
                                      null,
                                      (bdt) ->  new FieldAssigner(bdt, field));

           return wrapper;
        }

        /**
         * Creates wrapper and returns instance of T type.
         *
         * @param   ref
         *          The property reference.
         * @param   io
         *          Specifies an INPUT-OUTPUT parameter if {@code true}. In this case, the local variable in the
         *          called function will be initialized with the current value of the original variable. Otherwise
         *          (simple OUTPUT), the parameter is initialized to default value for respective datatype
         *          (follows the semantics of OUTPUT parameter passing from 4GL).
         *
         * @return  instance of the T type.
         */
        static <T extends BaseDataType> T wrap(PropertyReference ref, boolean io)
        {
           return ref.wrap(io);
        }

        /**
         * Create a new variable of type T as an anonymous class, which will act as a temporary transport for the
         * parameter assigner, which will be resolved and initialized only when the method is actually executed.
         *
         * @param    cls
         *           The parameter's type.
         * @param    init
         *           The initial value (applied only if not <code>null</code>).
         * @param    variable
         *           The caller's variable.
         * @param    assigner
         *           The assigner function which will be supplied by the anonymous class
         *           {@link BaseDataType#getAssigner()} implementation.
         *
         * @return   The new variable to be sent as an argument to the called method.
         */
        static <T extends BaseDataType> T createVariable(Class<T>               cls,
                                                         BaseDataType           init,
                                                         BaseDataType           variable,
                                                         Function<BaseDataType, AbstractSimpleParameter> assigner)
       {
          if (cls.isAnonymousClass())
          {
             cls = (Class<T>) cls.getSuperclass();
          }

          BaseDataType bdt;
          if (cls == blob.class)
          {
             bdt = new blob()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == memptr.class)
          {
             bdt = new memptr()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == raw.class)
          {
             bdt = new raw()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == comhandle.class)
          {
             bdt = new comhandle()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == date.class)
          {
             bdt = new date()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == datetime.class)
          {
             bdt = new datetime()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == datetimetz.class)
          {
             bdt = new datetimetz()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == handle.class)
          {
             bdt = new handle()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == jobject.class)
          {
             bdt = new jobject()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == logical.class)
          {
             bdt = new logical()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == decimal.class)
          {
             bdt = new decimal()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == int64.class)
          {
             bdt = new int64()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == integer.class)
          {
             bdt = new integer()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == recid.class)
          {
             bdt = new recid()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == object.class)
          {
             bdt = new object(((object) variable).type())
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == ObjectVar.class)
          {
             bdt = new ObjectVar(((ObjectVar) variable).type())
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == rowid.class)
          {
             bdt = new rowid()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == character.class)
          {
             bdt = new character()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == longchar.class)
          {
             bdt = new longchar()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else if (cls == clob.class)
          {
             bdt = new clob()
             {
                @Override
                Supplier<AbstractParameter> getAssigner()
                {
                   return () -> assigner.apply(this);
                }
             };
          }
          else
          {
             throw new IllegalArgumentException("Type " + cls.getName() +
                                                " is not recognized for OUTPUT/INPUT-OUTPUT arguments!");
          }

          if (cls == memptr.class)
          {
             ((memptr) bdt).asParameter((memptr) variable);
          }
          else
          {
             bdt.assign(init == null ? BaseDataType.generateDefault(cls) : init);
          }

          return (T) bdt;
       }
    }