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