# LegacyOpenClientCaller.java

    /*
    ** Module   : LegacyOpenClientCaller.java
    ** Abstract : Defines APIs to manage the arguments and perform pseudo-dynamic calls, for the case when
    **            OpenClient uses the 'invokeWithArgs' API.
    **
    ** Copyright (c) 2023-2024, Golden Code Development Corporation.
    **
    ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
    ** 001 CA  20230712 Created initial version.
    ** 002 CA  20240328 Added APIs to associate a pseudo-dynamic argument as a parameter to a static dataset/table.
    **                  Automatically clean up created resources during argument processing, which are still valid.
    ** 003 CA  20240512 Do not register invalid resources created from the varargs via 'getTableHandle' or
    **                  'getDataSetHandle'.
    */

    /*
    ** This program is free software: you can redistribute it and/or modify
    ** it under the terms of the GNU Affero General Public License as
    ** published by the Free Software Foundation, either version 3 of the
    ** License, or (at your option) any later version.
    **
    ** This program is distributed in the hope that it will be useful,
    ** but WITHOUT ANY WARRANTY; without even the implied warranty of
    ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ** GNU Affero General Public License for more details.
    **
    ** You may find a copy of the GNU Affero GPL version 3 at the following
    ** location: https://www.gnu.org/licenses/agpl-3.0.en.html
    **
    ** Additional terms under GNU Affero GPL version 3 section 7:
    **
    **   Under Section 7 of the GNU Affero GPL version 3, the following additional
    **   terms apply to the works covered under the License.  These additional terms
    **   are non-permissive additional terms allowed under Section 7 of the GNU
    **   Affero GPL version 3 and may not be removed by you.
    **
    **   0. Attribution Requirement.
    **
    **     You must preserve all legal notices or author attributions in the covered
    **     work or Appropriate Legal Notices displayed by works containing the covered
    **     work.  You may not remove from the covered work any author or developer
    **     credit already included within the covered work.
    **
    **   1. No License To Use Trademarks.
    **
    **     This license does not grant any license or rights to use the trademarks
    **     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
    **     of Golden Code Development Corporation. You are not authorized to use the
    **     name Golden Code, FWD, or the names of any author or contributor, for
    **     publicity purposes without written authorization.
    **
    **   2. No Misrepresentation of Affiliation.
    **
    **     You may not represent yourself as Golden Code Development Corporation or FWD.
    **
    **     You may not represent yourself for publicity purposes as associated with
    **     Golden Code Development Corporation, FWD, or any author or contributor to
    **     the covered work, without written authorization.
    **
    **   3. No Misrepresentation of Source or Origin.
    **
    **     You may not represent the covered work as solely your work.  All modified
    **     versions of the covered work must be marked in a reasonable way to make it
    **     clear that the modified work is not originating from Golden Code Development
    **     Corporation or FWD.  All modified versions must contain the notices of
    **     attribution required in this license.
    */

    package com.goldencode.p2j.util;

    import java.util.*;

    import com.goldencode.p2j.oo.lang.*;
    import com.goldencode.p2j.persist.*;
    import com.goldencode.p2j.security.*;
    import com.goldencode.p2j.util.ProcedureManager.ProcedureHelper;

    /**
     * Defines APIs to manage the arguments sent by the {@link LegacyJavaAppserverClient#invokeWithArgs} call.
     * <p>
     * The target program acts as a controller, where it identifies the program to dispatch to call from the
     * arguments or other means.  Only the OUTPUT or INPUT-OUTPUT argument can be sent back to the caller, via
     * the {@link #setArgument} and other setter APIs.
     * <p>
     * Arguments need to be retrieved via the getter APIs, and the target controller will need to prepare them,
     * as needed, to be eventually passed to the {@link #invokeExternalProgram}, {@link #invokeMethod} or
     * {@link #invokeStaticMethod}, to invoke the real target.
     * <p>
     * The getter, setters or otherwise APIs can be called only from the context of the target external program
     * acting as a controller.  Using these APIs from other code will throw a {@link ErrorConditionException}.
     */
    public class LegacyOpenClientCaller
    {
       /** Context-local state. */
       private static final ContextLocal<WorkArea> local = new ContextLocal<WorkArea>()
       {
          protected WorkArea initialValue()
          {
             return new WorkArea();
          };
       };

       /**
        * Associate the given dataset with the dataset-handle parameter at the given index.
        * <p>
        * This will act as if the given dataset reference has been defined as a parameter for the external program.
        *
        * @param    idx
        *           The 1-based index for the argument.
        * @param    dataset
        *           The handle to a static dataset or a handle where to store the dynamic dataset, otherwise.
        */
       public static void asDataSetParameter(int idx, handle dataset)
       {
          WorkArea wa = local.get();

          securityCheck(wa, true);

          validateIndex(wa, idx);

          idx = idx - 1;

          if (wa.argTypes[idx] != LegacyJavaAppserverApi.TYPE_DATASET_HANDLE)
          {
             ErrorManager.recordOrThrowError(-1,
                                             "Argument at index " + (idx + 1) + " must be a DATASET-HANDLE to " +
                                             "associate it as parameter.");
             return;
          }

          boolean isInput = wa.modes.charAt(idx) == 'I';
          boolean isOutput = wa.modes.charAt(idx) == 'O';
          boolean isInputOutput = wa.modes.charAt(idx) == 'U';
          DataSetParameter dsp = (DataSetParameter) wa.arguments[idx];

          if (dataset.getResource() instanceof DataSet)
          {
             DataSet ds = (DataSet) dataset.getResource();
             if (ds._dynamic())
             {
                ErrorManager.recordOrThrowError(-1,
                                                "Argument at index " + (idx + 1 ) + " cannot be associated as " +
                                                "parameter - the given dataset is dynamic.");
                return;
             }

             ds = ds.getProxyDataSet();
             DataSet.associate(dsp, ds, isInput || isInputOutput, isOutput || isInputOutput);
             wa.boundArguments[idx] = true;
          }
          else
          {
             DataSet.createDynamicDataSet(dsp, dataset, isInput || isInputOutput, isOutput || isInputOutput);
             wa.boundArguments[idx] = true;
          }

       }

       /**
        * Associate the given temp-table with the table-handle parameter at the given index.
        * <p>
        * This will act as if the given DMO reference has been defined as a parameter for the external program.
        *
        * @param    idx
        *           The 1-based index for the argument.
        * @param    table
        *           The handle to a static temp-table or a handle where to store the dynamic temp-table, otherwise.
        */
       public static void asTableParameter(int idx, handle table)
       {
          WorkArea wa = local.get();

          securityCheck(wa, true);

          validateIndex(wa, idx);

          idx = idx - 1;

          if (wa.argTypes[idx] != LegacyJavaAppserverApi.TYPE_TABLE_HANDLE)
          {
             ErrorManager.recordOrThrowError(-1,
                                             "Argument at index " + (idx + 1) + " must be a TABLE-HANDLE to " +
                                             "associate it as parameter.");
             return;
          }

          boolean isInput = wa.modes.charAt(idx) == 'I';
          boolean isOutput = wa.modes.charAt(idx) == 'O';
          boolean isInputOutput = wa.modes.charAt(idx) == 'U';
          TableParameter tp = (TableParameter) wa.arguments[idx];

          if (table.getResource() instanceof TempTable)
          {
             TempTable tt = (TempTable) table.getResource();
             if (tt._dynamic())
             {
                ErrorManager.recordOrThrowError(-1,
                                                "Argument at index " + (idx + 1 ) + " cannot be associated as " +
                                                "parameter - the given temp-table is dynamic.");
                return;
             }

             TemporaryBuffer.associate(tp,
                                       (Temporary) tt.defaultBufferHandleNative(),
                                       isInput || isInputOutput,
                                       isOutput || isInputOutput);
             wa.boundArguments[idx] = true;
          }
          else
          {
             TemporaryBuffer.createDynamicTable(tp, table, isInput || isInputOutput, isOutput || isInputOutput);
             wa.boundArguments[idx] = true;
          }
       }

       /**
        * Get the number of arguments sent by the remote side.
        *
        * @return   See above.
        */
       public static integer getNumArguments()
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          return new integer(wa.argTypes.length);
       }

       /**
        * Check if the argument at the specified index was sent as INPUT.
        *
        * @param    idx
        *           The 1-based index for the argument.
        *
        * @return   <code>true</code> if the argument's mode is INPUT.
        */
       public static boolean isInput(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx);

          char mode = wa.modes.charAt(idx - 1);
          return mode == 'I';
       }

       /**
        * Check if the argument at the specified index was sent as OUTPUT.
        *
        * @param    idx
        *           The 1-based index for the argument.
        *
        * @return   <code>true</code> if the argument's mode is OUTPUT.
        */
       public static boolean isOutput(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx);

          char mode = wa.modes.charAt(idx - 1);
          return mode == 'O';
       }

       /**
        * Check if the argument at the specified index was sent as INPUT-OUTPUT.
        *
        * @param    idx
        *           The 1-based index for the argument.
        *
        * @return   <code>true</code> if the argument's mode is INPUT-OUTPUT.
        */
       public static boolean isInputOutput(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx);

          char mode = wa.modes.charAt(idx - 1);
          return mode == 'U';
       }

       /**
        * Get the argument's type at the specified index.
        *
        * @param    idx
        *           The 1-based index for the argument.
        *
        * @return   The string representation of the argument's 4GL type.
        */
       public static String getParameterType(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx);

          String argTypeErr = null;

          switch (wa.argTypes[idx - 1])
          {
             case LegacyJavaAppserverApi.TYPE_INTEGER:
                return BaseDataType.Type.INTEGER.getTypeName();
             case LegacyJavaAppserverApi.TYPE_INT64:
                return BaseDataType.Type.INT64.getTypeName();
             case LegacyJavaAppserverApi.TYPE_DECIMAL:
                return BaseDataType.Type.DECIMAL.getTypeName();
             case LegacyJavaAppserverApi.TYPE_MEMPTR:
                return BaseDataType.Type.MEMPTR.getTypeName();
             case LegacyJavaAppserverApi.TYPE_CHARACTER:
                return BaseDataType.Type.CHARACTER.getTypeName();
             case LegacyJavaAppserverApi.TYPE_LONGCHAR:
                return BaseDataType.Type.LONGCHAR.getTypeName();
             case LegacyJavaAppserverApi.TYPE_LOGICAL:
                return BaseDataType.Type.LOGICAL.getTypeName();
             case LegacyJavaAppserverApi.TYPE_DATE:
                return BaseDataType.Type.DATE.getTypeName();
             case LegacyJavaAppserverApi.TYPE_DATETIME:
                return BaseDataType.Type.DATETIME.getTypeName();
             case LegacyJavaAppserverApi.TYPE_DATETIMETZ:
                return BaseDataType.Type.DATETIME_TZ.getTypeName();
             case LegacyJavaAppserverApi.TYPE_RECID:
                return BaseDataType.Type.RECID.getTypeName();
             case LegacyJavaAppserverApi.TYPE_RAW:
                return BaseDataType.Type.RAW.getTypeName();
             case LegacyJavaAppserverApi.TYPE_ROWID:
                return BaseDataType.Type.ROWID.getTypeName();
             case LegacyJavaAppserverApi.TYPE_DATASET_HANDLE:
                return BaseDataType.Type.DATASET_HANDLE.getTypeName();
             case LegacyJavaAppserverApi.TYPE_TABLE_HANDLE:
                return BaseDataType.Type.TABLE_HANDLE.getTypeName();

             // unsupported types
             case LegacyJavaAppserverApi.TYPE_BLOB:
                argTypeErr = "BLOB";
                break;
             case LegacyJavaAppserverApi.TYPE_CLOB:
                argTypeErr = "CLOB";
                break;
             case LegacyJavaAppserverApi.TYPE_HANDLE:
                argTypeErr = "HANDLE";
                break;
             case LegacyJavaAppserverApi.TYPE_UNKNOWN:
                argTypeErr = "UNKNOWN";
                break;
             case LegacyJavaAppserverApi.TYPE_DATASET:
                argTypeErr = "DATASET";
                break;
             case LegacyJavaAppserverApi.TYPE_TABLE:
                argTypeErr = "TABLE";
                break;

             default:
                ErrorManager.recordOrThrowError(-1, "Argument type " + wa.argTypes[idx - 1] + " is unknown.");
                return null;
          }

          ErrorManager.recordOrThrowError(-1, "Argument type " + argTypeErr + " is not supported.");

          return null;
       }

       /**
        * Invoke the target external program.
        * <p>
        * The argument types must be specified, so FWD runtime can build the proper argument for i.e.
        * TABLE-HANDLE or DATASET-HANDLE.
        *
        * @param    name
        *           The target external program.
        * @param    ptypes
        *           The argument data-types.
        * @param    args
        *           The arguments.
        */
       public static void invokeExternalProgram(String name, character[] ptypes, Object[] args)
       {
          invokeExternalProgram(new character(name), ptypes, args);
       }

       /**
        * Invoke the target external program.
        * <p>
        * The argument types must be specified, so FWD runtime can build the proper argument for i.e.
        * TABLE-HANDLE or DATASET-HANDLE.
        *
        * @param    name
        *           The target external program.
        * @param    ptypes
        *           The argument data-types.
        * @param    args
        *           The arguments.
        */
       public static void invokeExternalProgram(character name, character[] ptypes, Object[] args)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          Object[] oargs = resolveArgs(null, ptypes, args, false);
          ControlFlowOps.invokeExternalProcedure(name, false, null, false, null, null, oargs);
       }

       /**
        * Invoke the target static method in the given legacy class.
        * <p>
        * The argument types must be specified, so FWD runtime can build the proper argument for i.e.
        * TABLE-HANDLE or DATASET-HANDLE.
        *
        * @param    clsName
        *           The target legacy class name.
        * @param    modes
        *           The argument modes.
        * @param    ptypes
        *           The argument data-types.
        * @param    args
        *           The arguments.
        */
       public static Object invokeStaticMethod(String      clsName,
                                               String      name,
                                               String      modes,
                                               character[] ptypes,
                                               Object[]    args)
       {
          return invokeStaticMethod(new character(clsName), new character(name), modes, ptypes, args);
       }

       /**
        * Invoke the target static method in the given legacy class.
        * <p>
        * The argument types must be specified, so FWD runtime can build the proper argument for i.e.
        * TABLE-HANDLE or DATASET-HANDLE.
        *
        * @param    clsName
        *           The target legacy class name.
        * @param    modes
        *           The argument modes.
        * @param    ptypes
        *           The argument data-types.
        * @param    args
        *           The arguments.
        */
       public static Object invokeStaticMethod(character   clsName,
                                               String      name,
                                               String      modes,
                                               character[] ptypes,
                                               Object[]    args)
       {
          return invokeStaticMethod(clsName, new character(name), modes, ptypes, args);
       }

       /**
        * Invoke the target static method in the given legacy class.
        * <p>
        * The argument types must be specified, so FWD runtime can build the proper argument for i.e.
        * TABLE-HANDLE or DATASET-HANDLE.
        *
        * @param    clsName
        *           The target legacy class name.
        * @param    modes
        *           The argument modes.
        * @param    ptypes
        *           The argument data-types.
        * @param    args
        *           The arguments.
        */
       public static Object invokeStaticMethod(String      clsName,
                                               character   name,
                                               String      modes,
                                               character[] ptypes,
                                               Object[]    args)
       {
          return invokeStaticMethod(new character(clsName), name, modes, ptypes, args);
       }

       /**
        * Invoke the target static method in the given legacy class.
        * <p>
        * The argument types must be specified, so FWD runtime can build the proper argument for i.e.
        * TABLE-HANDLE or DATASET-HANDLE.
        *
        * @param    clsName
        *           The target legacy class name.
        * @param    modes
        *           The argument modes.
        * @param    ptypes
        *           The argument data-types.
        * @param    args
        *           The arguments.
        */
       public static Object invokeStaticMethod(character   clsName,
                                               character   name,
                                               String      modes,
                                               character[] ptypes,
                                               Object[]    args)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          Object[] oargs = resolveArgs(modes, ptypes, args, true);
          return ObjectOps.invoke(clsName, name, null, oargs);
       }

       /**
        * Invoke the target method in the given legacy class instance.
        * <p>
        * The argument types must be specified, so FWD runtime can build the proper argument for i.e.
        * TABLE-HANDLE or DATASET-HANDLE.
        *
        * @param    ref
        *           The target instance.
        * @param    name
        *           The target legacy method name.
        * @param    modes
        *           The argument modes.
        * @param    ptypes
        *           The argument data-types.
        * @param    args
        *           The arguments.
        */
       public static Object invokeMethod(object<? extends _BaseObject_> ref,
                                         String                         name,
                                         String                         modes,
                                         character[]                    ptypes,
                                         Object[]                       args)
       {
          return invokeMethod(ref, new character(name), modes, ptypes, args);
       }

       /**
        * Invoke the target method in the given legacy class instance.
        * <p>
        * The argument types must be specified, so FWD runtime can build the proper argument for i.e.
        * TABLE-HANDLE or DATASET-HANDLE.
        *
        * @param    ref
        *           The target instance.
        * @param    name
        *           The target legacy method name.
        * @param    modes
        *           The argument modes.
        * @param    ptypes
        *           The argument data-types.
        * @param    args
        *           The arguments.
        */
       public static Object invokeMethod(object<? extends _BaseObject_> ref,
                                         character                      name,
                                         String                         modes,
                                         character[]                    ptypes,
                                         Object[]                       args)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          Object[] oargs = resolveArgs(modes, ptypes, args, true);
          return ObjectOps.invoke(ref, name, null, oargs);
       }

       /**
        * Get the argument at the specified position, as a <code>TABLE-HANDLE</code>.
        *
        * @param    idx
        *           The 1-based argument index.
        *
        * @return   The argument's value.
        */
       public static handle getTableHandle(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          return (handle) toLegacy(wa, idx);
       }

       /**
        * Set the argument at the specified position, as a <code>TABLE-HANDLE</code>.
        *
        * @param    idx
        *           The 1-based argument index.
        * @param    val
        *           The argument's value.
        */
       public static void setTableHandle(int idx, handle val)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx, true);

          idx = idx - 1;

          wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_TABLE_HANDLE, val);
       }

       /**
        * Get the argument at the specified position, as a <code>DATASET-HANDLE</code>.
        *
        * @param    idx
        *           The 1-based argument index.
        *
        * @return   The argument's value.
        */
       public static handle getDataSetHandle(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          return (handle) toLegacy(wa, idx);
       }

       /**
        * Set the argument at the specified position, as a <code>DATASET-HANDLE</code>.
        *
        * @param    idx
        *           The 1-based argument index.
        * @param    val
        *           The argument's value.
        */
       public static void setDataSetHandle(int idx, handle val)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx, true);

          idx = idx - 1;

          wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_DATASET_HANDLE, val);
       }

       /**
        * Get the argument at the specified position, as a {@link memptr}.
        *
        * @param    idx
        *           The 1-based argument index.
        *
        * @return   The argument's value.
        */
       public static memptr getMemptr(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          return (memptr) toLegacy(wa, idx);
       }

       /**
        * Set the argument at the specified position, as a {@link memptr}.
        *
        * @param    idx
        *           The 1-based argument index.
        * @param    val
        *           The argument's value.
        */
       public static void setMemptr(int idx, memptr val)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx, true);

          idx = idx - 1;

          wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_MEMPTR, val);
       }

       /**
        * Get the argument at the specified position, as a {@link integer}.
        *
        * @param    idx
        *           The 1-based argument index.
        *
        * @return   The argument's value.
        */
       public static integer getInteger(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          return (integer) toLegacy(wa, idx);
       }

       /**
        * Set the argument at the specified position, as a {@link integer}.
        *
        * @param    idx
        *           The 1-based argument index.
        * @param    val
        *           The argument's value.
        */
       public static void setInteger(int idx, integer val)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx, true);

          idx = idx - 1;

          wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_INTEGER, val);
       }

       /**
        * Get the argument at the specified position, as a {@link int64}.
        *
        * @param    idx
        *           The 1-based argument index.
        *
        * @return   The argument's value.
        */
       public static int64 getInt64(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          return (int64) toLegacy(wa, idx);
       }

       /**
        * Set the argument at the specified position, as a {@link int64}.
        *
        * @param    idx
        *           The 1-based argument index.
        * @param    val
        *           The argument's value.
        */
       public static void setInt64(int idx, int64 val)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx, true);

          idx = idx - 1;

          wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_INT64, val);
       }

       /**
        * Get the argument at the specified position, as a {@link decimal}.
        *
        * @param    idx
        *           The 1-based argument index.
        *
        * @return   The argument's value.
        */
       public static decimal getDecimal(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          return (decimal) toLegacy(wa, idx);
       }

       /**
        * Set the argument at the specified position, as a {@link decimal}.
        *
        * @param    idx
        *           The 1-based argument index.
        * @param    val
        *           The argument's value.
        */
       public static void setDecimal(int idx, decimal val)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx, true);

          idx = idx - 1;

          wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_DECIMAL, val);
       }

       /**
        * Get the argument at the specified position, as a {@link character}.
        *
        * @param    idx
        *           The 1-based argument index.
        *
        * @return   The argument's value.
        */
       public static character getCharacter(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          return (character) toLegacy(wa, idx);
       }

       /**
        * Set the argument at the specified position, as a {@link character}.
        *
        * @param    idx
        *           The 1-based argument index.
        * @param    val
        *           The argument's value.
        */
       public static void setCharacter(int idx, character val)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx, true);

          idx = idx - 1;

          wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_CHARACTER, val);
       }

       /**
        * Get the argument at the specified position, as a {@link longchar}.
        *
        * @param    idx
        *           The 1-based argument index.
        *
        * @return   The argument's value.
        */
       public static longchar getLongchar(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          return (longchar) toLegacy(wa, idx);
       }

       /**
        * Set the argument at the specified position, as a {@link longchar}.
        *
        * @param    idx
        *           The 1-based argument index.
        * @param    val
        *           The argument's value.
        */
       public static void setLongchar(int idx, longchar val)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx, true);

          idx = idx - 1;

          wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_LONGCHAR, val);
       }

       /**
        * Get the argument at the specified position, as a {@link logical}.
        *
        * @param    idx
        *           The 1-based argument index.
        *
        * @return   The argument's value.
        */
       public static logical getLogical(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          return (logical) toLegacy(wa, idx);
       }

       /**
        * Set the argument at the specified position, as a {@link logical}.
        *
        * @param    idx
        *           The 1-based argument index.
        * @param    val
        *           The argument's value.
        */
       public static void setLogical(int idx, logical val)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx, true);

          idx = idx - 1;

          wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_LOGICAL, val);
       }

       /**
        * Get the argument at the specified position, as a {@link date}.
        *
        * @param    idx
        *           The 1-based argument index.
        *
        * @return   The argument's value.
        */
       public static date getDate(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          return (date) toLegacy(wa, idx);
       }

       /**
        * Set the argument at the specified position, as a {@link date}.
        *
        * @param    idx
        *           The 1-based argument index.
        * @param    val
        *           The argument's value.
        */
       public static void setDate(int idx, date val)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx, true);

          idx = idx - 1;

          wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_DATE, val);
       }

       /**
        * Get the argument at the specified position, as a {@link datetime}.
        *
        * @param    idx
        *           The 1-based argument index.
        *
        * @return   The argument's value.
        */
       public static datetime getDatetime(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          return (datetime) toLegacy(wa, idx);
       }

       /**
        * Set the argument at the specified position, as a {@link datetime}.
        *
        * @param    idx
        *           The 1-based argument index.
        * @param    val
        *           The argument's value.
        */
       public static void setDatetime(int idx, datetime val)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx, true);

          idx = idx - 1;

          wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_DATETIME, val);
       }

       /**
        * Get the argument at the specified position, as a {@link datetimetz}.
        *
        * @param    idx
        *           The 1-based argument index.
        *
        * @return   The argument's value.
        */
       public static datetimetz getDatetimetz(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          return (datetimetz) toLegacy(wa, idx);
       }

       /**
        * Set the argument at the specified position, as a {@link datetimetz}.
        *
        * @param    idx
        *           The 1-based argument index.
        * @param    val
        *           The argument's value.
        */
       public static void setDatetimetz(int idx, datetimetz val)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx, true);

          idx = idx - 1;

          wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_DATETIMETZ, val);
       }

       /**
        * Get the argument at the specified position, as a {@link recid}.
        *
        * @param    idx
        *           The 1-based argument index.
        *
        * @return   The argument's value.
        */
       public static recid getRecid(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          return (recid) toLegacy(wa, idx);
       }

       /**
        * Set the argument at the specified position, as a {@link recid}.
        *
        * @param    idx
        *           The 1-based argument index.
        * @param    val
        *           The argument's value.
        */
       public static void setRecid(int idx, recid val)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx, true);

          idx = idx - 1;

          wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_RECID, val);
       }

       /**
        * Get the argument at the specified position, as a {@link raw}.
        *
        * @param    idx
        *           The 1-based argument index.
        *
        * @return   The argument's value.
        */
       public static raw getRaw(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          return (raw) toLegacy(wa, idx);
       }

       /**
        * Set the argument at the specified position, as a {@link raw}.
        *
        * @param    idx
        *           The 1-based argument index.
        * @param    val
        *           The argument's value.
        */
       public static void setRaw(int idx, raw val)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx, true);

          idx = idx - 1;

          wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_RAW, val);
       }

       /**
        * Get the argument at the specified position, as a {@link rowid}.
        *
        * @param    idx
        *           The 1-based argument index.
        *
        * @return   The argument's value.
        */
       public static rowid getRowid(int idx)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          return (rowid) toLegacy(wa, idx);
       }

       /**
        * Set the argument at the specified position, as a {@link rowid}.
        *
        * @param    idx
        *           The 1-based argument index.
        * @param    val
        *           The argument's value.
        */
       public static void setRowid(int idx, rowid val)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx, true);

          idx = idx - 1;

          wa.arguments[idx] = toJava(wa, idx, LegacyJavaAppserverApi.TYPE_ROWID, val);
       }

       /**
        * Set the argument at the specified position.
        *
        * @param    idx
        *           The 1-based argument index.
        * @param    val
        *           The argument's value.
        */
       public static void setArgument(int idx, BaseDataType val)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          validateIndex(wa, idx, true);

          idx = idx - 1;

          wa.arguments[idx] = toJava(wa, idx, -1, val);
       }

       /**
        * Store all the OUTPUT or INPUT-OUTPUT arguments received in the given array to be transferred back to the
        * caller.
        *
        * @param    args
        *           The arguments.
        */
       public static void setArguments(Object... args)
       {
          WorkArea wa = local.get();

          securityCheck(wa);

          if (args.length != wa.arguments.length)
          {
             ErrorManager.recordOrThrowError(-1, "Argument length must be " + wa.arguments.length);
             return;
          }

          for (int i = 0; i < args.length; i++)
          {
             if (wa.boundArguments[i])
             {
                ErrorManager.recordOrShowError(-1,
                                               "Argument at index " + (i + 1) +
                                               " is already associated as parameter - skipping.",
                                               false);

                // bound arguments will be skipped.
                continue;
             }

             Object arg = args[i];
             if (arg instanceof jobject)
             {
                jobject j = (jobject) arg;
                arg = j.isUnknown() ? null : ((jobject) arg).ref();
             }

             wa.arguments[i] = toJava(wa, i, -1, arg);
          }
       }

       /**
        * For the {@link LegacyJavaAppserverClient#invokeWithArgs} call, initialize the argument state, before the
        * call was performed.
        *
        * @param    argTypes
        *           The argument types.
        * @param    modes
        *           The argument modes.
        * @param    args
        *           The arguments.
        */
       static void initializeArguments(int[] argTypes, String modes, Object[] args)
       {
          WorkArea wa = local.get();
          wa.withArgsCall = true;
          wa.argTypes = argTypes;
          wa.arguments = args;
          wa.originalArguments = new Object[args.length];
          System.arraycopy(args, 0, wa.originalArguments, 0, args.length);
          wa.boundArguments = new boolean[args.length];
          wa.modes = modes;

          // validation for unsupported types
          for (int i = 0; i < wa.arguments.length; i++)
          {
             String argTypeErr = null;

             int type = wa.argTypes[i];
             switch (type)
             {
                // complex types
                case LegacyJavaAppserverApi.TYPE_TABLE_HANDLE:
                case LegacyJavaAppserverApi.TYPE_DATASET_HANDLE:
                case LegacyJavaAppserverApi.TYPE_MEMPTR:
                // simple types
                case LegacyJavaAppserverApi.TYPE_INTEGER:
                case LegacyJavaAppserverApi.TYPE_INT64:
                case LegacyJavaAppserverApi.TYPE_DECIMAL:
                case LegacyJavaAppserverApi.TYPE_CHARACTER:
                case LegacyJavaAppserverApi.TYPE_LONGCHAR:
                case LegacyJavaAppserverApi.TYPE_LOGICAL:
                case LegacyJavaAppserverApi.TYPE_DATE:
                case LegacyJavaAppserverApi.TYPE_DATETIME:
                case LegacyJavaAppserverApi.TYPE_DATETIMETZ:
                case LegacyJavaAppserverApi.TYPE_RECID:
                case LegacyJavaAppserverApi.TYPE_RAW:
                case LegacyJavaAppserverApi.TYPE_ROWID:
                   break;

                // unsupported types
                case LegacyJavaAppserverApi.TYPE_BLOB:
                   argTypeErr = "BLOB";
                   break;
                case LegacyJavaAppserverApi.TYPE_CLOB:
                   argTypeErr = "CLOB";
                   break;
                case LegacyJavaAppserverApi.TYPE_HANDLE:
                   argTypeErr = "HANDLE";
                   break;
                case LegacyJavaAppserverApi.TYPE_UNKNOWN:
                   argTypeErr = "UNKNOWN";
                   break;
                case LegacyJavaAppserverApi.TYPE_DATASET:
                   argTypeErr = "DATASET";
                   break;
                case LegacyJavaAppserverApi.TYPE_TABLE:
                   argTypeErr = "TABLE";
                   break;

                default:
                   argTypeErr = "[undefined type " + type + "]";
                   break;
             }

             if (argTypeErr != null)
             {
                ErrorManager.recordOrThrowError(-1, "Argument type " + argTypeErr + " is not supported.");
                return;
             }
          }
       }

       /**
        * For the {@link LegacyJavaAppserverClient#invokeWithArgs} call, clear the argument state, once the call
        * has finished.
        */
       static void clearArguments()
       {
          WorkArea wa = local.get();

          // all arguments which are not bound need to be cleaned up
          for (int i = 0; i < wa.arguments.length; i++)
          {
             if (wa.boundArguments[i])
             {
                // bound arguments are cleaned automatically
                continue;
             }

             int type = wa.argTypes[i];
             switch (type)
             {
                // complex types
                case LegacyJavaAppserverApi.TYPE_TABLE_HANDLE:
                   if (wa.originalArguments[i] != wa.arguments[i])
                   {
                      // clean original first
                      TableParameter table = (TableParameter) wa.originalArguments[i];
                      handle htable = table.getTableHandle();
                      if (htable != null && htable._isValid())
                      {
                         HandleOps.delete(htable);
                      }
                   }

                   // clean argument second
                   TableParameter table = (TableParameter) wa.arguments[i];
                   handle htable = table.getTableHandle();
                   if (htable != null && htable._isValid())
                   {
                      HandleOps.delete(htable);
                   }
                   break;

                case LegacyJavaAppserverApi.TYPE_DATASET_HANDLE:
                   if (wa.originalArguments[i] != wa.arguments[i])
                   {
                      // clean original first
                      DataSetParameter dataset = (DataSetParameter) wa.originalArguments[i];
                      handle hdataset = dataset.get();
                      if (hdataset != null && hdataset._isValid())
                      {
                         HandleOps.delete(hdataset);
                      }
                   }

                   // clean argument second
                   DataSetParameter dataset = (DataSetParameter) wa.arguments[i];
                   handle hdataset = dataset.get();
                   if (hdataset != null && hdataset._isValid())
                   {
                      HandleOps.delete(hdataset);
                   }
                   break;

                case LegacyJavaAppserverApi.TYPE_MEMPTR:
                   if (wa.originalArguments[i] != wa.arguments[i])
                   {
                      // clean original first
                      ((memptr) wa.originalArguments[i]).setLength(0);
                   }

                   // clean argument second
                   ((memptr) wa.arguments[i]).setLength(0);
                   break;
             }
          }

          wa.withArgsCall = false;
          wa.argTypes = null;
          wa.arguments = null;
          wa.originalArguments = null;
          wa.boundArguments = null;
          wa.modes = null;

          for (int i = 0; i < wa.resources.size(); i++)
          {
             WrappedResource res = wa.resources.get(i);
             if (res.valid())
             {
                ErrorManager.nestedSilent(() -> ((Deletable) res).delete());
             }
          }
          wa.resources.clear();
       }

       /**
        * For a setter, convert the given argument to its representation so it can be sent back to the remote
        * side.
        *
        * @param    wa
        *           The context-local instance.
        * @param    idx
        *           The 0-based argument index.
        * @param    argType
        *           The argument type, one of the {@link LegacyJavaAppserverApi#integer()} APIs.
        * @param    arg
        *           The argument instance.
        *
        * @return   The argument, in the form where it can be stored in {@link WorkArea#arguments}, to be sent
        *           back to the remote side.
        */
       private static Object toJava(WorkArea wa, int idx, int argType, Object arg)
       {
          // TODO: extent

          if (wa.modes.charAt(idx) == 'I')
          {
             ErrorManager.recordOrShowError(-1,
                                            "INPUT argument " + (idx + 1) +
                                            " from the remote call can not be changed.",
                                            false);
             return wa.arguments[idx];
          }

          if (arg == null)
          {
             return null;
          }

          int type = wa.argTypes[idx];

          if (argType != -1 && argType != type)
          {
             ErrorManager.recordOrThrowError(-1,
                                             "Attempting to set argument as type " + argType +
                                             " but the remote parameter is of type " + type + ".");
             return arg;
          }

          switch (type)
          {
             // complex types
             case LegacyJavaAppserverApi.TYPE_TABLE_HANDLE:
                TableParameter tp = (TableParameter) wa.arguments[idx];
                tp.getResultSet().setResultSet(null);
                OutputTableHandleCopier tcopier = new OutputTableHandleCopier(tp, (handle) arg);
                tcopier.finished();
                return tp;
             case LegacyJavaAppserverApi.TYPE_DATASET_HANDLE:
                DataSetParameter dsp = (DataSetParameter) wa.arguments[idx];
                OutputDataSetHandleCopier dscopier = new OutputDataSetHandleCopier(dsp, (handle) arg);
                dscopier.finished();
                return dsp;

             case LegacyJavaAppserverApi.TYPE_MEMPTR:
                return arg;

             // simple types
             case LegacyJavaAppserverApi.TYPE_INTEGER:
             case LegacyJavaAppserverApi.TYPE_INT64:
             case LegacyJavaAppserverApi.TYPE_DECIMAL:
             case LegacyJavaAppserverApi.TYPE_CHARACTER:
             case LegacyJavaAppserverApi.TYPE_LONGCHAR:
             case LegacyJavaAppserverApi.TYPE_LOGICAL:
             case LegacyJavaAppserverApi.TYPE_DATE:
             case LegacyJavaAppserverApi.TYPE_DATETIME:
             case LegacyJavaAppserverApi.TYPE_DATETIMETZ:
             case LegacyJavaAppserverApi.TYPE_RECID:
             case LegacyJavaAppserverApi.TYPE_RAW:
             case LegacyJavaAppserverApi.TYPE_ROWID:
                return arg;
          }

          ErrorManager.recordOrThrowError(-1, "Argument type " + type + " is not supported.");

          return null;
       }

       /**
        * Get the argument at the given 1-based index in a {@link BaseDataType} form, used by the legacy converted
        * code.
        * @param    wa
        *           The context-local instance.
        * @param    idx
        *           The 1-based argument index.
        *
        * @return   See above.
        */
       private static BaseDataType toLegacy(WorkArea wa, int idx)
       {
          validateIndex(wa, idx);

          idx = idx - 1;

          // TODO: extent

          int type = wa.argTypes[idx];
          Object arg  = wa.arguments[idx];

          switch (type)
          {
             // complex types
             case LegacyJavaAppserverApi.TYPE_TABLE_HANDLE:
                handle tth = new handle();
                TemporaryBuffer.createDynamicTable((TableParameter) arg, tth, true, false);
                if (tth._isValid())
                {
                   wa.resources.add(tth.getResource());
                }
                return tth;
             case LegacyJavaAppserverApi.TYPE_DATASET_HANDLE:
                handle dsh = new handle();
                DataSet.createDynamicDataSet((DataSetParameter) arg, dsh, true, false);
                if (dsh._isValid())
                {
                   wa.resources.add(dsh.getResource());
                }
                return dsh;

             case LegacyJavaAppserverApi.TYPE_MEMPTR:
                return new memptr((memptr) arg);

             // simple types
             case LegacyJavaAppserverApi.TYPE_INTEGER:
                return new integer((integer) arg);
             case LegacyJavaAppserverApi.TYPE_INT64:
                return new int64((int64) arg);
             case LegacyJavaAppserverApi.TYPE_DECIMAL:
                return new decimal((decimal) arg);
             case LegacyJavaAppserverApi.TYPE_CHARACTER:
                return new character((character) arg);
             case LegacyJavaAppserverApi.TYPE_LONGCHAR:
                return new longchar((longchar) arg);
             case LegacyJavaAppserverApi.TYPE_LOGICAL:
                return new logical((logical) arg);
             case LegacyJavaAppserverApi.TYPE_DATE:
                return new date((date) arg);
             case LegacyJavaAppserverApi.TYPE_DATETIME:
                return new datetime((datetime) arg);
             case LegacyJavaAppserverApi.TYPE_DATETIMETZ:
                return new datetimetz((datetimetz) arg);
             case LegacyJavaAppserverApi.TYPE_RECID:
                return new recid((recid) arg);
             case LegacyJavaAppserverApi.TYPE_RAW:
                return new raw((raw) arg);
             case LegacyJavaAppserverApi.TYPE_ROWID:
                return new rowid((rowid) arg);

          }

          ErrorManager.recordOrThrowError(-1, "Argument type " + type + " is not supported.");

          return null;
       }

       /**
        * Resolve the arguments passed to {@link #invokeExternalProgram}, {@link #invokeStaticMethod} or
        * {@link #invokeMethod}, so that they can be passed to the target invocation.
        *
        * @param    modes
        *           The argument modes.
        * @param    ptypes
        *           The string-representation of the argument types.
        * @param    args
        *           The arguments.
        * @param    isOO
        *           Flag indicating if this is a OO call.
        *
        * @return   See above.
        */
       private static Object[] resolveArgs(String modes, character[] ptypes, Object[] args, boolean isOO)
       {
          if (modes != null && modes.length() != args.length)
          {
             ErrorManager.recordOrThrowError(-1, "Argument mode length and argument length does not match!");
             return args;
          }

          if (isOO && modes == null)
          {
             ErrorManager.recordOrThrowError(-1, "For a OO call, the argument modes must be specified!");
             return args;
          }

          if (modes != null)
          {
             modes = modes.toUpperCase();
             for (int i = 0; i < modes.length(); i++)
             {
                char c = modes.charAt(i);
                if (c != 'I' && c != 'U' && c != 'O')
                {
                   ErrorManager.recordOrThrowError(-1, "Invalid argument mode at position " + (i + 1) + ": " + c);
                   return args;
                }
             }
          }

          Object[] oargs = new Object[args.length];
          for (int i = 0; i < args.length; i++)
          {
             String ptype = ptypes[i].getValue();
             Object arg = args[i];
             if (arg instanceof jobject)
             {
                arg = ((jobject) arg).ref();
             }

             char pmode = (modes == null ? (char) -1 : modes.charAt(i));

             switch (ptype.toUpperCase())
             {
                // complex types
                case "TABLE-HANDLE":
                   if (isOO)
                   {
                      switch (pmode)
                      {
                         case 'I':
                            arg = new InputTableHandle((handle) arg);
                            break;
                         case 'O':
                            arg = new OutputTableHandle((handle) arg);
                            break;
                         case 'U':
                            arg = new InputOutputTableHandle((handle) arg);
                            break;
                      }
                   }
                   else
                   {
                      arg = new TableParameter((handle) arg);
                   }
                   break;
                case "DATASET-HANDLE":
                   if (isOO)
                   {
                      switch (pmode)
                      {
                         case 'I':
                            arg = new InputDataSetHandle((handle) arg);
                            break;
                         case 'O':
                            arg = new OutputDataSetHandle((handle) arg);
                            break;
                         case 'U':
                            arg = new InputOutputDataSetHandle((handle) arg);
                            break;
                      }
                   }
                   else
                   {
                      arg = new DataSetParameter((handle) arg);
                   }
                   break;

                case "MEMPTR":
                   // do nothing
                   break;

                // simple types
                case "INTEGER":
                case "INT64":
                case "DECIMAL":
                case "CHARACTER":
                case "LONGCHAR":
                case "LOGICAL":
                case "DATE":
                case "DATETIME":
                case "DATETIMETZ":
                case "DATETIME-TZ":
                case "RECID":
                case "RAW":
                case "ROWID":
                   // do nothing
                   break;

                // unsupported types
                case "BLOB":
                case "CLOB":
                case "HANDLE":
                case "UNKNOWN":
                case "DATASET":
                case "TABLE":
                   ErrorManager.recordOrThrowError(-1, "Parameter type " + ptype + " is not supported!");
                   break;

                default:
                   ErrorManager.recordOrThrowError(-1, "Parameter type " + ptype + " is unknown!");
                   break;
             }

             oargs[i] = arg;
          }
          return oargs;
       }

       /**
        * Validate the given index to be in the {@link WorkArea#arguments} range.
        *
        * @param    wa
        *           The context-local instance.
        * @param    idx
        *           The 1-based index.
        */
       private static void validateIndex(WorkArea wa, int idx)
       {
          validateIndex(wa, idx, false);
       }

       /**
        * Validate the given index to be in the {@link WorkArea#arguments} range.
        *
        * @param    wa
        *           The context-local instance.
        * @param    idx
        *           The 1-based index.
        * @param    validateBound
        *           Flag indicating if the {@link WorkArea#boundArguments} needs to be false.
        */
       private static void validateIndex(WorkArea wa, int idx, boolean validateBound)
       {
          if (idx < 1 || idx > wa.arguments.length)
          {
             ErrorManager.recordOrThrowError(-1, "LegacyOpenClientCaller: index " + idx +
                                                 " is out of range [1.." + wa.arguments.length + "].");
             return;
          }

          if (validateBound && wa.boundArguments[idx - 1])
          {
             ErrorManager.recordOrThrowError(-1, "Parameter " + idx + " is already associated as parameter.");
             return;
          }
       }

       /**
        * Check if the call is being performed in the context of the OpenClient call.  This means that
        * {@link ProcedureHelper#_thisProcedure()} must be the same as {@link ProcedureHelper#_rootProcedure()}.
        *
        * @param    wa
        *           The context-local instance.
        * @param    root
        *           Flag indicating we must be in the root external block.
        */
       private static void securityCheck(WorkArea wa)
       {
          securityCheck(wa, false);
       }

       /**
        * Check if the call is being performed in the context of the OpenClient call.  This means that
        * {@link ProcedureHelper#_thisProcedure()} must be the same as {@link ProcedureHelper#_rootProcedure()}.
        *
        * @param    wa
        *           The context-local instance.
        * @param    root
        *           Flag indicating we must be in the root external block.
        */
       private static void securityCheck(WorkArea wa, boolean root)
       {
          // all API calls must be done from the context of the root caller, and only if the root caller is
          // an 'invokeWithArgs'
          if (!wa.withArgsCall)
          {
             ErrorManager.recordOrThrowError(-1, "LegacyOpenClientCaller APIs can be accessed only if the remote " +
                                                 "call was performed via 'invokeWithArgs'!");
             return;
          }

          Object thisProc = wa.pm._thisProcedure();
          Object rootProc = wa.pm._rootProcedure();
          if (thisProc != rootProc)
          {
             ErrorManager.recordOrThrowError(-1, "LegacyOpenClientCaller APIs can be accessed only from the " +
                                                 "context of the invoked program: " + rootProc.getClass());
             return;
          }

          if (root && TransactionManager.getNestingLevel() > 3)
          {
             ErrorManager.recordOrThrowError(-1,
                                             "'asDataSetParameter' or 'asTableParameter' can be used only from " +
                                             "the root external program for the proxy call.");
             return;
          }
       }

       /**
        * Stores global data relating to the state of the current context.
        */
       private static class WorkArea
       {
          /** Helper to use the {@link ProcedureManager} without any context local lookups. */
          private ProcedureHelper pm = ProcedureManager.getProcedureHelper();

          /** Flag indicating that the root call is via the {@link LegacyJavaAppserverClient#invokeWithArgs}. */
          private boolean withArgsCall;

          /** The received argument types. */
          private int[] argTypes = null;

          /** The received argument modes. */
          private String modes = null;

          /** The received argument. */
          private Object[] arguments = null;

          /** The original references for the arguments. */
          private Object[] originalArguments = null;

          /** Flags indicating which [INPUT-]OUTPUT arameters have been bound to a 4GL converted variable. */
          private boolean[] boundArguments = null;

          /** List of resources created by {@link LegacyOpenClientCaller#toLegacy}. */
          private List<WrappedResource> resources = new ArrayList<>();
       }
    }