# TypeFactory.java

    /*
    ** Module   : TypeFactory.java
    ** Abstract : defines APIs to create 4GL-compatible instances and initialize parameters.
    **
    ** Copyright (c) 2016-2023, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
    ** 001 CA  20160721 Created initial version.  Added APIs to create 4GL-compatible instances, with
    **                  or without initializers, with or without extent clauses.  Also, there are
    **                  APIs to initialize function/procedure parameters.
    ** 002 SVL 20170126 Added initializer for COM handle type.
    ** 003 OM  20170807 Added missing methods related to comhandles.
    ** 004 GES 20181211 Added object methods.
    **     OM  20181218 Improved generics.
    ** 005 CA  20190122 Removed deprecated object APIs - all objects must specify their underlying
    **                  type.
    **                  Legacy object instances are constructed as ObjectVar, as only these instances
    **                  will be enable the reference counting on assignment.
    ** 006 OM  20190626 Added chained-attribute expression error in handle.
    ** 007 CA  20190719 INPUT parameters must do reference counting always.
    ** 008 CA  20190927 Added support for direct Java access from 4GL code.
    ** 009 ME  20201125 Register newly created object array with indeterminate extent as dynamic.
    **     CA  20210215 An INPUT parameter at the property SETTER, method, procedure r function must be registered
    **                  as pending, so its reference can be tracked.
    **     ME  20210322 Instantiated object arrays are not registered as pending, also register handle arrays.
    **     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.
    **     VVT 20210917 Javadoc fixed.
    **     CA  20220120 Allow OO vars to be registered as pending, at their definition.
    **     CA  20220901 Refactored scope notification support: ScopeableFactory was removed, and the registration
    **                  is now specific to each type of scopeable.  For each case, the block will be registered
    **                  for scope support (for that particular scopeable) only when the scopeable is 'active'
    **                  (i.e. unnamed streams or accumulators are used).  This allows a lazy registration of
    **                  scopeables, to avoid the unnecessary overhead of processing all the scopeables for each
    **                  and every block.
    **     CA  20230116 Avoid using handle.unwrap, handle.getReference or other BDT usage from within FWD runtime.
    ** 010 CA  20230712 Added support for 'jobject' extent variables, emitted for Java-style arrays used in 4GL
    **                  code.
    ** 011 CA  20230817 Removed ObjectOps.register, as this registration is handled by runtime.
    */

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

    import java.util.function.Function;

    /**
     * Provides APIs to create or initialize or 4GL-compatbile instance.
     */
    public class TypeFactory
    {
       /** Function to create and initialize a {@link date} instance. */
       private static Function<date, date> DATE_INIT = (d) -> (new date(d));

       /** Function to create and initialize a {@link datetime} instance. */
       private static Function<datetime, datetime> DATETIME_INIT = (d) -> (new datetime(d));

       /** Function to create and initialize a {@link datetimetz} instance. */
       private static Function<datetimetz, datetimetz> DATETIMETZ_INIT = (d) -> (new datetimetz(d));

       /** Function to create and initialize a {@link logical} instance. */
       private static Function<Boolean, logical> BOOLEAN_INIT = (d) -> (d == null ? new logical()
                                                                                  : new logical(d));

       /** Function to create and initialize a {@link decimal} instance. */
       private static Function<decimal, decimal> DECIMAL_INIT = (d) -> (new decimal(d));

       /** Function to create and initialize a {@link integer} instance. */
       private static Function<Long, integer> INTEGER_INIT = (d) -> (d == null ? new integer()
                                                                               : new integer(d));

       /** Function to create and initialize a {@link int64} instance. */
       private static Function<Long, int64> INT64_INIT = (d) -> (d == null ? new int64()
                                                                           : new int64(d));

       /** Function to create and initialize a {@link character} instance. */
       private static Function<String, character> CHARACTER_INIT = (d) -> (d == null ? new character()
                                                                                     : new character(d));

       /** Function to create and initialize a {@link longchar} instance. */
       private static Function<String, longchar> LONGCHAR_INIT = (d) -> (d == null ? new longchar()
                                                                                   : new longchar(d));

       /** Function to create and initialize a {@link recid} instance. */
       private static Function<Long, recid> RECID_INIT = (d) -> (d == null ? new recid()
                                                                           : new recid(d));

       /**
        * Initialize an <code>INPUT</code> parameter by creating a deep copy of the specified instance.
        *
        * @param    var
        *           The original instance receive as an argument.
        *
        * @return   A copy of the given variable.
        *
        * @see BaseDataType#deepCopy()
        */
       public static <T extends BaseDataType> T initInput(T var)
       {
          T newVar = null;

          // this needs to create a ObjectVar
          if (var instanceof object)
          {
             if (ObjectOps.isValid(((object) var).ref))
             {
                newVar  = (T) new ObjectVar(((object) var).type());
                newVar.assign(var);
             }
             else
             {
                // TODO: we need to emit at the conversion the parameter's OO type (as it gets lost if
                // an INPUT is sent).
                newVar = (T) var.deepCopy();
             }

             ObjectOps.registerPending(newVar);
          }
          else if (var instanceof memptr)
          {
             memptr m = new memptr();
             m.asParameter((memptr) var);
             newVar = (T) m;
          }
          else
          {
             newVar = (T) var.deepCopy();
          }

          return newVar;
       }

       /**
        * Initialize an <code>INPUT</code> extent parameter by creating a deep copy of the specified
        * array.
        *
        * @param    var
        *           The original array instance receive as an argument.
        *
        * @return   A copy of the given array.
        *
        * @see ArrayAssigner#copyOf(BaseDataType[])
        */
       public static <T extends BaseDataType> T[] initInput(T[] var)
       {
          if (memptr.class.isAssignableFrom(var.getClass().getComponentType()))
          {
             memptr[] newVar = new memptr[var.length];
             for (int i = 0; i < newVar.length; i++)
             {
                newVar[i] = new memptr();
                newVar[i].asParameter((memptr) var[i]);
             }

             return (T[]) newVar;
          }

          // this needs to create a ObjectVar
          T[] newVar = ArrayAssigner.copyOf(var);

          return newVar;
       }

       /**
        * Initialize an <code>OUTPUT</code> parameter by setting it to its default value.
        *
        * @param    var
        *           The original instance receive as an argument.
        *
        * @return   The instance to be used at the method's execution.
        */
       public static <T extends BaseDataType> T initOutput(T var)
       {
          return initOutput(true, var);
       }

       /**
        * Initialize an <code>OUTPUT</code> parameter by setting it either to its default value or
        * to <code>unknown</code>, depending on the state of the <code>initDefault</code> flag.
        *
        * @param    initDefault
        *           When <code>true</code>, initialize to default init value, otherwise to unknown.
        * @param    var
        *           The original instance receive as an argument.
        *
        * @return   The instance to be used at the method's execution.
        */
       public static <T extends BaseDataType> T initOutput(boolean initDefault, T var)
       {
          T newVar = var.getAssigner() == null ? OutputParameter.wrap(var) : var;

          if (!(newVar instanceof memptr))
          {
             if (initDefault && !(var instanceof ObjectVar))
             {
                BaseDataType def = var.instantiateDefault();
                newVar.assign(def);
             }
             else
             {
                newVar.setUnknown();
             }
          }

          // this ensures the assigner is created and registered as pending for the next scope
          AbstractSimpleParameter assigner = (AbstractSimpleParameter) newVar.getAssigner().get();

          newVar = (T) AbstractParameter.postProcessOutput(assigner, var);

          if (newVar instanceof memptr)
          {
             ((memptr) newVar).asParameter((memptr) var);
          }

          return newVar;
       }

       /**
        * Initialize an <code>OUTPUT</code> parameter by setting it to the specified init value.
        *
        * @param    var
        *           The original instance receive as an argument.
        * @param    init
        *           The init instance.
        *
        * @return   The instance to be used at the method's execution.
        */
       public static <T extends BaseDataType> T initOutput(T var, T init)
       {
          // memptr can't have an INIT option

          T newVar = var.getAssigner() == null ? OutputParameter.wrap(var) : var;

          newVar.assign(init);

          // this ensures the assigner is created and registered as pending for the next scope
          AbstractSimpleParameter assigner = (AbstractSimpleParameter) newVar.getAssigner().get();

          newVar = (T) AbstractParameter.postProcessOutput(assigner, var);

          return newVar;
       }

       /**
        * Initialize an <code>OUTPUT</code> extent parameter.
        *
        * @param    var
        *           The original instance receive as an argument.
        *
        * @return   The associated array.
        *
        * @see OutputExtentParameter#initParameter()
        */
       public static <T extends BaseDataType> T[] initOutput(OutputExtentParameter<T> var)
       {
          var.registerParameter();

          T[] newVar = var.initParameter();

          return newVar;
       }

       /**
        * Initialize an <code>OUTPUT</code> extent parameter using the specified initializer and
        * extent value.
        *
        * @param    var
        *           The original instance receive as an argument.
        * @param    extent
        *           The array length.
        * @param    init
        *           The initializer array.
        *
        * @return   The associated array.
        *
        * @see #initExtent(BaseDataType[], Object[], Function)
        */
       public static <T extends BaseDataType> T[] initOutput(OutputExtentParameter<T> var,
                                                             int                      extent,
                                                             T...                     init)
       {
          var.registerParameter();

          T[] newVar = var.initParameter(extent);

          if (extent == 0 || memptr.class.isAssignableFrom(newVar.getClass().getComponentType()))
          {
             return newVar;
          }

          if (init == null || init.length == 0)
          {
             BaseDataType.initializeDefaultExtent(newVar);
          }
          else
          {
             T last = init[init.length - 1];
             for (int i = 0; i < Math.min(extent, init.length); i++)
             {
                newVar[i].assign(init[i]);
             }
             for (int i = init.length; i < extent; i++)
             {
                newVar[i].assign(last);
             }
          }

          return newVar;
       }

       /**
        * Initialize an <code>OUTPUT</code> parameter by setting it to its default value.
        *
        * @param    var
        *           The original instance receive as an argument.
        *
        * @return   The instance to be used at the method's execution.
        */
       public static <T extends BaseDataType> T initInputOutput(T var)
       {
          T newVar = var.getAssigner() == null ? OutputParameter.wrap(var, true) : var;

          // this ensures the assigner is created and registered as pending for the next scope
          AbstractSimpleParameter assigner = (AbstractSimpleParameter) newVar.getAssigner().get();

          newVar = (T) AbstractParameter.postProcessOutput(assigner, var);

          if (newVar instanceof memptr)
          {
             ((memptr) newVar).asParameter((memptr) var);
          }

          return newVar;
       }

       /**
        * Initialize an <code>INPUT-OUTPUT</code> extent parameter.
        *
        * @param    var
        *           The original instance receive as an argument.
        *
        * @return   The associated array.
        *
        * @see InputOutputExtentParameter#initParameter()
        */
       public static <T extends BaseDataType> T[] initInputOutput(InputOutputExtentParameter<T> var)
       {
          var.registerParameter();

          T[] newVar = var.initParameter();

          return newVar;
       }

       /**
        * Initialize an <code>INPUT-OUTPUT</code> extent parameter.
        *
        * @param    var
        *           The original instance receive as an argument.
        * @param    extent
        *           The array length.
        *
        * @return   The associated array.
        *
        * @see InputOutputExtentParameter#initParameter(int)
        */
       public static <T extends BaseDataType> T[] initInputOutput(InputOutputExtentParameter<T> var,
                                                                  int                           extent)
       {
          var.registerParameter();

          T[] newVar = var.initParameter(extent);

          return newVar;
       }

       /**
        * Create a {@link memptr} instance initialized to the default value.
        *
        * @return   See above.
        */
       public static memptr memptr()
       {
          memptr res = new memptr();

          return res;
       }

       /**
        * Create a {@link memptr} array of the given extent and initialize it with the default value.
        *
        * @param    extent
        *           The array length.
        *
        * @return   See above.
        */
       public static memptr[] memptrExtent(int extent)
       {
          memptr[] res = new memptr[extent];

          BaseDataType.initializeDefaultExtent(res);

          return res;
       }

       /**
        * Create a 0-length {@link memptr} array.
        *
        * @return   See above.
        */
       public static memptr[] memptrExtent()
       {
          memptr[] res = new memptr[0];

          return res;
       }

       /**
        * Create a {@link raw} instance initialized to the default value.
        *
        * @return   See above.
        */
       public static raw raw()
       {
          raw res = new raw();

          return res;
       }

       /**
        * Create a {@link raw} array of the given extent and initialize it with the default value.
        *
        * @param    extent
        *           The array length.
        *
        * @return   See above.
        */
       public static raw[] rawExtent(int extent)
       {
          raw[] res = new raw[extent];

          BaseDataType.initializeDefaultExtent(res);

          return res;
       }

       /**
        * Create a 0-length {@link raw} array.
        *
        * @return   See above.
        */
       public static raw[] rawExtent()
       {
          raw[] res = new raw[0];

          return res;
       }

       /**
        * Create a {@link date} instance initialized to the default value.
        *
        * @return   See above.
        */
       public static date date()
       {
          date res = new date();

          return res;
       }

       /**
        * Create a {@link date} array of the given extent.  If there is data in the
        * <code>init</code> array for a specified index, that will be used to initialize it.
        * Otherwise, if the init array is empty, default initializer will be used; if the init array
        * is not empty, all indices which don't have a counterpart will use the last entry in
        * this array.
        *
        * @param    extent
        *           The array length.
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static date[] dateExtent(int extent, date... init)
       {
          date[] res = new date[extent];

          initExtent(res, init, DATE_INIT);

          return res;
       }

       /**
        * Create a {@link date} extent using as initial values the data in the <code>init</code>
        * array.  If the init array is empty, a non-initialized (0-extent) array will be returned.
        *
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static date[] dateExtent(date... init)
       {
          date[] res = new date[init.length];

          initExtent(res, init, DATE_INIT);

          return res;
       }

       /**
        * Create a {@link date} instance initialized to the init value.
        *
        * @param    init
        *           The init value.
        *
        * @return   See above.
        */
       public static date date(date init)
       {
          date res = new date(init);

          return res;
       }

       /**
        * Create a {@link datetime} instance initialized to the default value.
        *
        * @return   See above.
        */
       public static datetime datetime()
       {
          datetime res = new datetime();

          return res;
       }

       /**
        * Create a {@link datetime} array of the given extent.  If there is data in the
        * <code>init</code> array for a specified index, that will be used to initialize it.
        * Otherwise, if the init array is empty, default initializer will be used; if the init array
        * is not empty, all indices which don't have a counterpart will use the last entry in
        * this array.
        *
        * @param    extent
        *           The array length.
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static datetime[] datetimeExtent(int extent, datetime... init)
       {
          datetime[] res = new datetime[extent];

          initExtent(res, init, DATETIME_INIT);

          return res;
       }

       /**
        * Create a {@link datetime} extent using as initial values the data in the <code>init</code>
        * array.  If the init array is empty, a non-initialized (0-extent) array will be returned.
        *
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static datetime[] datetimeExtent(datetime... init)
       {
          datetime[] res = new datetime[init.length];

          initExtent(res, init, DATETIME_INIT);

          return res;
       }

       /**
        * Create a {@link datetime} instance initialized to the init value.
        *
        * @param    init
        *           The init value.
        *
        * @return   See above.
        */
       public static datetime datetime(datetime init)
       {
          datetime res = new datetime(init);

          return res;
       }

       /**
        * Create a {@link datetimetz} instance initialized to the default value.
        *
        * @return   See above.
        */
       public static datetimetz datetimetz()
       {
          datetimetz res = new datetimetz();

          return res;
       }

       /**
        * Create a {@link datetimetz} array of the given extent.  If there is data in the
        * <code>init</code> array for a specified index, that will be used to initialize it.
        * Otherwise, if the init array is empty, default initializer will be used; if the init array
        * is not empty, all indices which don't have a counterpart will use the last entry in
        * this array.
        *
        * @param    extent
        *           The array length.
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static datetimetz[] datetimetzExtent(int extent, datetimetz... init)
       {
          datetimetz[] res = new datetimetz[extent];

          initExtent(res, init, DATETIMETZ_INIT);

          return res;
       }

       /**
        * Create a {@link datetimetz} extent using as initial values the data in the <code>init</code>
        * array.  If the init array is empty, a non-initialized (0-extent) array will be returned.
        *
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static datetimetz[] datetimetzExtent(datetimetz... init)
       {
          datetimetz[] res = new datetimetz[init.length];

          initExtent(res, init, DATETIMETZ_INIT);

          return res;
       }

       /**
        * Create a {@link datetimetz} instance initialized to the init value.
        *
        * @param    init
        *           The init value.
        *
        * @return   See above.
        */
       public static datetimetz datetimetz(datetimetz init)
       {
          datetimetz res = new datetimetz(init);

          return res;
       }

       /**
        * Create a generic {@link jobject} instance initialized to the default value.
        *
        * @param   cls
        *          The class that will be wrapped by this object.
        *
        * @return   See above.
        */
       public static <V extends Object> jobject<V> jobject(Class<V> cls)
       {
          jobject<V> res = new jobject<V>(cls);

          ObjectOps.registerPending(res);

          return res;
       }

       /**
        * Create an empty {@link jobject} array.
        *
        * @param   cls
        *          The class that will be wrapped by the objects in this array.
        *
        * @return  See above.
        */
       public static <V extends Object> jobject<V>[] jobjectExtent(Class<V> cls)
       {
          return jobjectExtent(0, cls);
       }

       /**
        * Create an {@link jobject} array of the given extent and initialize it with the default value.
        *
        * @param    extent
        *           The array length.
        * @param    cls
        *           The class that will be wrapped by the objects in this array.
        *
        * @return   See above.
        */
       public static <V extends Object> jobject<V>[] jobjectExtent(int extent, Class<V> cls)
       {
          jobject<V>[] res = new jobject[extent]; // unchecked warning

          for (int i = 0; i < extent; i++)
          {
             res[i] = new jobject<V>(cls);
          }

          return res;
       }

       /**
        * Create a generic {@link object} instance initialized to the default value.
        *
        * @param   cls
        *          The class that will be wrapped by this object.
        *
        * @return   See above.
        */
       public static <V extends _BaseObject_> object<V> object(Class<V> cls)
       {
          object<V> res = new ObjectVar<V>(cls);

          ObjectOps.registerPending(res);

          return res;
       }

       /**
        * Create an empty {@link object} array  (indeterminate EXTENT variable).
        *
        * @param    cls
        *           The class that will be wrapped by the objects in this array.
        * @param    register
        *           Flag indicating if the registration must be done as 'pending' for the next top-level block.
        *
        * @return   See above.
        */
       public static <V extends _BaseObject_> object<V>[] objectExtent(Class<V> cls, boolean register)
       {
          return objectExtent(0, cls, register);
       }

       /**
        * Create an empty {@link object} array  (indeterminate EXTENT variable).
        *
        * @param   cls
        *          The class that will be wrapped by the objects in this array.
        *
        * @return  See above.
        */
       public static <V extends _BaseObject_> object<V>[] objectExtent(Class<V> cls)
       {
          return objectExtent(cls, true);
       }

       /**
        * Create an {@link object} array of the given extent and initialize it with the default value.
        *
        * @param    extent
        *           The array length.
        * @param    cls
        *           The class that will be wrapped by the objects in this array.
        *
        * @return   See above.
        */
       public static <V extends _BaseObject_> object<V>[] objectExtent(int extent, Class<V> cls)
       {
          return objectExtent(extent, cls, true);
       }

       /**
        * Create an {@link object} array of the given extent and initialize it with the default value.
        *
        * @param    extent
        *           The array length.
        * @param    cls
        *           The class that will be wrapped by the objects in this array.
        * @param    register
        *           Flag indicating if the registration must be done as 'pending' for the next top-level block.
        *
        * @return   See above.
        */
       public static <V extends _BaseObject_> object<V>[] objectExtent(int extent, Class<V> cls, boolean register)
       {
          object<V>[] res = new object[extent]; // unchecked warning

          if (extent == 0)
          {
             ArrayAssigner.registerDynamicArray(res);

             ArrayAssigner.setObjectType(res, cls);
          }

          for (int i = 0; i < extent; i++)
          {
             res[i] = new ObjectVar<V>(cls);
          }

          if (register)
          {
             ObjectOps.registerPending((Object) res);
          }

          return res;
       }

       /**
        * Create a {@link handle} instance initialized to the default value.
        *
        * @return   See above.
        */
       public static handle handle()
       {
          handle res = new handle(false);

          return res;
       }

       /**
        * Create a 0-length {@link handle} array.
        *
        * @return   See above.
        */
       public static handle[] handleExtent()
       {
          return handleExtent(0);
       }

       /**
        * Create a {@link handle} array of the given extent and initialize it with the default value.
        *
        * @param    extent
        *           The array length.
        *
        * @return   See above.
        */
       public static handle[] handleExtent(int extent)
       {
          handle[] res = new handle[extent];

          if (extent == 0)
          {
             ArrayAssigner.registerDynamicArray(res);
          }

          BaseDataType.initializeDefaultExtent(res);

          return res;
       }

       /**
        * Create a {@link comhandle} instance initialized to the default value.
        *
        * @return   See above.
        */
       public static comhandle comhandle()
       {
          return new comhandle();
       }

       /**
        * Create a 0-length {@link comhandle} array.
        *
        * @return   See above.
        */
       public static comhandle[] comhandleExtent()
       {
          return new comhandle[0];
       }

       /**
        * Create a 0-length {@link comhandle} array.
        *
        * @param extent
        *        array extent
        *
        * @return   See above.
        */
       public static comhandle[] comhandleExtent(int extent)
       {
          comhandle[] res = new comhandle[extent];

          BaseDataType.initializeDefaultExtent(res);

          return res;
       }

       /**
        * Create a {@link logical} instance initialized to the default value.
        *
        * @return   See above.
        */
       public static logical logical()
       {
          logical res = new logical(false);

          return res;
       }

       /**
        * Create a {@link logical} array of the given extent.  If there is data in the
        * <code>init</code> array for a specified index, that will be used to initialize it.
        * Otherwise, if the init array is empty, default initializer will be used; if the init array
        * is not empty, all indices which don't have a counterpart will use the last entry in
        * this array.
        *
        * @param    extent
        *           The array length.
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static logical[] logicalExtent(int extent, Boolean... init)
       {
          logical[] res = new logical[extent];

          initExtent(res, init, BOOLEAN_INIT);

          return res;
       }

       /**
        * Create a {@link logical} extent using as initial values the data in the <code>init</code>
        * array.  If the init array is empty, a non-initialized (0-extent) array will be returned.
        *
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static logical[] logicalExtent(Boolean... init)
       {
          logical[] res = new logical[init.length];

          initExtent(res, init, BOOLEAN_INIT);

          return res;
       }

       /**
        * Create a {@link logical} instance.  If the <code>init</code> is null, initialize it to
        * <code>unknown</code>.  Otherwise, initialize it to the init value.
        *
        * @param    init
        *           The init value.
        *
        * @return   See above.
        */
       public static logical logical(Boolean init)
       {
          logical res = (init == null ? new logical() : new logical(init));

          return res;
       }

       /**
        * Create a {@link decimal} instance initialized to the default value.
        *
        * @return   See above.
        */
       public static decimal decimal()
       {
          decimal res = new decimal(0);

          return res;
       }

       /**
        * Create a {@link decimal} array of the given extent.  If there is data in the
        * <code>init</code> array for a specified index, that will be used to initialize it.
        * Otherwise, if the init array is empty, default initializer will be used; if the init array
        * is not empty, all indices which don't have a counterpart will use the last entry in
        * this array.
        *
        * @param    extent
        *           The array length.
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static decimal[] decimalExtent(int extent, decimal... init)
       {
          decimal[] res = new decimal[extent];

          initExtent(res, init, DECIMAL_INIT);

          return res;
       }

       /**
        * Create a {@link decimal} extent using as initial values the data in the <code>init</code>
        * array.  If the init array is empty, a non-initialized (0-extent) array will be returned.
        *
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static decimal[] decimalExtent(decimal... init)
       {
          decimal[] res = new decimal[init.length];

          initExtent(res, init, DECIMAL_INIT);

          return res;
       }

       /**
        * Create a {@link decimal} instance initialized to the init value.
        *
        * @param    init
        *           The init value.
        *
        * @return   See above.
        */
       public static decimal decimal(decimal init)
       {
          decimal res = new decimal(init);

          return res;
       }

       /**
        * Create a {@link integer} instance initialized to the default value.
        *
        * @return   See above.
        */
       public static integer integer()
       {
          integer res = new integer(0);

          return res;
       }

       /**
        * Create a {@link integer} array of the given extent.  If there is data in the
        * <code>init</code> array for a specified index, that will be used to initialize it.
        * Otherwise, if the init array is empty, default initializer will be used; if the init array
        * is not empty, all indices which don't have a counterpart will use the last entry in
        * this array.
        *
        * @param    extent
        *           The array length.
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static integer[] integerExtent(int extent, Long... init)
       {
          integer[] res = new integer[extent];

          initExtent(res, init, INTEGER_INIT);

          return res;
       }

       /**
        * Create a {@link integer} extent using as initial values the data in the <code>init</code>
        * array.  If the init array is empty, a non-initialized (0-extent) array will be returned.
        *
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static integer[] integerExtent(Long... init)
       {
          integer[] res = new integer[init.length];

          initExtent(res, init, INTEGER_INIT);

          return res;
       }

       /**
        * Create a {@link integer} instance.  If the <code>init</code> is null, initialize it to
        * <code>unknown</code>.  Otherwise, initialize it to the init value.
        *
        * @param    init
        *           The init value.
        *
        * @return   See above.
        */
       public static integer integer(Long init)
       {
          integer res = (init == null ? new integer() : new integer(init));

          return res;
       }

       /**
        * Create a {@link int64} instance initialized to the default value.
        *
        * @return   See above.
        */
       public static int64 int64()
       {
          int64 res = new int64(0);

          return res;
       }

       /**
        * Create a {@link int64} array of the given extent.  If there is data in the
        * <code>init</code> array for a specified index, that will be used to initialize it.
        * Otherwise, if the init array is empty, default initializer will be used; if the init array
        * is not empty, all indices which don't have a counterpart will use the last entry in
        * this array.
        *
        * @param    extent
        *           The array length.
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static int64[] int64Extent(int extent, Long... init)
       {
          int64[] res = new int64[extent];

          initExtent(res, init, INT64_INIT);

          return res;
       }

       /**
        * Create a {@link int64} extent using as initial values the data in the <code>init</code>
        * array.  If the init array is empty, a non-initialized (0-extent) array will be returned.
        *
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static int64[] int64Extent(Long... init)
       {
          int64[] res = new int64[init.length];

          initExtent(res, init, INT64_INIT);

          return res;
       }

       /**
        * Create a {@link int64} instance.  If the <code>init</code> is null, initialize it to
        * <code>unknown</code>.  Otherwise, initialize it to the init value.
        *
        * @param    init
        *           The init value.
        *
        * @return   See above.
        */
       public static int64 int64(Long init)
       {
          int64 res = (init == null ? new int64() : new int64(init));

          return res;
       }

       /**
        * Create a {@link rowid} instance initialized to the default value.
        *
        * @return   See above.
        */
       public static rowid rowid()
       {
          rowid res = new rowid();

          return res;
       }

       /**
        * Create a {@link rowid} array of the given extent and initialize it with the default value.
        *
        * @param    extent
        *           The array length.
        *
        * @return   See above.
        */
       public static rowid[] rowidExtent(int extent)
       {
          rowid[] res = new rowid[extent];

          BaseDataType.initializeDefaultExtent(res);

          return res;
       }

       /**
        * Create a 0-length {@link rowid} array.
        *
        * @return   See above.
        */
       public static rowid[] rowidExtent()
       {
          rowid[] res = new rowid[0];

          return res;
       }

       /**
        * Create a {@link recid} instance initialized to the default value.
        *
        * @return   See above.
        */
       public static recid recid()
       {
          recid res = new recid();

          return res;
       }

       /**
        * Create a {@link recid} array of the given extent.  If there is data in the
        * <code>init</code> array for a specified index, that will be used to initialize it.
        * Otherwise, if the init array is empty, default initializer will be used; if the init array
        * is not empty, all indices which don't have a counterpart will use the last entry in
        * this array.
        *
        * @param    extent
        *           The array length.
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static recid[] recidExtent(int extent, Long... init)
       {
          recid[] res = new recid[extent];

          initExtent(res, init, RECID_INIT);

          return res;
       }

       /**
        * Create a {@link recid} extent using as initial values the data in the <code>init</code>
        * array.  If the init array is empty, a non-initialized (0-extent) array will be returned.
        *
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static recid[] recidExtent(Long... init)
       {
          recid[] res = new recid[init.length];

          initExtent(res, init, RECID_INIT);

          return res;
       }

       /**
        * Create a {@link recid} instance.  If the <code>init</code> is null, initialize it to
        * <code>unknown</code>.  Otherwise, initialize it to the init value.
        *
        * @param    init
        *           The init value.
        *
        * @return   See above.
        */
       public static recid recid(Long init)
       {
          recid res = (init == null ? new recid() : new recid(init));

          return res;
       }

       /**
        * Create a {@link character} instance initialized to the default value.
        *
        * @return   See above.
        */
       public static character character()
       {
          character res = new character("");

          return res;
       }

       /**
        * Create a {@link character} array of the given extent.  If there is data in the
        * <code>init</code> array for a specified index, that will be used to initialize it.
        * Otherwise, if the init array is empty, default initializer will be used; if the init array
        * is not empty, all indices which don't have a counterpart will use the last entry in
        * this array.
        *
        * @param    extent
        *           The array length.
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static character[] characterExtent(int extent, String... init)
       {
          character[] res = new character[extent];

          initExtent(res, init, CHARACTER_INIT);

          return res;
       }

       /**
        * Create a {@link character} extent using as initial values the data in the <code>init</code>
        * array.  If the init array is empty, a non-initialized (0-extent) array will be returned.
        *
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static character[] characterExtent(String... init)
       {
          character[] res = new character[init.length];

          initExtent(res, init, CHARACTER_INIT);

          return res;
       }

       /**
        * Create a {@link character} instance.  If the <code>init</code> is null, initialize it to
        * <code>unknown</code>.  Otherwise, initialize it to the init value.
        *
        * @param    init
        *           The init value.
        *
        * @return   See above.
        */
       public static character character(String init)
       {
          character res = (init == null ? new character() : new character(init));

          return res;
       }

       /**
        * Create a {@link longchar} instance initialized to the default value.
        *
        * @return   See above.
        */
       public static longchar longchar()
       {
          longchar res = new longchar("");

          return res;
       }

       /**
        * Create a {@link longchar} array of the given extent.  If there is data in the
        * <code>init</code> array for a specified index, that will be used to initialize it.
        * Otherwise, if the init array is empty, default initializer will be used; if the init array
        * is not empty, all indices which don't have a counterpart will use the last entry in
        * this array.
        *
        * @param    extent
        *           The array length.
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static longchar[] longcharExtent(int extent, String... init)
       {
          longchar[] res = new longchar[extent];

          initExtent(res, init, LONGCHAR_INIT);

          return res;
       }

       /**
        * Create a {@link longchar} extent using as initial values the data in the <code>init</code>
        * array.  If the init array is empty, a non-initialized (0-extent) array will be returned.
        *
        * @param    init
        *           The initializer array.
        *
        * @return   See above.
        */
       public static longchar[] longcharExtent(String... init)
       {
          longchar[] res = new longchar[init.length];

          initExtent(res, init, LONGCHAR_INIT);

          return res;
       }

       /**
        * Create a {@link longchar} instance.  If the <code>init</code> is null, initialize it to
        * <code>unknown</code>.  Otherwise, initialize it to the init value.
        *
        * @param    init
        *           The init value.
        *
        * @return   See above.
        */
       public static longchar longchar(String init)
       {
          longchar res = (init == null ? new longchar() : new longchar(init));

          return res;
       }

       /**
        * Initialize the specified array using; if there is data in the <code>init</code> array for
        * a specified index, that will be used to initialize it.  Otherwise, if the init array is
        * empty, default initializer will be used; if the init array is not empty, all indices which
        * don't have a counterpart will use the last entry in this array.
        *
        * @param    res
        *           The array to be initialized.
        * @param    init
        *           The initializer.
        * @param    finit
        *           Function which will create either a default value or an initialized value,
        *           depending on the given argument.
        */
       private static <T extends BaseDataType, I> void initExtent(BaseDataType[] res,
                                                                  I[]            init,
                                                                  Function<I, T> finit)
       {
          int extent = res.length;
          if (extent == 0)
          {
             ArrayAssigner.registerDynamicArray(res);
             return;
          }

          if (init == null || init.length == 0)
          {
             BaseDataType.initializeDefaultExtent(res);
          }
          else
          {
             I last = init[init.length - 1];
             for (int i = 0; i < Math.min(extent, init.length); i++)
             {
                res[i] = finit.apply(init[i]);
             }
             for (int i = init.length; i < extent; i++)
             {
                res[i] = finit.apply(last);
             }
          }
       }
    }