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