=== modified file 'rules/annotations/annotations_prep.xml'
--- old/rules/annotations/annotations_prep.xml	2022-04-28 12:37:22 +0000
+++ new/rules/annotations/annotations_prep.xml	2022-05-12 15:05:03 +0000
@@ -8,7 +8,7 @@
 **
 ** Copyright (c) 2018-2022, Golden Code Development Corporation.
 **
-** _#_ _I_ __Date__ ________________________________Description_________________________________
+** _#_ _I_ __Date__ ______________________________________Description_______________________________________
 ** 001 GES 20181209 First version.
 **     CA  20181213 Automatically register all builtin skeleton classes, in the 
 **                  com.goldencode.p2j.oo package followed by the legacy package without the 
@@ -26,13 +26,16 @@
 ** 005 CA  20200412 Added incremental conversion support.
 ** 006 CA  20200508 Track the USING specs for each program - needed to solve cases when the imported
 **                  class name is the same name as the program with the import statement.
-** 007 RFB 20200930 Check for a dynamic-function without an "IN", and create an "IN THIS-PROCEDURE". Ref #4861
+** 007 RFB 20200930 Check for a dynamic-function without an "IN", and create an "IN THIS-PROCEDURE". Refs
+**                  #4861.
 **     RFB 20201001 Backed out #4861 updates.
 **     RFB 20201004 Reintroduced changes, with additional safeguards and new annotation. Also made it more
 **                  robust, handling nested dynamic-functions. Ref #4861
 **     CA  20220310 Fixed 'dynfunc_in_this_procedure, the oldtype must be explicitly set, and not hard-coded.
 **     CA  20220426 If the same simple class name exists in more than one explicit import, then only the first
 **                  import using this simple class name must be emitted. 
+**     GES 20220427 Removed the unused variable mode and renamed convert_member_name to convert_method_name.
+**                  Rewrote convert_method_name to delegate most of the work to ClassDefinition.
 */
  -->
  
@@ -117,74 +120,23 @@
    <variable name="clsName"          type="java.lang.String" />
    <variable name="clsPkg"           type="java.lang.String" />
    <variable name="mkey"             type="java.lang.String" />
-   <variable name="cclass"           type="com.goldencode.p2j.uast.ConvertedClassName" />
+   <variable name="clsdef"           type="com.goldencode.p2j.uast.ClassDefinition" />
 
    <!-- pipeline of rule-sets to process -->
    
    <func-library access="public">
-      <function name="convert_member_name">
+      <function name="convert_method_name">
          <parameter name="mname"       type="java.lang.String" />
-         <parameter name="cclass"      type="com.goldencode.p2j.uast.ConvertedClassName" />
-         <parameter name="mtype"       type="java.lang.Integer" />
+         <parameter name="clsdef"      type="com.goldencode.p2j.uast.ClassDefinition" />
          <parameter name="mAst"        type="com.goldencode.ast.Aast" />
          
          <return    name="javaname"    type="java.lang.String" />
          
-         <variable  name="idx"            type="java.lang.Integer" />
-         
          <rule>ovrdNames.containsKey(mname.toLowerCase())
             <action>mname = ovrdNames.get(mname.toLowerCase())</action>
          </rule>
          
-         <!-- the ConvertedClassName manages our namespaces and has separate behavior for
-              each case; as other types are added the logic must be expanded below; the result
-              is cleaner for the caller while adding a small amount of extra code here; the
-              trade-off is worth it -->
-         
-         <rule>mtype == names.variable
-            <action>javaname = cclass.getVariable(mname)</action>
-           
-           <!-- do not track method names -->
-           <action on="false">javaname = null</action>
-         </rule>
-         
-         <rule>mtype == names.method and mAst != null
-            <action>javaname = cclass.getConvertedMethodName(mname, mAst)</action>
-         </rule>
-         
-         <!-- if not already in the legacy map, then we have a new javaname to calculate -->
-         <rule>javaname == null
-            <action>javaname = names.convert(mname, mtype)</action>
-
-            <rule>conflicts.contains(javaname)
-               <action>javaname = sprintf("%s_", javaname)</action>
-            </rule>
-            
-            <!-- make sure we don't have collisions (the legacy names can't collide but the
-                 converted javaname could -->
-            <rule>
-               (mtype == names.variable and cclass.isVariableNameConflict(javaname)) or
-               (mtype == names.method   and cclass.isMethodNameConflict(javaname, mAst))
-               
-               <action>idx = 0</action>
-               
-               <!-- compute an index such that this name is unique across all method names -->
-               <while>
-                  (mtype == names.variable and cclass.isVariableNameConflict(javaname)) or
-                  (mtype == names.method   and cclass.isMethodNameConflict(javaname, mAst))
-               
-                  <action>idx = idx + 1</action>
-                  <action>javaname = sprintf("%s_%d", javaname, idx)</action>
-               </while>
-            </rule>
-            
-            <rule>mtype == names.variable
-               <action>cclass.addVariable(mname, javaname)</action>
-            </rule>
-            <rule>mtype == names.method
-               <action>cclass.addMethod(mname, javaname, mAst)</action>
-            </rule>
-         </rule>
+         <rule>javaname = clsdef.getConvertedMethodName(mname, mAst)</rule>
       </function>
 
    </func-library>

=== modified file 'rules/annotations/method_defs.rules'
--- old/rules/annotations/method_defs.rules	2022-05-16 16:24:45 +0000
+++ new/rules/annotations/method_defs.rules	2022-05-22 12:43:27 +0000
@@ -39,6 +39,8 @@
 **                  and the javaname must be changed).
 **     CA  20220516 For extent OO properties, we need both indexed and bulk setters, plus bulk getters, 
 **                  regardless if there are explicit indexed setters/getters at the property definition.
+**     GES 20220522 Reworked to properly separate and handle the method names for the extent property bulk
+**                  getter, bulk setter and "all" setter. 
 */
 -->
 
@@ -149,10 +151,13 @@
    <variable name="childref"     type="com.goldencode.ast.Aast" />
    
    <func-library access="private">
+   
+      <!-- called with copy as KW_GET or KW_SET -->
       <function name="process_getter_setter">
-         <parameter name="mref"    type="com.goldencode.ast.Aast" />
-         <parameter name="tref"    type="com.goldencode.ast.Aast" />
-         <parameter name="indexed" type="java.lang.Boolean" />
+         <parameter name="mref"     type="com.goldencode.ast.Aast" />
+         <parameter name="tref"     type="com.goldencode.ast.Aast" />
+         <parameter name="indexed"  type="java.lang.Boolean" />
+         <variable  name="javaname" type="java.lang.String" />
          
          <rule>true
             <rule>isAbstract
@@ -233,7 +238,7 @@
             </rule>
             
             <rule>extent != null and extent != 0
-               <!-- emit the bulk setter and getter, single-value setter -->
+               <!-- emit the bulk setter, "all" setter and bulk getter -->
                <rule>indexed
                   <action>tref = mref.graftCopyTo(mref.parent, null)</action>
                   <action on="false">tref = mref</action>
@@ -242,6 +247,13 @@
                <rule>type == prog.kw_get
                   <!-- bulk getter -->
                   
+                  <!-- lookup and copy the bulk getter javaname (was deferred from naming.rules) -->
+                  <rule>copy.parent.isAnnotation("bulk-get-javaname")
+                     <action>javaname = copy.parent.getAnnotation("bulk-get-javaname")</action>
+                     <action on="false">javaname = "method_defs.rules_FIXME1"</action>
+                  </rule>
+                  <action>tref.putAnnotation("javaname", javaname)</action>
+                  
                   <!-- remove the parameter and set the extent for the return type -->
                   <rule>indexed
                      <action>eref = tref.getImmediateChild(prog.lparens, null)</action>
@@ -284,6 +296,13 @@
                <rule>type == prog.kw_set
                   <!-- bulk setter -->
                   
+                  <!-- lookup and copy the bulk setter javaname (was deferred from naming.rules) -->
+                  <rule>copy.parent.isAnnotation("bulk-set-javaname")
+                     <action>javaname = copy.parent.getAnnotation("bulk-set-javaname")</action>
+                     <action on="false">javaname = "method_defs.rules_FIXME2"</action>
+                  </rule>
+                  <action>tref.putAnnotation("javaname", javaname)</action>
+                  
                   <!-- remove the index parameter and set the extent -->
                   <rule>indexed
                      <action>eref = tref.getImmediateChild(prog.lparens, null)</action>
@@ -328,9 +347,16 @@
                      <action>eref.putAnnotation("refid", vref.id)</action>
                   </rule>
 
-                  <!-- single-value setter -->
+                  <!-- "all" setter (single element parameter is assigned to all elements)  -->
                   <action>tref = mref.graftCopyTo(mref.parent, null)</action>
 
+                  <!-- lookup and copy the bulk getter javaname (was deferred from naming.rules) -->
+                  <rule>copy.parent.isAnnotation("set-all-javaname")
+                     <action>javaname = copy.parent.getAnnotation("set-all-javaname")</action>
+                     <action on="false">javaname = "method_defs.rules_FIXME3"</action>
+                  </rule>
+                  <action>tref.putAnnotation("javaname", javaname)</action>
+                  
                   <!-- remove the index parameter and don't set the extent -->
                   <rule>indexed
                      <action>eref = tref.getImmediateChild(prog.lparens, null)</action>
@@ -572,7 +598,7 @@
             <action on="false">putNote("classname", "void")</action>
          </rule>
          
-         <!-- conversion of javaname was already done during annotations_prep -->
+         <!-- conversion of javaname was already done during parsing -->
          <action>pname = getNoteString("name")</action>
          <action>javaname = getNoteString("javaname")</action>
          

=== modified file 'rules/annotations/naming.rules'
--- old/rules/annotations/naming.rules	2022-05-16 16:24:45 +0000
+++ new/rules/annotations/naming.rules	2022-05-22 12:23:43 +0000
@@ -7,7 +7,7 @@
 **
 ** Copyright (c) 2005-2022, Golden Code Development Corporation.
 **
-** _#_ _I_ __Date__ __JPRM__ ___________________________________Description___________________________________
+** _#_ _I_ __Date__ __JPRM__ __________________________________Description__________________________________
 ** 001 SIY 20051103   @23186 Initial implementation.
 ** 002 GES 20060124   @24051 Rewrote to enhance the annotations to provide
 **                           a more correct name mapping calculation.
@@ -47,7 +47,7 @@
 **     CA  20200508          Track the USING specs for each program - needed to solve cases when 
 **                           the imported class name is the same name as the program with the 
 **                           import statement.
-**     CA  20210609          Fixed extent property used as output/input-output argument.
+** 021 CA  20210609          Fixed extent property used as output/input-output argument.
 **     ME  20211022          Send the AST node as input to loadConvertedClass call.
 **     OM  20211111          Added transaction support for finally blocks.
 **     CA  20220427          Constructors names must be aware of overload suffixes (as their Java signature
@@ -55,6 +55,9 @@
 **     CA  20220428          Non-persistent programs are emitted only at the (Sub)AppObject service.
 **     CA  20220516          For explicit setter or getter for OO properties, force the index to be int64.
 **                           Fixed 'override' for OO properties.
+**     GES 20220501          Reworked convert_member_name to be convert_method_name. Rewrote usage of
+**                           ConvertedClassName to use ClassDefinition instead. Reworked method naming for
+**                           properties with extents.
 */
  -->
  
@@ -144,10 +147,11 @@
    <variable name="mname"       type="java.lang.String" />
    <variable name="pname"       type="java.lang.String" />
    <variable name="ename"       type="java.lang.String" />
+   <variable name="propkey"     type="java.lang.String" />
    <variable name="pkgbase"     type="java.lang.String" />
    <variable name="conflicts"   type="java.util.Set" />
    <variable name="iter"        type="java.util.Iterator" />
-   <variable name="cclass"      type="com.goldencode.p2j.uast.ConvertedClassName" />
+   <variable name="clsdef"      type="com.goldencode.p2j.uast.ClassDefinition" />
    <variable name="mAst"        type="com.goldencode.ast.Aast" />
    <variable name="ref"         type="com.goldencode.ast.Aast" />
    <variable name="ref2"        type="com.goldencode.ast.Aast" />
@@ -321,13 +325,12 @@
          <!-- this needs to be done first -->
          <action>setConvertedClassName(file, ooname, fullpkg, classname)</action>
          <!-- map the source file name to the converted class naming info -->
-         <action>cclass = loadConvertedClass(file, true, this)</action>
+         <action>clsdef = loadConvertedClass(file, true, this)</action>
 
          <!-- save the converted default constructor name at the root node -->
-         <action>mname = cclass.getLegacySimpleName()</action>
-         <action>
-            javaname = execLib("convert_member_name", mname, cclass, names.method, null)
-         </action>
+         <action>javaname = clsdef.getSimpleJavaName()</action>
+         <!-- TODO: do we need to register a method for this?  A better approach would be to calculate
+                    this at parse time -->
          <rule>pkgbase.isEmpty()
             <action>javaname = sprintf("__%s_constructor__", javaname)</action>
             <action on="false">javaname = sprintf("__%s_%s_constructor__", pkgbase, javaname)</action>
@@ -339,7 +342,7 @@
       <rule>relativePath("METHOD_DEF/KW_METHOD/SYMBOL")
          <action>mname = text</action>
          <action>
-            javaname = execLib("convert_member_name", mname, cclass, names.method, copy.parent.parent)
+            javaname = execLib("convert_method_name", mname, clsdef, copy.parent.parent)
          </action>
          <action>copy.parent.putAnnotation("javaname", javaname)</action>
       </rule>
@@ -347,8 +350,10 @@
       <rule>relativePath("CONSTRUCTOR/KW_CONSTRUC")
          <!-- to resolve constructor overload clash on object parameters, disambiguate here -->
          <action>mname = getNoteString("name")</action>
+         <!-- TODO: do we need to register a method for this?  A better approach would be to calculate
+                    this at parse time -->
          <action>
-            javaname = execLib("convert_member_name", mname, cclass, names.method, copy.parent)
+            javaname = execLib("convert_method_name", mname, clsdef, copy.parent)
          </action>
          <rule>pkgbase.isEmpty()
             <action>javaname = sprintf("__%s_constructor__", javaname)</action>
@@ -364,7 +369,10 @@
          relativePath("DEFINE_PROPERTY/KW_GET")    or
          relativePath("DEFINE_PROPERTY/KW_SET")
          
-         <!-- getter and setter method names are converted here -->
+         <!-- simple getter and setter method names already exist and are copied here; bulk getter and
+              bulk/all setters don't yet exist but their names are already converted; since there is no
+              method def yet for those we will defer lookup/copy of those until they exist (see the
+              process_getter_setter function in method_defs.rules) -->
          <action>pname = copy.parent.getAnnotation("name")</action>
          <action>putNote("name", pname)</action>
 
@@ -377,14 +385,17 @@
          </rule>
             
          <rule>type == prog.kw_get
+         
+            <!-- only the simple scalar/single element getter can be handled here -->
+            <action>pname = sprintf("get-%s", pname)</action>
+            <action>propkey = "get-javaname"</action>
+            
             <rule>downPath("LPARENS/PARAMETER") and parent.downPath("KW_EXTENT")  
                <!-- force the index parameter's data type to be int64 -->
                <action>ref2 = copy.getImmediateChild(prog.lparens, null)</action>
                <action>ref2 = ref2.getImmediateChild(prog.parameter, null)</action>
                <action>ref2.putAnnotation("classname", "int64")</action>
             </rule>
-         
-            <action>pname = cclass.disambiguateLegacyPropertyName("get", pname)</action>
             <action>
                mAst.putAnnotation("return_type", #(long) parent.getAnnotation("type"))
             </action>
@@ -401,7 +412,10 @@
                </rule>
 
                <action>mAst.putAnnotation("return_type", #(long) prog.kw_void)</action>
-               <action>pname = cclass.disambiguateLegacyPropertyName("set", pname)</action>
+               
+               <!-- only the simple scalar/single element setter can be handled here -->
+               <action>pname = sprintf("set-%s", pname)</action>
+               <action>propkey = "set-javaname"</action>
 
                <action>ref = createProgressAst(prog.parameter, ref)</action>
                <action>ref.putAnnotation("type", #(long) parent.getAnnotation("type"))</action>
@@ -425,9 +439,10 @@
                <action>mAst.setHidden(true)</action>
             </rule>
          </rule>
-         <action>
-            javaname = execLib("convert_member_name", pname, cclass, names.method, mAst)
-         </action>
+         <rule>copy.parent.isAnnotation(propkey)
+            <action>javaname = copy.parent.getAnnotation(propkey)</action>
+            <action on="false">javaname = "naming.rules_FIXME1"</action>
+         </rule>
          <action>
             mAst.putAnnotation("access-mode", #(long) parent.getAnnotation("access-mode"))
          </action>
@@ -448,7 +463,8 @@
             <action>pname = getNoteString("name")</action>
             
             <!-- - extent(property) will be like a function named length-of-property() -->
-            <action>ename = cclass.disambiguateLegacyPropertyName("length-of", pname)</action>
+            <action>ename = sprintf("length-of-%s", pname)</action>
+            <action>propkey = "length-of-javaname"</action>
             <!-- we temporary attach, to build the AST - will delete once done -->
             <action>mAst = createProgressAst(prog.kw_method, copy)</action>
             <action>ref = createProgressAst(prog.lparens, mAst)</action>
@@ -458,9 +474,10 @@
             <action>ref = createProgressAst(prog.kw_as, ref)</action>
             <action>ref = createProgressAst(prog.kw_int64, ref)</action>
             <action>mAst.setHidden(true)</action>
-            <action>
-               javaname = execLib("convert_member_name", ename, cclass, names.method, mAst)
-            </action>
+            <rule>copy.isAnnotation(propkey)
+               <action>javaname = copy.getAnnotation(propkey)</action>
+               <action on="false">javaname = "naming.rules_FIXME2"</action>
+            </rule>
             <action>mAst.putAnnotation("name", ename)</action>
             <action>mAst.putAnnotation("javaname", javaname)</action>
             <action>mAst.putAnnotation("oo-data-store", "METHOD")</action>
@@ -469,13 +486,15 @@
             <action>putNote("extent-length-javaname", javaname)</action>
             
             <!-- extent(property) = size will be like a function named resize-property() -->
-            <action>ename = cclass.disambiguateLegacyPropertyName("resize", pname)</action>
+            <action>ename = sprintf("resize-%s", pname)</action>
+            <action>propkey = "resize-javaname"</action>
             <action>mAst = createProgressAst(prog.kw_method, copy)</action>
             <action>ref = createProgressAst(prog.lparens, mAst)</action>
             <action>mAst.setHidden(true)</action>
-            <action>
-               javaname = execLib("convert_member_name", ename, cclass, names.method, mAst)
-            </action>
+            <rule>copy.isAnnotation(propkey)
+               <action>javaname = copy.getAnnotation(propkey)</action>
+               <action on="false">javaname = "naming.rules_FIXME3"</action>
+            </rule>
             <action>mAst.putAnnotation("name", ename)</action>
             <action>mAst.putAnnotation("javaname", javaname)</action>
             <action>mAst.putAnnotation("oo-data-store", "METHOD")</action>
@@ -484,14 +503,6 @@
             <action>putNote("extent-resize-javaname", javaname)</action>
          </rule>
       </rule>
-      
-      <rule>this.type == prog.define_variable and upPath("CLASS_DEF/BLOCK/STATEMENT")
-         <action>
-            cclass.addVariable(getNoteString("name"), 
-                               getNoteString("javaname"), 
-                               getNoteLong("extent"))
-         </action>
-      </rule>
    </walk-rules>
 
    <post-rules>

=== modified file 'rules/annotations/oo_references.rules'
--- old/rules/annotations/oo_references.rules	2022-05-16 16:24:45 +0000
+++ new/rules/annotations/oo_references.rules	2022-05-23 19:34:04 +0000
@@ -43,6 +43,8 @@
 **     CA  20220426 Always wrap the DYNAMIC-PROPERTY's property name and index, when they are literals.
 **                  Fixed a problem when accessing a builtin OO property from a getter/setter.
 **     CA  20220516 Fixed ASSIGN EXTENT(property) = size.
+**     GES 20220501 Rewrote usage of ConvertedClassName to use ClassDefinition instead. This required
+**                  parse time name conversion for the methods we emit for properties.
 */
  -->
  
@@ -108,11 +110,12 @@
    <worker class="com.goldencode.p2j.pattern.FileOperationsWorker"       namespace="fileWorker" />
 
    <!-- variables -->
-   <variable name="nameinfo"  type="com.goldencode.p2j.uast.ConvertedClassName" />
+   <variable name="clsdef"    type="com.goldencode.p2j.uast.ClassDefinition" />
    <variable name="fname"     type="java.lang.String" />
    <variable name="pname"     type="java.lang.String" />
    <variable name="oname"     type="java.lang.String" />
    <variable name="sname"     type="java.lang.String" />
+   <variable name="propkey"   type="java.lang.String" />
    <variable name="javaname"  type="java.lang.String" />
    <variable name="usingPkg"  type="java.lang.String" />
    <variable name="pkgroot"   type="java.lang.String" />
@@ -143,6 +146,10 @@
    <variable name="pidx"      type="java.lang.Long" />
    <variable name="refid"     type="java.lang.Long" />
    <variable name="access"    type="java.lang.Long" />
+   <variable name="isStatic"     type="java.lang.Boolean" />
+   <variable name="isExtent"     type="java.lang.Boolean" />
+   <variable name="accessMode"   type="java.lang.Long" />
+   <variable name="extent"       type="java.lang.Long" />
    <variable name="usingClasses" type="java.util.Set" />
    
    <init-rules>
@@ -247,11 +254,11 @@
                </rule>
                
                <rule on="false">true
-                  <!-- explicit class import -->
+                  <!-- non-Java, explicit class import -->
                   <action>fname = getNoteString("source-file")</action>
-                  <action>nameinfo = loadConvertedClass(fname, false, this)</action>
-                  <rule>nameinfo != null
-                     <action>putNote("legacy-import", nameinfo.getQualifiedName())</action>
+                  <action>clsdef = loadConvertedClass(fname, false, this)</action>
+                  <rule>clsdef != null
+                     <action>putNote("legacy-import", clsdef.getQualifiedJavaName())</action>
                   </rule>
                </rule>
             </rule>
@@ -288,20 +295,20 @@
             isNote("qualified") and isNote("source-file")
 
          <action>fname = getNoteString("source-file")</action>
-         <action>nameinfo = loadConvertedClass(fname, false, this)</action>
+         <action>clsdef = loadConvertedClass(fname, false, this)</action>
          
-         <rule>nameinfo != null
-            <action>oname = nameinfo.getQualifiedName()</action>
+         <rule>clsdef != null
+            <action>oname = clsdef.getQualifiedJavaName()</action>
             <rule>oname.endsWith("_BaseObject_") and parent.type == prog.kw_inherits
                <action>oname = "com.goldencode.p2j.oo.lang.BaseObject"</action>
                <action>sname = "BaseObject"</action>
                
-               <action on="false">sname = nameinfo.getClassName()</action>
+               <action on="false">sname = clsdef.getSimpleJavaName()</action>
             </rule>
 
             <action>putNote("full-java-class", oname)</action>
             <action>putNote("simple-java-class", sname)</action>
-            <action>putNote("containing-package", nameinfo.getPackage())</action>
+            <action>putNote("containing-package", clsdef.getJavaPackage())</action>
             <rule>not usingClasses.contains(oname)
                <action>putNote("qualified-reference", true)</action>
             </rule>
@@ -321,13 +328,13 @@
          <action>copy.parent.putAnnotation("source-file", getNoteString("source-file"))</action>
 
          <action>fname = getNoteString("source-file")</action>
-         <action>nameinfo = loadConvertedClass(fname, false, this)</action>
+         <action>clsdef = loadConvertedClass(fname, false, this)</action>
          
-         <rule>nameinfo != null
-            <action>copy.parent.putAnnotation("full-java-class", nameinfo.getQualifiedName())</action>
-            <action>copy.parent.putAnnotation("simple-java-class", nameinfo.getClassName())</action>
-            <action>copy.parent.putAnnotation("containing-package", nameinfo.getPackage())</action>
-            <rule>not usingClasses.contains(nameinfo.getQualifiedName())
+         <rule>clsdef != null
+            <action>copy.parent.putAnnotation("full-java-class", clsdef.getQualifiedJavaName())</action>
+            <action>copy.parent.putAnnotation("simple-java-class", clsdef.getSimpleJavaName())</action>
+            <action>copy.parent.putAnnotation("containing-package", clsdef.getJavaPackage())</action>
+            <rule>not usingClasses.contains(clsdef.getQualifiedJavaName())
                <action>copy.parent.putAnnotation("qualified-reference", true)</action>
             </rule>
             <action on="false">
@@ -341,13 +348,13 @@
       
       <rule>this.type == prog.enum_value
          <action>fname = getNoteString("source-file")</action>
-         <action>nameinfo = loadConvertedClass(fname, false, this)</action>
+         <action>clsdef = loadConvertedClass(fname, false, this)</action>
          
-         <rule>nameinfo != null
-            <action>copy.putAnnotation("full-java-class", nameinfo.getQualifiedName())</action>
-            <action>copy.putAnnotation("simple-java-class", nameinfo.getClassName())</action>
-            <action>copy.putAnnotation("containing-package", nameinfo.getPackage())</action>
-            <rule>not usingClasses.contains(nameinfo.getQualifiedName())
+         <rule>clsdef != null
+            <action>copy.putAnnotation("full-java-class", clsdef.getQualifiedJavaName())</action>
+            <action>copy.putAnnotation("simple-java-class", clsdef.getSimpleJavaName())</action>
+            <action>copy.putAnnotation("containing-package", clsdef.getJavaPackage())</action>
+            <rule>not usingClasses.contains(clsdef.getQualifiedJavaName())
                <action>copy.putAnnotation("qualified-reference", true)</action>
             </rule>
             <action on="false">
@@ -479,8 +486,8 @@
             
             <rule on="false">true
                <action>fname = getNoteString("found-in-source-file")</action>
-               <action>nameinfo = loadConvertedClass(fname, false, this)</action>
-               <rule>nameinfo != null
+               <action>clsdef = loadConvertedClass(fname, false, this)</action>
+               <rule>clsdef != null
                   <rule>isNote("refid")
                      <!-- use the javaname at the refid, and not what we thing we should convert to -->
                      <action>ref = execLib("get_var", this)</action>
@@ -488,12 +495,15 @@
                         putNote("javaname", #(java.lang.String) ref.getAnnotation("javaname"))
                      </action>
                      
+                     <!-- TODO: it is not clear the exact case which depends upon this code; the previous
+                          code just did a lookup using only the legacy method name (not the signature) so the
+                          getConvertedMethodName() seems like a better approach but we need to watch this -->
                      <rule on="false">!isNote("javaname")
-                        <action>putNote("javaname", nameinfo.getMethod(this.text))</action>
+                        <action>putNote("javaname", clsdef.getConvertedMethodName(this.text, copy))</action>
                      </rule>
                   </rule>
                   
-                  <action>putNote("found-in-full-java-class", nameinfo.getQualifiedName())</action>
+                  <action>putNote("found-in-full-java-class", clsdef.getQualifiedJavaName())</action>
                   <rule>!evalLib("is_java_cls", this)
                      <action>putNote("wrap", true)</action>
                   </rule>
@@ -592,12 +602,12 @@
       <rule>this.type == prog.class_event
          <!-- annotate the event name -->
          <action>fname = getNoteString("found-in-source-file")</action>
-         <action>nameinfo = loadConvertedClass(fname, false, this)</action>
+         <action>clsdef = loadConvertedClass(fname, false, this)</action>
          <action>refid = getNoteLong("refid")</action>
          
          <rule>refid == null
             <action>propref = null</action>
-            <action>printfln("MISSING_REFID for property %s", copy.dumpTree())</action>
+            <action>printfln("MISSING_REFID for event %s", copy.dumpTree())</action>
             
             <action on="false">propref = getAst(refid)</action>
          </rule>
@@ -607,7 +617,12 @@
             <action>javaname = propref.getAnnotation("javaname")</action>
          </rule>
          <rule>javaname == null
-            <action>javaname = nameinfo.getVariable(this.text)</action>
+            <action>isStatic = isNote("static") and getNoteBoolean("static")</action>
+            <rule>isNote("access-mode")
+               <action>accessMode = getNoteLong("access-mode")</action>
+               <action on="false">accessMode = prog.kw_public</action>
+            </rule>
+            <action>javaname = clsdef.lookupVariableJavaName(this.text, accessMode, isStatic)</action>
          </rule>
          
          <action>putNote("javaname", javaname)</action>
@@ -627,14 +642,19 @@
           (copy.parent.type == prog.object_invocation and evalLib("input_argument", copy.parent)))
             
          <action>fname = getNoteString("cls-container")</action>
-         <action>nameinfo = loadConvertedClass(fname, false, this)</action>
+         <action>clsdef = loadConvertedClass(fname, false, this)</action>
          <action>refid = getNoteLong("refid")</action>
+         <action>isExtent = false</action>
          
          <rule>refid == null
             <action>propref = null</action>
-            <action>printfln("MISSING_REFID for property %s", copy.dumpTree())</action>
+            <action>printfln("MISSING_REFID for property %s", copy.dumpTree(true))</action>
+            <action>extent = copy.getAnnotation("extent")</action>
+            <action>isExtent = (extent != null and (extent == -1 or extent &gt; 0))</action>
             
             <action on="false">propref = getAst(refid)</action>
+            <action on="false">extent = propref.getAnnotation("extent")</action>
+            <action on="false">isExtent = (extent != null and (extent == -1 or extent &gt; 0))</action>
          </rule>
          
          <!-- TODO: each of the object_invocation cases below does not consider if the
@@ -653,20 +673,6 @@
             
             <rule>setter == null or setter.id != refid or propref == null
                
-               <!-- normal usage through the setter method -->
-               <rule>propref != null
-                  <action>
-                     javaname = nameinfo.getConvertedMethodName(
-                                  getReferenceNoteString(propref.id, "set-legacysignature"),
-                                  getReferenceNoteString(propref.id, "set-javasignature"))
-                  </action>
-               </rule>
-               <rule>javaname == null
-                  <action>
-                     javaname = nameinfo.getMethodForProperty("set", this.text)
-                  </action>
-               </rule>
-               
                <!-- rewrite the tree -->
                <rule>parent.type == prog.assign
                   <action>attref = copy.parent</action>
@@ -693,6 +699,7 @@
                </rule>
                <action>subref = copy.getImmediateChild(prog.lbracket, null)</action>
                <rule>subref != null
+                  <!-- normal indexed setter for an extent property -->
                   <action>idxref = subref.getChildAt(0)</action>
                   <action>subref.remove()</action>
                   <action>eref = createProgressAst(prog.expression, copy, 1)</action>
@@ -705,6 +712,26 @@
                </rule>
                <action>execLib("rewrite_prop_to_method", copy)</action>
                
+               <rule>!isExtent or subref != null
+                  <!-- normal usage through the setter method (it is not an extent prop or the lvalue
+                       has a subscript which means it is assigning a single element)-->
+                  <action>propkey = "set-javaname"</action>
+                  
+                  <!-- one of the special extent setters -->
+                  <rule on="false">evalLib("is_unsubscripted_extent", childref)
+                     <!-- replace the reference itself -->
+                     <action>propkey = "bulk-set-javaname"</action>
+                     
+                     <!-- assign multi -->
+                     <action on="false">propkey = "set-all-javaname"</action>
+                  </rule>
+               </rule>
+               
+               <rule>propref != null
+                  <action>javaname = propref.getAnnotation(propkey)</action>
+                  <action on="false">javaname = "oo_references.rules_FIXME1"</action>
+               </rule>               
+               
                <!-- direct reference, no rewriting -->
                <action on="false">javaname = setter.getAnnotation("javaname")</action>
                <action on="false">putNote("direct-property-access", true)</action>
@@ -720,16 +747,8 @@
             
             <action>putNote("property-access-type", "length-of")</action>
             <rule>propref != null
-               <action>
-                  javaname = nameinfo.getConvertedMethodName(
-                               getReferenceNoteString(propref.id, "length-of-legacysignature"),
-                               getReferenceNoteString(propref.id, "length-of-javasignature"))
-               </action>
-            </rule>
-            <rule>javaname == null
-               <action>
-                  javaname = nameinfo.getMethodForProperty("length-of", this.text)
-               </action>
+               <action>javaname = propref.getAnnotation("length-of-javaname")</action>
+               <action on="false">javaname = "oo_references.rules_FIXME2"</action>
             </rule>
             
             <!-- rewrite the tree -->
@@ -751,24 +770,15 @@
          
          <!-- EXTENT(property) = size (indeterminate extent resize) -->
          <rule>
-            (upPath("STATEMENT/ASSIGN/KW_EXTENT/OBJECT_INVOCATION")           and parent.indexPos == 0) or
-            (upPath("STATEMENT/KW_ASSIGN/ASSIGN/KW_EXTENT/OBJECT_INVOCATION") and parent.indexPos == 0) or
-            (upPath("STATEMENT/ASSIGN/KW_EXTENT")                             and copy.indexPos == 0)   or
-            (upPath("STATEMENT/KW_ASSIGN/ASSIGN/KW_EXTENT")                   and copy.indexPos == 0)
+            (upPath("STATEMENT/ASSIGN/KW_EXTENT/OBJECT_INVOCATION")           and parent.parent.indexPos == 0) or
+            (upPath("STATEMENT/KW_ASSIGN/ASSIGN/KW_EXTENT/OBJECT_INVOCATION") and parent.parent.indexPos == 0) or
+            (upPath("STATEMENT/ASSIGN/KW_EXTENT")                             and parent.indexPos == 0)        or
+            (upPath("STATEMENT/KW_ASSIGN/ASSIGN/KW_EXTENT")                   and parent.indexPos == 0)
             
             <action>putNote("property-access-type", "resize")</action>
             <rule>propref != null
-               <action>
-                  javaname = nameinfo.getConvertedMethodName(
-                               getReferenceNoteString(propref.id, "resize-legacysignature"),
-                               getReferenceNoteString(propref.id, "resize-javasignature"))
-               </action>
-            </rule>
-
-            <rule>javaname == null
-               <action>
-                  javaname = nameinfo.getMethodForProperty("resize", this.text)
-               </action>
+               <action>javaname = propref.getAnnotation("resize-javaname")</action>
+               <action on="false">javaname = "oo_references.rules_FIXME3"</action>
             </rule>
             
             <!-- rewrite the tree -->
@@ -797,23 +807,8 @@
          <!-- getter -->
          <rule>javaname == null
             <action>putNote("property-access-type", "getter")</action>
-         
+            
             <rule>getter == null or getter.id != refid or propref == null
-               
-               <!-- normal usage through the getter method -->
-               <rule>propref != null
-                  <action>
-                     javaname = nameinfo.getConvertedMethodName(
-                                  getReferenceNoteString(propref.id, "get-legacysignature"),
-                                  getReferenceNoteString(propref.id, "get-javasignature"))
-                  </action>
-               </rule>
-               
-               <rule>javaname == null
-                  <action>
-                     javaname = nameinfo.getMethodForProperty("get", this.text)
-                  </action>
-               </rule>
 
                <!-- rewrite the tree -->
                <action>subref = copy.getImmediateChild(prog.lbracket, null)</action>
@@ -825,6 +820,20 @@
                <action>execLib("rewrite_prop_to_method", copy)</action>
                <action>putNote("wrap", true)</action>
                
+               <rule>!isExtent or subref != null
+                  <!-- normal usage through the getter method (scalar/non-extent or return a single
+                       element) -->
+                  <action>propkey = "get-javaname"</action>
+                  
+                  <!-- the special extent getter that obtains the reference to the array -->
+                  <action on="false">propkey = "bulk-get-javaname"</action>
+               </rule>
+               
+               <rule>propref != null
+                  <action>javaname = propref.getAnnotation(propkey)</action>
+                  <action on="false">javaname = "oo_references.rules_FIXME4"</action>
+               </rule>
+               
                <!-- direct reference -->
                <action on="false">javaname = getter.getAnnotation("javaname")</action>
                <action on="false">putNote("direct-property-access", true)</action>
@@ -834,8 +843,9 @@
          <rule>javaname != null
             <action>putNote("javaname", javaname)</action>
             <action on="false">
-               printfln("WARNING: non-local property javaname lookup failure! %s",
-                        copy.dumpTree())
+               printfln("WARNING: non-local property javaname (%s) lookup failure! %s",
+                        propkey,
+                        copy.dumpTree(true))
             </action>
          </rule>
          
@@ -847,17 +857,8 @@
                
                <!-- save the setter name, so assignment_style_stmt_rewriting can use it -->
                <rule>propref != null
-                  <action>
-                     javaname = nameinfo.getConvertedMethodName(
-                                  getReferenceNoteString(propref.id, "set-legacysignature"),
-                                  getReferenceNoteString(propref.id, "set-javasignature"))
-                  </action>
-               </rule>
-               
-               <rule>javaname == null
-                  <action>
-                     javaname = nameinfo.getMethodForProperty("set", this.text)
-                  </action>
+                  <action>javaname = propref.getAnnotation("set-javaname")</action>
+                  <action on="false">javaname = "oo_references.rules_FIXME5"</action>
                </rule>
 
                <!-- direct reference -->
@@ -887,13 +888,21 @@
             !(isNote("cls-property") or getNoteBoolean("cls-property"))
             
          <action>fname = getNoteString("found-in-source-file")</action>
-         <action>nameinfo = loadConvertedClass(fname, false, this)</action>
+         <action>clsdef = loadConvertedClass(fname, false, this)</action>
          
          <!-- member vars -->
          <rule>!isNote("refid") and (!isNote("cls-property") or !getNoteBoolean("cls-property"))
-            <action>putNote("javaname", nameinfo.getVariable(this.text))</action>
-            <rule>nameinfo.getVariableExtent(this.text) != null
-               <action>putNote("extent", nameinfo.getVariableExtent(this.text))</action>
+            <action>isStatic = isNote("static") and getNoteBoolean("static")</action>
+            <rule>isNote("access-mode")
+               <action>accessMode = getNoteLong("access-mode")</action>
+               <action on="false">accessMode = prog.kw_public</action>
+            </rule>
+            <action>
+               putNote("javaname", clsdef.lookupVariableJavaName(this.text, accessMode, isStatic))
+            </action>
+            <action>extent = clsdef.lookupVariableExtent(this.text, accessMode, isStatic)</action>
+            <rule>extent != 0
+               <action>putNote("extent", extent)</action>
             </rule>
             
             <rule on="false">isNote("refid")
@@ -904,7 +913,7 @@
             </rule>
          </rule>
          
-         <action>putNote("found-in-full-java-class", nameinfo.getQualifiedName())</action>
+         <action>putNote("found-in-full-java-class", clsdef.getQualifiedJavaName())</action>
       </rule>
    </walk-rules>
    

=== modified file 'rules/annotations/output_parameters.rules'
--- old/rules/annotations/output_parameters.rules	2022-05-14 17:02:52 +0000
+++ new/rules/annotations/output_parameters.rules	2022-05-20 11:57:12 +0000
@@ -128,6 +128,8 @@
    <variable name="idx"            type="java.lang.Integer" />
    <variable name="ptype"          type="java.lang.Integer" />
    <variable name="refid"          type="java.lang.Long" />
+   <variable name="deftype"        type="java.lang.String" />
+   <variable name="calltype"       type="java.lang.String" />
    <variable name="assign_back"    type="java.lang.Boolean" />
    <variable name="needs_value"    type="java.lang.Boolean" />
    
@@ -274,6 +276,7 @@
             </rule>
          </rule>
          
+         <!-- lref will only be non-null for extent cases -->
          <rule>lref != null and 
                (lref.type == prog.kw_in_out or 
                 lref.type == prog.kw_output or 
@@ -282,23 +285,41 @@
             <action>putNote("extent", #(long) ref.getAnnotation("extent"))</action>
             <action>ref.putAnnotation("force_extent_mutable", true)</action>
             
+            <action>calltype = null</action>
+            
             <rule>!evalLib("fieldtype", type)
                <rule>isNote("full-java-class")
                   <action>
-                     putNote("outputExtentType", 
-                             sprintf("object&lt;? extends %s&gt;", 
-                                     ref.getAnnotation("full-java-class")))
+                     deftype = sprintf("object&lt;? extends %s&gt;", ref.getAnnotation("full-java-class"))
                   </action>
 
-                  <action on="false">
-                     putNote("outputExtentType", #(java.lang.String) ref.getAnnotation("classname"))
-                  </action>
+                  <!-- override type to match definition signature -->
+                  <rule on="false">isNote("classname")
+                     <action>deftype = getNoteString("classname")</action>
+                     <action>calltype = getNoteString("calltype")</action>
+                     <action on="false">deftype = #(java.lang.String) ref.getAnnotation("classname")</action>
+                     <action on="false">calltype = #(java.lang.String) ref.getAnnotation("calltype")</action>
+                  </rule>
                </rule>
                
-               <action on="false">
-                  putNote("outputExtentType", 
-                          #(java.lang.String) execLib("wrapper_name_for_field_type", type))
-               </action>
+               <rule on="false">ref.isAnnotation("classname")
+                  <!-- override type to match definition signature -->
+                  <action>
+                     deftype = #(java.lang.String) ref.getAnnotation("classname")
+                  </action>
+                  <action on="false">
+                     calltype = #(java.lang.String) ref.getAnnotation("calltype")
+                  </action>
+                  
+                  <!-- use the field type -->
+                  <action on="false">
+                     deftype = #(java.lang.String) execLib("wrapper_name_for_field_type", type)
+                  </action>
+               </rule>
+            </rule>
+            <action>putNote("outputExtentType", deftype)</action>
+            <rule>calltype != null
+               <action>putNote("calltype", calltype)</action>
             </rule>
 
             <rule>lref.id != this.id

=== modified file 'rules/annotations/record_scoping_prep.rules'
--- old/rules/annotations/record_scoping_prep.rules	2022-05-16 12:34:34 +0000
+++ new/rules/annotations/record_scoping_prep.rules	2022-05-20 12:08:20 +0000
@@ -6,7 +6,7 @@
 **
 ** Copyright (c) 2005-2022, Golden Code Development Corporation.
 **
-** _#_ _I_ __Date__ __JPRM__ ___________________________________Description___________________________________
+** _#_ _I_ __Date__ __JPRM__ __________________________________Description__________________________________
 ** 001 GES 20050919   @22810 Preparation for record scoping analysis. In
 **                           particular, all database references are
 **                           classified into the type (weak, strong,
@@ -112,11 +112,12 @@
 **     CA  20220225          A 'DEFINE BUFFER book FOR book' statement (where the buffer name is the same as 
 **                           the table name) inside an internal top-level block will be dropped if there is a
 **                           reference for this buffer, appearing somewhere before this statement.
-*     CA  20220425           Refactored to eliminate the dependency on '-1' reference ID for resolving a  
+**     CA  20220425           Refactored to eliminate the dependency on '-1' reference ID for resolving a  
 **                           buffer's reference scope - the default buffer will be referenced instead, and a 
 **                           special annotation possible_needs_buffer will be used to track the previous 
 **                           requirements of '-1' refid.
-**    CA  20220516           'b4temporary' is a boolean, not string, annotation.
+**     CA  20220516          'b4temporary' is a boolean, not string, annotation.
+**     GES 20210506          Use TableParameter subclasses when needed.
 */
 -->
 
@@ -199,6 +200,7 @@
    <variable name="extra"            type="java.lang.String" />
    <variable name="pextra"           type="java.lang.String" />
    <variable name="bufRefKey"        type="java.lang.String" />
+   <variable name="tabParmType"      type="java.lang.String" />
    <variable name="ref"              type="com.goldencode.ast.Aast" />
    <variable name="master"           type="com.goldencode.p2j.convert.BufferList" />
    <variable name="ref2"             type="com.goldencode.ast.Aast" />
@@ -287,7 +289,20 @@
                   
                   <!-- lookup and store the DMO class/package names -->
                   <rule>bref.parent.type == prog.kw_table
-                     <action>bref.putAnnotation("bufclassname", "TableParameter")</action>
+                  
+                     <!-- lookup the TableParameter subclass that should be used, default to the base class
+                          if no jtype annotation exists -->
+                     <action>
+                        tabParmType = #(java.lang.String) execLib("read_jtype",
+                                                                  bref.parent,
+                                                                  "TableParameter",
+                                                                  "record_scoping_prep")
+                     </action>
+                  
+                     <!-- record the proper TableParameter or TableParameter subclass -->
+                     <action>
+                        bref.putAnnotation("bufclassname", tabParmType)
+                     </action>
                      
                      <rule>bref.type == prog.buffer
                         <action>refid = lookupDictionaryLong("buf_ids", bufName)</action>

=== modified file 'rules/convert/buffer_definitions.rules'
--- old/rules/convert/buffer_definitions.rules	2022-04-26 13:04:51 +0000
+++ new/rules/convert/buffer_definitions.rules	2022-05-12 15:24:44 +0000
@@ -4,7 +4,7 @@
 ** Module   : buffer_definitions.rules
 ** Abstract : converts implicit and explicit buffer definitions
 **
-** Copyright (c) 2004-2021, Golden Code Development Corporation.
+** Copyright (c) 2004-2022, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ____________________________Description_____________________________
 ** 001 GES 20051005   @22993 First version which converts Progress
@@ -179,6 +179,7 @@
 **     CA  20220208          External programs can have buffer parameters, but only for permanent tables, and
 **                           these need to be promoted.
 **     CA  20220426          Added RCODE-INFO:TABLE-LIST conversion support.
+**     GES 20220502          Reworked TableParameter to use subclasses.
 */
 -->
 
@@ -304,6 +305,7 @@
    <variable name="dbAliases"     type="java.util.Set" />
    <variable name="tableList"     type="java.util.Set" />
    <variable name="iter"          type="java.util.Iterator" />
+   <variable name="usesSubclass"     type="java.lang.Boolean" />
    
    <!-- functions "depot_stub" and "buffer_open_scope_anchor" moved to common-convert.rules. -->
    <include name="convert/common-convert" />
@@ -518,21 +520,29 @@
                         <action>bufjname = sprintf("%s.get()", bufjname)</action>
                      </rule>
                      
-                     <rule>parent.parent.type == prog.define_parameter
-                        <action>parmtype = #(long) parent.parent.getChildAt(0).type</action>
-                        <action on="false">
-                           parmtype = #(long) parent.parent.getAnnotation("parmtype")
-                        </action>
-                     </rule>
-                     
-                     <rule>parmtype == prog.kw_input or parmtype == prog.kw_in_out
-                        <action>parmin = "BOOL_TRUE"</action>
-                        <action on="false">parmin = "BOOL_FALSE"</action>
-                     </rule>
-                     
-                     <rule>parmtype == prog.kw_output or parmtype == prog.kw_in_out
-                        <action>parmout = "BOOL_TRUE"</action>
-                        <action on="false">parmout = "BOOL_FALSE"</action>
+                     <!-- functions and procedures use the base class TableParameter, OO methods use
+                          a TableParameter subclass; the subclass cases use simpler forms below -->
+                     <action>
+                        usesSubclass = (parent.parent.type == prog.parameter    and
+                                        parent.parent.isAnnotation("isOO")      and
+                                        #(java.lang.Boolean) parent.parent.getAnnotation("isOO"))
+                     </action>
+                     
+                     <rule>!usesSubclass
+                        <rule>parent.parent.type == prog.define_parameter
+                           <action>parmtype = #(long) parent.parent.getChildAt(0).type</action>
+                           <action on="false">
+                              parmtype = #(long) parent.parent.getAnnotation("parmtype")
+                           </action>
+                        </rule>
+                        <rule>parmtype == prog.kw_input or parmtype == prog.kw_in_out
+                           <action>parmin = "BOOL_TRUE"</action>
+                           <action on="false">parmin = "BOOL_FALSE"</action>
+                        </rule>
+                        <rule>parmtype == prog.kw_output or parmtype == prog.kw_in_out
+                           <action>parmout = "BOOL_TRUE"</action>
+                           <action on="false">parmout = "BOOL_FALSE"</action>
+                        </rule>
                      </rule>
                      <!-- the second argument to TemporaryBuffer.associate is the destination DMO;
                           if it has the same name as the first argument (the table parameter),
@@ -540,13 +550,21 @@
                      <rule>getNoteBoolean("qualify-outer")
                         <action>bufjname = sprintf("%s.this.%s", baseClass, bufjname)</action>
                      </rule>
-                     <action>
-                        lastthparam = tw.graft("associate_temp_tables", null, promid,
-                                               "javaname1", javaname,
-                                               "javaname2", bufjname,
-                                               "input",     parmin,
-                                               "output",    parmout)
-                     </action>
+                     
+                     <rule>usesSubclass
+                        <action>
+                           lastthparam = tw.graft("associate_temp_tables", null, promid,
+                                                  "javaname1", javaname,
+                                                  "javaname2", bufjname)
+                        </action>
+                        <action on="false">
+                           lastthparam = tw.graft("associate_temp_tables_full", null, promid,
+                                                  "javaname1", javaname,
+                                                  "javaname2", bufjname,
+                                                  "input",     parmin,
+                                                  "output",    parmout)
+                        </action>
+                     </rule>
                      <action>evalLib("setPassingModes", copy.parent.parent, lastthparam)</action>
                      
                      <rule on="false">true
@@ -1120,18 +1138,28 @@
                javaname = #(java.lang.String) parent.getAnnotation("javaname")
             </action>
          </rule>
+
+         <!-- functions and procedures use the base class TableParameter, OO methods use
+              a TableParameter subclass; the subclass cases use simpler forms below -->
+         <action>
+            usesSubclass = (parent.type == prog.parameter    and
+                            parent.isAnnotation("isOO")      and
+                            #(java.lang.Boolean) parent.getAnnotation("isOO"))
+         </action>
          
-         <rule>parent.type == prog.define_parameter
-            <action>parmtype = #(long) parent.getChildAt(0).type</action>
-            <action on="false">parmtype = #(long) parent.getAnnotation("parmtype")</action>
-         </rule>
-         <rule>parmtype == prog.kw_input or parmtype == prog.kw_in_out
-            <action>parmin = "BOOL_TRUE"</action>
-            <action on="false">parmin = "BOOL_FALSE"</action>
-         </rule>
-         <rule>parmtype == prog.kw_output or parmtype == prog.kw_in_out
-            <action>parmout = "BOOL_TRUE"</action>
-            <action on="false">parmout = "BOOL_FALSE"</action>
+         <rule>!usesSubclass         
+            <rule>parent.type == prog.define_parameter
+               <action>parmtype = #(long) parent.getChildAt(0).type</action>
+               <action on="false">parmtype = #(long) parent.getAnnotation("parmtype")</action>
+            </rule>
+            <rule>parmtype == prog.kw_input or parmtype == prog.kw_in_out
+               <action>parmin = "BOOL_TRUE"</action>
+               <action on="false">parmin = "BOOL_FALSE"</action>
+            </rule>
+            <rule>parmtype == prog.kw_output or parmtype == prog.kw_in_out
+               <action>parmout = "BOOL_TRUE"</action>
+               <action on="false">parmout = "BOOL_FALSE"</action>
+            </rule>
          </rule>
          <rule>lastthparam == null
             <action>ref2 = getAst(promid)</action>
@@ -1140,13 +1168,22 @@
             <action on="false">ref2 = lastthparam.parent</action>
             <action on="false">idx = lastthparam.indexPos + 1</action>
          </rule>
-         <action>
-            lastthparam = tw.graftAt("create_dynamic_tt", null, ref2, idx,
-                                     "javaname1", sprintf("_%s", javaname),
-                                     "javaname2", javaname,
-                                     "input", parmin,
-                                     "output", parmout)
-         </action>
+         
+         <rule>usesSubclass
+            <action>
+               lastthparam = tw.graftAt("create_dynamic_tt", null, ref2, idx,
+                                        "javaname1", sprintf("_%s", javaname),
+                                        "javaname2", javaname)
+            </action>
+            <action on="false">
+               lastthparam = tw.graftAt("create_dynamic_tt_full", null, ref2, idx,
+                                        "javaname1", sprintf("_%s", javaname),
+                                        "javaname2", javaname,
+                                        "input", parmin,
+                                        "output", parmout)
+            </action>
+         </rule>
+         
          <action>evalLib("setPassingModes", copy.parent, lastthparam)</action>
          
          <action>createImport("com.goldencode.p2j.persist.*")</action>

=== modified file 'rules/convert/common-convert.rules'
--- old/rules/convert/common-convert.rules	2021-12-23 02:03:54 +0000
+++ new/rules/convert/common-convert.rules	2022-05-12 15:03:49 +0000
@@ -5,17 +5,18 @@
 ** Module   : common-convert.rules
 ** Abstract : library of callable specific functions/rules in the code conversion iteration.
 **
-** Copyright (c) 2005-2020, Golden Code Development Corporation.
+** Copyright (c) 2005-2021, Golden Code Development Corporation.
 **
-** _#_ _I_ __Date__ ________________________________ Description _________________________________
+** _#_ _I_ __Date__ _____________________________________ Description ______________________________________
 ** 001 OM  20131128 Initial commit.
 ** 002 VIG 20160624 Added function "collect_ui_strings".
 ** 003 GES 20181217 Added a comment.
 ** 004 OM  20190403 Moved in buffer_open_scope_anchor from buffer_definitions.rules.
 ** 005 OM  20190523 Added setPassingModes() and add_bool_param() functions.
 ** 006 CA  20200412 Added incremental conversion support.
-** 007 CA  20200503 ParameterOption enums are qualified and not imported, as it collides 
-**                  character.valueOf with.
+** 007 CA  20200503 ParameterOption enums are qualified and not imported, as it collides with
+**                  character.valueOf.
+** 008 GES 20210430 Moved append flag from an unconditional boolean flag to a ParameterOption enum.
 */
 -->
 
@@ -220,7 +221,7 @@
       </function>
       
       <!-- optionally adds parameter passing mode for complex data types to target JAST method
-           call node: APPEND as a boolean, BY-VALUE, BY-REFERENCE and BIND as excluding enum.
+           call node: APPEND, BY-VALUE, BY-REFERENCE or BIND as mutually exclusive enum;
            can't import static, as enum's valueOf conflicts with character valueOf -->
       <function name="setPassingModes">
          <parameter name="source"  type="com.goldencode.ast.Aast" />
@@ -237,9 +238,10 @@
          <rule>isBind = source.isAnnotation("tt_ds_bind")</rule>
          
          <rule>isAppend or isByVal or isBind or isByRef
-            <!-- add APPEND flag regardless of other flags -->
-            <action>evalLib("add_bool_param", target, isAppend)</action>
-            
+            <!-- add APPEND flag -->
+            <rule>isAppend
+               <action>createJavaAst(java.reference, "ParameterOption.APPEND", target)</action>
+            </rule>            
             <!-- add BY-VALUE flag -->
             <rule>isByVal
                <action>createJavaAst(java.reference, "ParameterOption.BY_VALUE", target)</action>

=== modified file 'rules/convert/database_references.rules'
--- old/rules/convert/database_references.rules	2022-05-14 17:02:52 +0000
+++ new/rules/convert/database_references.rules	2022-05-20 12:08:38 +0000
@@ -183,6 +183,7 @@
 **     CA  20220514          Added 'dynamic_set_parameter_last_argument' - this is required to properly
 **                           distinguish the parameter in a ParameterList:setParameter, as this OO method call
 **                           can have the INPUT/OUTPUT/INPUT-OUTPUT modes set.
+**     GES 20210519          Reworked table parameters to use subclasses of TableParameter.
 */
 -->
 
@@ -279,43 +280,44 @@
    </func-library>
    
    <!-- variables -->
-   <variable name="casttype"    type="java.lang.String" />
-   <variable name="javaname"    type="java.lang.String" />
-   <variable name="clsname"     type="java.lang.String" />
-   <variable name="txt"         type="java.lang.String" />
-   <variable name="methodtxt"   type="java.lang.String" />
-   <variable name="bufname"     type="java.lang.String" />
-   <variable name="wrapimp"     type="java.lang.Boolean" />
-   <variable name="finaltxt"    type="java.lang.String" />
-   <variable name="fieldtype"   type="java.lang.String" />
-   <variable name="partype"     type="java.lang.String" />
-   <variable name="methodtype"  type="java.lang.Integer" />
-   <variable name="idx"         type="java.lang.Integer" />
-   <variable name="astid"       type="java.lang.Long" />
-   <variable name="lastid"      type="java.lang.Long" />
-   <variable name="origid"      type="java.lang.Long" />
-   <variable name="deferid"     type="java.lang.Long" />
-   <variable name="paridx"      type="java.lang.Long" />
-   <variable name="builtin"     type="java.lang.Boolean" />
-   <variable name="inmsg"       type="java.lang.Boolean" />
-   <variable name="insetp"      type="java.lang.Boolean" />
-   <variable name="special"     type="java.lang.Boolean" />
-   <variable name="suppress"    type="java.lang.Boolean" />
-   <variable name="accessor"    type="java.lang.Boolean" />
-   <variable name="assign_back" type="java.lang.Boolean" />
-   <variable name="needs_value" type="java.lang.Boolean" />
-   <variable name="fieldrefimp" type="java.lang.Boolean" />
-   <variable name="inline"      type="java.lang.Boolean" />
-   <variable name="isAccum"     type="java.lang.Boolean" />
-   <variable name="ref"         type="com.goldencode.ast.Aast" />
-   <variable name="ref2"        type="com.goldencode.ast.Aast" />
-   <variable name="lref"        type="com.goldencode.ast.Aast" />
-   <variable name="lref2"       type="com.goldencode.ast.Aast" />
-   <variable name="parref"      type="com.goldencode.ast.Aast" />
-   <variable name="scopeRef"    type="com.goldencode.ast.Aast" />
-   <variable name="usedByExprs" type="java.util.Set" />
-   <variable name="is_extent"   type="java.lang.Boolean" />
-   <variable name="isTableHandle" type="java.lang.Boolean" />
+   <variable name="casttype"                    type="java.lang.String" />
+   <variable name="javaname"                    type="java.lang.String" />
+   <variable name="tabParmType"                 type="java.lang.String" />
+   <variable name="clsname"                     type="java.lang.String" />
+   <variable name="txt"                         type="java.lang.String" />
+   <variable name="methodtxt"                   type="java.lang.String" />
+   <variable name="bufname"                     type="java.lang.String" />
+   <variable name="wrapimp"                     type="java.lang.Boolean" />
+   <variable name="finaltxt"                    type="java.lang.String" />
+   <variable name="fieldtype"                   type="java.lang.String" />
+   <variable name="partype"                     type="java.lang.String" />
+   <variable name="methodtype"                  type="java.lang.Integer" />
+   <variable name="idx"                         type="java.lang.Integer" />
+   <variable name="astid"                       type="java.lang.Long" />
+   <variable name="lastid"                      type="java.lang.Long" />
+   <variable name="origid"                      type="java.lang.Long" />
+   <variable name="deferid"                     type="java.lang.Long" />
+   <variable name="paridx"                      type="java.lang.Long" />
+   <variable name="builtin"                     type="java.lang.Boolean" />
+   <variable name="inmsg"                       type="java.lang.Boolean" />
+   <variable name="insetp"                      type="java.lang.Boolean" />
+   <variable name="special"                     type="java.lang.Boolean" />
+   <variable name="suppress"                    type="java.lang.Boolean" />
+   <variable name="accessor"                    type="java.lang.Boolean" />
+   <variable name="assign_back"                 type="java.lang.Boolean" />
+   <variable name="needs_value"                 type="java.lang.Boolean" />
+   <variable name="fieldrefimp"                 type="java.lang.Boolean" />
+   <variable name="inline"                      type="java.lang.Boolean" />
+   <variable name="isAccum"                     type="java.lang.Boolean" />
+   <variable name="ref"                         type="com.goldencode.ast.Aast" />
+   <variable name="ref2"                        type="com.goldencode.ast.Aast" />
+   <variable name="lref"                        type="com.goldencode.ast.Aast" />
+   <variable name="lref2"                       type="com.goldencode.ast.Aast" />
+   <variable name="parref"                      type="com.goldencode.ast.Aast" />
+   <variable name="scopeRef"                    type="com.goldencode.ast.Aast" />
+   <variable name="usedByExprs"                 type="java.util.Set" />
+   <variable name="is_extent"                   type="java.lang.Boolean" />
+   <variable name="isTableHandle"               type="java.lang.Boolean" />
    <variable name="lval_of_embedded_assignment" type="java.lang.Boolean" />
    
    <!-- main processing (once per tree) -->
@@ -372,6 +374,12 @@
          <!-- dereference the Java name for the buffer -->
          <action>javaname = execLib("get_javaname", copy)</action>
          
+         <!-- lookup the TableParameter subclass that should be used, default to the base class if no
+              jtype annotation exists -->
+         <action>
+            tabParmType = execLib("read_jtype", copy, "TableParameter", "database_references 1")
+         </action>
+         
          <action>isTableHandle = false</action>
          <action>parref = parent.parent</action>
          <rule>(relativePath("COLON/KW_TEMP_TAB/TEMP_TABLE") or
@@ -387,7 +395,7 @@
          </rule>
 
          <!-- emit the buffer reference (or TableParameter if we pass a TABLE parameter to
-              procedure/function) -->
+              procedure/function, or TableParameter subclass if passed to an OO method) -->
          <rule>(relativePath("KW_TABLE/TEMP_TABLE") or
                 relativePath("KW_TABLE/BUFFER")     or
                 isTableHandle) and
@@ -412,8 +420,19 @@
                   <action>partype = parref.getAnnotation("typelist", paridx)</action>
                   <rule>partype == "TABLE-HANDLE"
                      <action>lref = getAst(closestPeerId)</action>
-                     <action>
-                        lref2 = createJavaAst(java.constructor, "TableParameter", lref.parent, lref.indexPos)
+                     <!-- lookup the TableParameter subclass that should be used, default to the base class
+                          if no jtype annotation exists -->
+                     <action>
+                        tabParmType = #(java.lang.String) execLib("read_jtype",
+                                                                  ref2,
+                                                                  "TableParameter",
+                                                                  "database_references 2")
+                     </action>
+                     <action>
+                        lref2 = createJavaAst(java.constructor,
+                                              tabParmType,
+                                              lref.parent,
+                                              lref.indexPos)
                      </action>
                      <action>lref.move(lref2, 0)</action>
                      <action>lastid = lref.id</action>
@@ -432,10 +451,14 @@
                <rule on="false">
                   ref.isAnnotation("static") and getReferenceNoteBoolean(ref.id, "static")
                   <action>
-                     ref = tw.graft("table_param_static", copy, closestPeerId, "javaname", javaname)
+                     ref = tw.graft("table_param_static", copy, closestPeerId,
+                                    "tab-parm-type", tabParmType,
+                                    "javaname", javaname)
                   </action>
                   <action on="false">
-                     ref = tw.graft("table_param", copy, closestPeerId, "javaname", javaname)
+                     ref = tw.graft("table_param", copy, closestPeerId,
+                                    "tab-parm-type", tabParmType,
+                                    "javaname", javaname)
                   </action>
                </rule>
             </rule>
@@ -1001,9 +1024,11 @@
                   
                   <!-- for wrapped fields we need to pass in the datatype class as 3rd param -->
                   <rule>assign_back
-                     <rule>isNote("classname")
-                        <!-- some cases for OO method arg requires classname override -->
-                        <action>fieldtype = getNoteString("classname")</action>
+                     <!-- OO fields use generics and that can be directly emitted as a class -->
+                     <rule>type == prog.field_class
+                        <!-- no cast to Class<object<? extends _BaseObject_>> is needed here to compile the
+                             result; if the cast is attempted it won't compile -->
+                        <action>fieldtype = "object"</action>
                      </rule>
                      
                      <action>

=== modified file 'rules/convert/datasets.rules'
--- old/rules/convert/datasets.rules	2022-05-03 18:14:41 +0000
+++ new/rules/convert/datasets.rules	2022-05-12 15:28:21 +0000
@@ -6,7 +6,7 @@
 **
 ** Copyright (c) 2019-2022, Golden Code Development Corporation.
 **
-** _#_ _I_ __Date__ _________________________________Description_________________________________
+** _#_ _I_ __Date__ _______________________________________Description_______________________________________
 ** 001 OM  20190327 First commit.
 ** 002 OM  20190305 Fixed DataSetParameter/dataset-handle issue.
 ** 003 CA  20190514 Fixed conversion of DATASET and DATA-SOURCE class members (access-mode and
@@ -20,7 +20,9 @@
 **                  APIs, as these modes are optional for function or OO method definitions.
 ** 006 CA  20200503 ParameterOption enums are qualified and not imported, as it collides 
 **                  character.valueOf with.
-** 067 CA  20220426 Fixed conversion of DATASET arguments at NEW or DYNAMIC-NEW.
+** 007 GES 20210506 Reworked emit of parameters/arguments to use subclasses of DataSetParameter. Removed
+**                  unnecessary boolean parameters to associate() and createDynamicDataSet().
+**     CA  20220426 Fixed conversion of DATASET arguments at NEW or DYNAMIC-NEW.
 */
 -->
 
@@ -99,6 +101,7 @@
    <variable name="parmtype"      type="java.lang.Long" />
    <variable name="javaName"      type="java.lang.String" />
    <variable name="thisName"      type="java.lang.String" />
+   <variable name="dsetParmType"  type="java.lang.String" />
    <variable name="toplvlref"     type="com.goldencode.ast.Aast" />
    <variable name="dsref"         type="com.goldencode.ast.Aast" />
    <variable name="ref"           type="com.goldencode.ast.Aast" />
@@ -135,9 +138,17 @@
                <action>lastid = #(long) execLib("get_parmroot", copy.getAncestor(2))</action>
                <action on="false">lastid = closestPeerId</action>
             </rule>
+            
+            <!-- lookup the DataSetParameter subclass that should be used, use the base class for
+                 functions or procedures (but we must use a subclass for OO methods) -->
             <action>
-               lastid = createJavaAst(java.reference_def, "DataSetParameter", lastid))
+               dsetParmType = execLib("read_jtype",
+                                      copy.parent.parent,
+                                      "DataSetParameter",
+                                      "datasets 1")
             </action>
+            
+            <action>lastid = createJavaAst(java.reference_def, dsetParmType, lastid))</action>
             <action>ref = getAst(lastid)</action>
             <action>ref.putAnnotation("name", getNoteString("javaname"))</action>
             <action>ref.putAnnotation("final", true)</action>
@@ -167,8 +178,10 @@
                   createJavaAst(java.reference, sprintf("%s.this", thisName), lastid)
                </action>
             </rule>
-            
+
             <action>ref2 = copy.parent.parent</action>
+            
+            <!-- procedures use the base class so we need the extra booleans -->
             <rule>ref2.type == prog.define_parameter
                <!-- add input flag -->
                <action>
@@ -183,8 +196,14 @@
                           ref2.getImmediateChild(prog.kw_output, null) != null)
                </action>
             </rule>
-            
-            <rule>ref2.type == prog.parameter
+               
+            <!-- functions use the base class so they also need the extra boolean parms but we must avoid
+                 the OO cases -->
+            <rule>
+               ref2.type == prog.parameter   and
+               ref2.isAnnotation("isOO")     and
+               !(#(java.lang.Boolean) ref2.getAnnotation("isOO"))
+               
                <action>parmtype = #(long) ref2.getAnnotation("parmtype")</action>
                
                <!-- add input flag -->
@@ -247,7 +266,8 @@
          </action>
          <action>createPeerAst(java.reference, sprintf("_%s", javaName), ref)</action>
          <action>createPeerAst(java.reference, javaName, ref)</action>
-         
+
+         <!-- procedures use the base class so we need the extra booleans -->
          <rule>parent.type == prog.define_parameter
             <!-- add input flag -->
             <action>
@@ -263,7 +283,13 @@
             </action>
          </rule>
          
-         <rule>parent.type == prog.parameter
+         <!-- functions use the base class so they also need the extra boolean parms but we must avoid
+              the OO cases -->
+         <rule>
+            parent.type == prog.parameter   and
+            parent.isAnnotation("isOO")     and
+            !(#(java.lang.Boolean) parent.getAnnotation("isOO"))
+            
             <action>parmtype = #(long) parent.getAnnotation("parmtype")</action>
 
             <!-- add input flag -->
@@ -295,7 +321,17 @@
                 copy.parent.type &lt; prog.end_oo_meth))                or
               (parent.type == prog.parameter and 
                evalLib("type_pair", parent.parent.parent, prog.func_class, prog.kw_new))
-            <action>createPeerAst(java.constructor, "DataSetParameter", closestPeerId)</action>
+               
+            <!-- lookup the DataSetParameter subclass that should be used, use the base class for
+                 functions or procedures (but we must use a subclass for OO methods) -->
+            <action>
+               dsetParmType = execLib("read_jtype",
+                                      copy,
+                                      "DataSetParameter",
+                                      "datasets 2")
+            </action>
+            
+            <action>createPeerAst(java.constructor, dsetParmType, closestPeerId)</action>
             <action>importPersist = true</action>
          </rule>
       </rule>

=== modified file 'rules/convert/java_templates.tpl'
--- old/rules/convert/java_templates.tpl	2022-05-05 17:43:41 +0000
+++ new/rules/convert/java_templates.tpl	2022-05-20 01:25:33 +0000
@@ -7,7 +7,7 @@
 **
 ** Copyright (c) 2004-2022, Golden Code Development Corporation.
 **
-** _#_ _I_ __Date__ __JPRM__ ___________________________________Description___________________________________
+** _#_ _I_ __Date__ __JPRM__ _________________________________ Description__________________________________
 ** 001 GES 20050721   @21760 Good working version which provides many
 **                           templates for processing variable references,
 **                           variable definitions, method calls,
@@ -292,6 +292,10 @@
 **     CA  20220202          alias_buffer_def is no longer deprecated.
 **     CA  20220426          Changed 'class_proxy_client_definition', to allow any kind of procedure, 
 **                           persistent or not.
+**     GES 20220519          Added templates with fewer boolean parameters for temp-table associate() and 
+**                           table-handle createDynamicTable(). Reworked TableParameter templates to allow
+**                           subclasses. Reworked OutputExtentParameter inner class to allow narrowing of
+**                           types.
 */
  -->
  
@@ -1018,6 +1022,40 @@
       </ast>
    </ast-root>
    
+   <!-- inner subclass of an output extent parameter class - in case of variables set as parameter -->
+   <ast-root name="output_extent_var_expr_morph" terse="true" ast_class="com.goldencode.p2j.uast.JavaAst" >
+      <ast id="0" type="ANON_CTOR" text="${extentParamClass}&lt;${rettype}&gt;">
+         <ast id="0" type="CS_INSTANCE_METHODS" text="">
+            <ast id="0" type="METHOD_DEF" text="getVariable">
+               <annotation datatype="java.lang.String" key="access" value="public" />
+               <annotation datatype="java.lang.String" key="rettype" value="${rettype}[]" />
+               <ast id="0" type="BLOCK" text="block">
+                  <ast id="0" type="KW_RETURN" text="">
+                     <ast id="0" type="STATIC_METHOD_CALL" text="${narrow}">
+                        <annotation datatype="java.lang.Boolean" key="peernode" value="true" />
+                     </ast>
+                  </ast>
+               </ast>
+            </ast>
+            <ast id="0" type="METHOD_DEF" text="setVariable">
+               <annotation datatype="java.lang.String" key="access" value="public" />
+               <annotation datatype="java.lang.String" key="rettype" value="void" />
+               <ast col="0" id="0" line="0" text="" type="LPARENS">
+                  <ast col="0" id="0" line="0" text="${rettype}[]" type="REFERENCE_DEF">
+                     <annotation datatype="java.lang.Boolean" key="final" value="true" />
+                     <annotation datatype="java.lang.String" key="name" value="newRef" />
+                  </ast>
+               </ast>
+               <ast id="0" type="BLOCK" text="block">
+                  <ast id="0" type="ASSIGN" text="=">
+                     <ast col="0" id="0" line="0" text="${widen}newRef)" type="REFERENCE" />
+                  </ast>
+               </ast>
+            </ast>
+         </ast>
+      </ast>
+   </ast-root>   
+   
    <!-- inner subclass of a resolvable expression class -->
    <ast-root name="anon_expr" terse="true" ast_class="com.goldencode.p2j.uast.JavaAst" >
       <ast id="0" type="ANON_CTOR" text="${classname}">
@@ -1337,13 +1375,13 @@
 
    <!-- instantiate a TableParameter -->
    <ast-root name="table_param" terse="true" ast_class="com.goldencode.p2j.uast.JavaAst" >
-      <ast id="0" type="CONSTRUCTOR" text="TableParameter">
+      <ast id="0" type="CONSTRUCTOR" text="${tab-parm-type}">
          <ast id="0" type="REFERENCE" text="${javaname}" />
       </ast>
    </ast-root>
 
    <ast-root name="table_param_static" terse="true" ast_class="com.goldencode.p2j.uast.JavaAst" >
-      <ast id="0" type="CONSTRUCTOR" text="TableParameter">
+      <ast id="0" type="CONSTRUCTOR" text="${tab-parm-type}">
          <ast id="0" type="METHOD_CALL" text="get">
             <ast id="0" type="REFERENCE" text="${javaname}" />
          </ast>
@@ -2148,8 +2186,6 @@
       <ast id="0" type="STATIC_METHOD_CALL" text="TemporaryBuffer.associate">
          <ast id="0" type="REFERENCE" text="${javaname1}" />
          <ast id="0" type="REFERENCE" text="${javaname2}" />
-         <ast id="0" type="${input}" text="" />
-         <ast id="0" type="${output}" text="" />
       </ast>
    </ast-root>
 
@@ -2158,6 +2194,24 @@
       <ast id="0" type="STATIC_METHOD_CALL" text="TemporaryBuffer.createDynamicTable">
          <ast id="0" type="REFERENCE" text="${javaname1}" />
          <ast id="0" type="REFERENCE" text="${javaname2}" />
+      </ast>
+   </ast-root>
+   
+   <!-- set up an association between two temp table instances -->
+   <ast-root name="associate_temp_tables_full" terse="true" ast_class="com.goldencode.p2j.uast.JavaAst" >
+      <ast id="0" type="STATIC_METHOD_CALL" text="TemporaryBuffer.associate">
+         <ast id="0" type="REFERENCE" text="${javaname1}" />
+         <ast id="0" type="REFERENCE" text="${javaname2}" />
+         <ast id="0" type="${input}" text="" />
+         <ast id="0" type="${output}" text="" />
+      </ast>
+   </ast-root>
+
+   <!-- create dynamic temp table -->
+   <ast-root name="create_dynamic_tt_full" terse="true" ast_class="com.goldencode.p2j.uast.JavaAst" >
+      <ast id="0" type="STATIC_METHOD_CALL" text="TemporaryBuffer.createDynamicTable">
+         <ast id="0" type="REFERENCE" text="${javaname1}" />
+         <ast id="0" type="REFERENCE" text="${javaname2}" />
          <ast id="0" type="${input}" text="" />
          <ast id="0" type="${output}" text="" />
       </ast>

=== modified file 'rules/convert/output_parameters.rules'
--- old/rules/convert/output_parameters.rules	2021-06-09 14:35:48 +0000
+++ new/rules/convert/output_parameters.rules	2022-05-20 02:09:03 +0000
@@ -5,7 +5,7 @@
 ** Module   : output_parameters.rules
 ** Abstract : procedure/function output and input-output parameters analysis
 **
-** Copyright (c) 2013-2021, Golden Code Development Corporation.
+** Copyright (c) 2013-2022, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _________________________________Description__________________________________
 ** 001 CA  20130208 Created initial version.
@@ -27,8 +27,10 @@
 **     CA  20210216 Fix for the 'cast-to-object' case for a single extent argument in a dynamic call, when
 **                  INPUT modifier exists or not.
 **     CA  20210609 Fixed extent property used as output/input-output argument.
+**     GES 20220519 Reworked OutputExtentParameter to morph types for OO method usage when the types narrow.
 */
  -->
+ 
 <!--
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -93,19 +95,27 @@
    <include name="common-progress" />
    
    <!-- variables -->   
-   <variable name="propref"  type="com.goldencode.ast.Aast" />
-   <variable name="ref"      type="com.goldencode.ast.Aast" />
-   <variable name="chref"    type="com.goldencode.ast.Aast" />
-   <variable name="varref"   type="com.goldencode.ast.Aast" />
-   <variable name="wrapper"  type="java.lang.String" />
-   <variable name="rettype"  type="java.lang.String" />
-   <variable name="template" type="java.lang.String" />
+   <variable name="propref"          type="com.goldencode.ast.Aast" />
+   <variable name="ref"              type="com.goldencode.ast.Aast" />
+   <variable name="chref"            type="com.goldencode.ast.Aast" />
+   <variable name="varref"           type="com.goldencode.ast.Aast" />
+   <variable name="wrapper"          type="java.lang.String" />
+   <variable name="rettype"          type="java.lang.String" />
+   <variable name="calltype"         type="java.lang.String" />
+   <variable name="narrow"           type="java.lang.String" />
+   <variable name="widen"            type="java.lang.String" />
+   <variable name="template"         type="java.lang.String" />
    <variable name="extentParamClass" type="java.lang.String" />
    
    <walk-rules>
       
       <rule>isNote("outputExtentType")
          <action>rettype = getNoteString("outputExtentType")</action>
+         
+         <rule>isNote("calltype")
+            <action>calltype = getNoteString("calltype")</action>
+            <action on="false">calltype = rettype</action>
+         </rule>
 
          <!-- handle extent variables/fields set at function/procedure calls -->
          <rule>!evalLib("fields")
@@ -120,15 +130,29 @@
                   extentParamClass = "InputOutputExtentParameter"
                </action>
             </rule>
+            
             <!-- emit the inner class -->
-            <action>
-               tw.graft("output_extent_var_expr", copy,
-                        closestPeerId,
-                        "rettype", rettype,
-                        "extent", sprintf("%d", getNoteLong("extent")),
-                        "extentParamClass", extentParamClass)
-            </action>
-
+            <rule>!rettype.equals(calltype)
+               <action>narrow = execLib("type_conversion_method_name", rettype)</action>
+               <action>widen = sprintf("%s(", execLib("type_conversion_method_name", calltype))</action>
+               <action>
+                  tw.graft("output_extent_var_expr_morph", copy,
+                           closestPeerId,
+                           "rettype", rettype,
+                           "narrow", narrow,
+                           "widen", widen,
+                           "extent", sprintf("%d", getNoteLong("extent")),
+                           "extentParamClass", extentParamClass)
+               </action>
+               <action on="false">
+                  tw.graft("output_extent_var_expr", copy,
+                           closestPeerId,
+                           "rettype", rettype,
+                           "extent", sprintf("%d", getNoteLong("extent")),
+                           "extentParamClass", extentParamClass)
+               </action>
+            </rule>
+            
             <!-- for fields, we emit the dedicated handler 
                  decide the class depending on parameter type -->
             <rule on="false">getNoteLong("parmtype") == prog.kw_output

=== modified file 'rules/convert/variable_definitions.rules'
--- old/rules/convert/variable_definitions.rules	2021-07-26 12:52:17 +0000
+++ new/rules/convert/variable_definitions.rules	2022-05-12 15:03:49 +0000
@@ -7,7 +7,7 @@
 **
 ** Copyright (c) 2005-2021, Golden Code Development Corporation.
 **
-** _#_ _I_ __Date__ __JPRM__ ____________________________Description_____________________________
+** _#_ _I_ __Date__ __JPRM__ __________________________________Description___________________________________
 ** 001 GES 20050727   @21865 First version which converts Progress
 **                           variable definitions into Java instance or
 **                           local variable definitions.  Procedure and
@@ -218,6 +218,8 @@
 **     CA  20210726          Fixed a bug when external program's extent parameters are promoted to class 
 **                           fields: those fields must be two-dimension, too, if the parameter is 'extent
 **                           mutable'.
+**     GES 20210506          Reworked table-handle and dataset-handle parameters to use subclasses of
+**                           TableParameter/DataSetParameter.
 */
  -->
 
@@ -347,6 +349,7 @@
    <variable name="typeSuffix" type="java.lang.String" />
    <variable name="parMode"    type="java.lang.String" />
    <variable name="aliases"    type="java.lang.String" />
+   <variable name="jtype"      type="java.lang.String" />
    <variable name="mode"       type="java.lang.Integer" />
    <variable name="initparm"   type="java.lang.Boolean" />
    <variable name="global"     type="java.lang.Boolean" />
@@ -504,7 +507,6 @@
             !evalLib("is_schema_trigger_new_value")                              and
             !evalLib("is_schema_trigger_old_value")                              and
             !(this.type == prog.parameter and ancestor(prog.event_signature, 2)) and
-            !(this.type == prog.define_property and (isAbstract or isIface))     and
             this.type != prog.define_dataset                                     and
             this.type != prog.define_data_source                                 and
             !(this.type == prog.define_property and (isAbstract or isIface))     and
@@ -768,17 +770,33 @@
          </rule>
          
          <!-- now create a REFERENCE_DEF as the first child of the ASSIGN such that the classname
-              and the variable's Java name will be emitted; 
-              if is a TABLE-HANDLE parameter of a procedure, use TableParameter; in case of a
-              DATASET-HANDLE use DataSetParameter. -->
+              and the variable's Java name will be emitted -->
          <rule>descendant(prog.kw_tab_hand, 1)
-            <action>
-               refid = createJavaAst(java.reference_def, "TableParameter", lastid)
+         
+            <!-- lookup the TableParameter subclass that should be used, use the base class for
+                 functions or procedures (but we must use a subclass for OO methods) -->
+            <action>
+               jtype = execLib("read_jtype",
+                               copy.getImmediateChild(prog.kw_tab_hand, null),
+                               "TableParameter",
+                               "var defs 1")
+            </action>
+            <action>
+               refid = createJavaAst(java.reference_def, jtype, lastid)
             </action>
             
             <rule on="false">descendant(prog.kw_dset_hnd, 1)
-               <action>
-                  refid = createJavaAst(java.reference_def, "DataSetParameter", lastid)
+            
+               <!-- lookup the DataSetParameter subclass that should be used, use the base class for
+                    functions or procedures (but we must use a subclass for OO methods) -->
+               <action>
+                  jtype = execLib("read_jtype",
+                                  copy.getImmediateChild(prog.kw_dset_hnd, null),
+                                  "DataSetParameter",
+                                  "var defs 2")
+               </action>
+               <action>
+                  refid = createJavaAst(java.reference_def, jtype, lastid)
                </action>
                
                <!-- do not emit OLD/NEW var from ASSIGN trigger; it was promoted and will be

=== modified file 'rules/convert/variable_references.rules'
--- old/rules/convert/variable_references.rules	2022-05-16 16:24:45 +0000
+++ new/rules/convert/variable_references.rules	2022-05-20 12:08:54 +0000
@@ -7,7 +7,7 @@
 **
 ** Copyright (c) 2005-2022, Golden Code Development Corporation.
 **
-** _#_ _I_ __Date__ __JPRM__ ____________________________Description_____________________________
+** _#_ _I_ __Date__ __JPRM__ __________________________________Description___________________________________
 ** 001 GES 20050727   @21866 First version which converts Progress variable references (including
 **                           array subscripts) into Java instance or local variable references.
 **                           All built-in functions  that truly act as global variables are
@@ -161,6 +161,7 @@
 **                           can have the INPUT/OUTPUT/INPUT-OUTPUT modes set.
 **     CA  20220516          Added conversion for COMPILER. 
 **     CA  20220516          Fixes related to an indexed OO property access, which is chained with a reference.
+**     GES 20210519          Reworked table-handles to use subclasses of TableParameter.
 */
  -->
  
@@ -266,31 +267,33 @@
    </func-library>
    
    <!-- variables -->
-   <variable name="lastid"       type="java.lang.Long" />
-   <variable name="newid"        type="java.lang.Long" />
-   <variable name="subid"        type="java.lang.Long" />
-   <variable name="refid"        type="java.lang.Long" />
-   <variable name="methodType"   type="java.lang.Integer" />
-   <variable name="oldtype"      type="java.lang.Integer" />
-   <variable name="idx"          type="java.lang.Integer" />
-   <variable name="propBypass"   type="java.lang.Boolean" />
-   <variable name="needsRef"     type="java.lang.Boolean" />
-   <variable name="dbimport"     type="java.lang.Boolean" />
-   <variable name="uiimport"     type="java.lang.Boolean" />
-   <variable name="pmimport"     type="java.lang.Boolean" />
-   <variable name="keyimport"    type="java.lang.Boolean" />
-   <variable name="ltimport"     type="java.lang.Boolean" />
-   <variable name="smimport"     type="java.lang.Boolean" />
-   <variable name="aaimport"     type="java.lang.Boolean" />
-   <variable name="mathimport"   type="java.lang.Boolean" />
-   <variable name="methodTxt"    type="java.lang.String" />
-   <variable name="clsname"      type="java.lang.String" />
-   <variable name="chp_wrapper"  type="java.lang.String" />
-   <variable name="ref"          type="com.goldencode.ast.Aast" />
-   <variable name="ref2"         type="com.goldencode.ast.Aast" />
-   <variable name="varref"       type="com.goldencode.ast.Aast" />
-   <variable name="subref"       type="com.goldencode.ast.Aast" />
-   <variable name="wrapimp"      type="java.lang.Boolean" /> <!-- static OutputParameter -->
+   <variable name="lastid"         type="java.lang.Long" />
+   <variable name="newid"          type="java.lang.Long" />
+   <variable name="subid"          type="java.lang.Long" />
+   <variable name="refid"          type="java.lang.Long" />
+   <variable name="methodType"     type="java.lang.Integer" />
+   <variable name="oldtype"        type="java.lang.Integer" />
+   <variable name="idx"            type="java.lang.Integer" />
+   <variable name="propBypass"     type="java.lang.Boolean" />
+   <variable name="needsRef"       type="java.lang.Boolean" />
+   <variable name="dbimport"       type="java.lang.Boolean" />
+   <variable name="uiimport"       type="java.lang.Boolean" />
+   <variable name="pmimport"       type="java.lang.Boolean" />
+   <variable name="keyimport"      type="java.lang.Boolean" />
+   <variable name="ltimport"       type="java.lang.Boolean" />
+   <variable name="smimport"       type="java.lang.Boolean" />
+   <variable name="aaimport"       type="java.lang.Boolean" />
+   <variable name="mathimport"     type="java.lang.Boolean" />
+   <variable name="methodTxt"      type="java.lang.String" />
+   <variable name="clsname"        type="java.lang.String" />
+   <variable name="chp_wrapper"    type="java.lang.String" />
+   <variable name="cname"          type="java.lang.String" />
+   <variable name="tabParmType"    type="java.lang.String" />
+   <variable name="ref"            type="com.goldencode.ast.Aast" />
+   <variable name="ref2"           type="com.goldencode.ast.Aast" />
+   <variable name="varref"         type="com.goldencode.ast.Aast" />
+   <variable name="subref"         type="com.goldencode.ast.Aast" />
+   <variable name="wrapimp"        type="java.lang.Boolean" /> <!-- static OutputParameter -->
    
    <!-- main processing (once per tree) -->
    <init-rules>
@@ -572,18 +575,21 @@
                      <action>
                         lastid = createPeerAst(java.constructor, "PropertyReference", closestPeerId)
                      </action>
-
+                  
                      <rule on="false">isNote("assign_back")
                         <action>
                            lastid = createPeerAst(java.static_method_call, "wrap", closestPeerId)
                         </action>
                         <action>wrapimp = true</action>
                         
-                        <action>
-                           createJavaAst(java.reference, 
-                                         sprintf("%s.class", ref.getAnnotation("classname")), 
-                                         lastid)
-                        </action>
+                        <action>cname = ref.getAnnotation("classname")</action>
+                        <rule>cname.startsWith("object&lt;")
+                           <action>cname = "object"</action>
+                        </rule>
+                        <rule>cname.startsWith("jobject&lt;")
+                           <action>cname = "jobject"</action>
+                        </rule>
+                        <action>createJavaAst(java.reference, sprintf("%s.class", cname), lastid)</action>
                         
                         <rule>getNoteBoolean("needs_value")
                            <action>copy.putAnnotation("wrapNodeId", lastid)</action>
@@ -669,11 +675,16 @@
                </rule>
                
                <rule>isNote("classname")
-                  <action>
-                     createJavaAst(java.reference, 
-                                   sprintf("%s.class", getNoteString("classname")), 
-                                   lastid)
-                  </action>
+               
+                  <action>cname = getNoteString("classname")</action>
+                  <rule>cname.startsWith("object&lt;")
+                     <action>cname = "object"</action>
+                  </rule>
+                  <rule>cname.startsWith("jobject&lt;")
+                     <action>cname = "jobject"</action>
+                  </rule>
+                  <action>createJavaAst(java.reference, sprintf("%s.class", cname), lastid)</action>
+                  
                </rule>
                <action>wrapimp = true</action>
                
@@ -908,15 +919,25 @@
          <!-- create the node -->
          <rule>methodTxt != null
             <rule>relativePath("KW_TAB_HAND/VAR_HANDLE")
-               
+                                              
+               <!-- lookup the TableParameter subclass that should be used, use the base class for
+                    functions or procedures (but we must use a subclass for OO methods) -->
+               <action>
+                  tabParmType = execLib("read_jtype", copy.parent, "TableParameter", "var refs")
+               </action>
+            
                <!-- case of TABLE-HANDLE parameter passed to a function -->
                <action>ref = getAst(getNoteLong("refid"))</action>
                <rule>ref.isAnnotation("static") and getReferenceNoteBoolean(ref.id, "static")
                   <action>
-                     ref = tw.graft("table_param_static", copy, closestPeerId, "javaname", methodTxt)
+                     ref = tw.graft("table_param_static", copy, closestPeerId,
+                                    "tab-parm-type", tabParmType,
+                                    "javaname", methodTxt)
                   </action>
                   <action on="false">
-                     ref = tw.graft("table_param", copy, closestPeerId, "javaname", methodTxt)
+                     ref = tw.graft("table_param", copy, closestPeerId,
+                                    "tab-parm-type", tabParmType,
+                                    "javaname", methodTxt)
                   </action>
                </rule>
                

=== modified file 'rules/include/common-progress.rules'
--- old/rules/include/common-progress.rules	2022-05-16 12:34:34 +0000
+++ new/rules/include/common-progress.rules	2022-05-23 19:11:40 +0000
@@ -470,6 +470,8 @@
 **                           distinguish the parameter in a ParameterList:setParameter, as this OO method call
 **                           can have the INPUT/OUTPUT/INPUT-OUTPUT modes set.
 **     CA  20220516          Added 'getUniqueJavaMethodName', moved from annotations.xml.
+**     GES 20210506          Added read_jtype. Reworked enum_java_type to use ClassDefinition.  Added
+**                           type_conversion_method_name and is_unsubscripted_extent.
 */
  -->
  
@@ -1056,6 +1058,38 @@
          <rule>mixed = (numd &gt; 0 and numf != numd)</rule>
       </function>
       
+      <function name="type_conversion_method_name">
+         <parameter    name="classname" type="java.lang.String" />
+         <return       name="methname"  type="java.lang.String" />
+         
+         <rule>methname = ""</rule>
+         
+         <rule>classname.equals("integer")
+            <action>methname = "toInteger"</action>
+         </rule>
+         <rule>classname.equals("int64")
+            <action>methname = "toInt64"</action>
+         </rule>
+         <rule>classname.equals("decimal")
+            <action>methname = "toDecimal"</action>
+         </rule>
+         <rule>classname.equals("date")
+            <action>methname = "toDate"</action>
+         </rule>
+         <rule>classname.equals("datetime")
+            <action>methname = "toDatetime"</action>
+         </rule>
+         <rule>classname.equals("datetimetz")
+            <action>methname = "toDatetimeTz"</action>
+         </rule>
+         <rule>classname.equals("character")
+            <action>methname = "toCharacter"</action>
+         </rule>
+         <rule>classname.equals("longchar")
+            <action>methname = "toLongchar"</action>
+         </rule>
+      </function>
+      
       <function name="wrapper_name_for_field_type">
          <parameter name="ttype"     type="java.lang.Integer" />
          <return    name="classname" type="java.lang.String" />
@@ -3299,7 +3333,7 @@
          <parameter name="target"  type="com.goldencode.ast.Aast" />
          <variable  name="cname"   type="java.lang.String" />
          <variable  name="qname"   type="java.lang.String" />
-         <variable  name="ccn"     type="com.goldencode.p2j.uast.ConvertedClassName" />
+         <variable  name="cdef"    type="com.goldencode.p2j.uast.ClassDefinition" />
          <return    name="jname"   type="java.lang.String" />
          
          <rule>cname = ecw.expressionType(target)</rule>
@@ -3307,8 +3341,8 @@
          <rule>cname != null
             <rule>cname.startsWith("object&lt;? extends ")
                <action>qname = cname.substring(17, cname.length() - 1)</action>
-               <action>ccn = loadConvertedClassByQname(qname)</action>
-               <action>jname = sprintf("object&lt;? extends %s&gt;", ccn.getQualifiedName())</action>
+               <action>cdef = loadConvertedClassByQname(qname)</action>
+               <action>jname = sprintf("object&lt;? extends %s&gt;", cdef.getQualifiedName())</action>
             </rule>
          </rule>
       </function>
@@ -6481,6 +6515,22 @@
          
       </function>
       
+      <function name="is_unsubscripted_extent">
+         <parameter name="target"   type="com.goldencode.ast.Aast" />
+         <return    name="isExtent" type="java.lang.Boolean" />
+         <variable  name="extent"   type="java.lang.Long" />
+         
+         <rule>isExtent = false</rule>
+         
+         <rule>target.isAnnotation("extent")
+            <rule>extent = target.getAnnotation("extent")</rule>
+            <rule>
+               isExtent = (extent == -1 or extent &gt; 0) and !target.descendant(1, prog.lbracket)
+            </rule>
+         </rule>
+         
+      </function>
+      
       <function name="get_function_return_class">
          <parameter name="target" type="com.goldencode.ast.Aast" />
          <variable  name="ttype"  type="java.lang.Integer" />
@@ -10817,6 +10867,28 @@
          </rule>
       </function>
       
+      <function name="read_jtype">
+         <parameter  name="node"    type="com.goldencode.ast.Aast" />
+         <parameter  name="defval"  type="java.lang.String" />
+         <parameter  name="descr"   type="java.lang.String" />
+         <variable   name="ref"     type="com.goldencode.ast.Aast" />
+         <return     name="jtype"   type="java.lang.String" />
+         
+         <rule>jtype = defval</rule>
+         
+         <rule>!node.isAnnotation("jtype") and node.parent.isAnnotation("jtype")
+            <action>ref = node.parent</action>
+            <action on="false">ref = node</action>
+         </rule>
+         
+         <rule>ref.isAnnotation("jtype") 
+            <action>jtype = #(java.lang.String) ref.getAnnotation("jtype")</action>
+            <action on="false">
+               printf("WARNING: missing 'jtype' annotation for %s at %s\n", descr, ref.dumpTree())
+            </action>
+         </rule>
+      </function>
+      
       <!-- Create a runtime annotation.
          When executed in runtime mode, any object can be stored as annotation because they are
             kept in a memory persistence, not serialized to disk.

=== modified file 'src/com/goldencode/p2j/convert/TransformDriver.java'
--- old/src/com/goldencode/p2j/convert/TransformDriver.java	2022-05-16 12:34:34 +0000
+++ new/src/com/goldencode/p2j/convert/TransformDriver.java	2022-05-20 12:10:05 +0000
@@ -36,6 +36,8 @@
 **                  resource (like a file or namespace) is being processed.  Only front phase is supported at 
 **                  this time.
 **     CA  20220516 Made JobDefinition protected.
+**     GES 20210520 Sort interfaces into the BFS class hierachy so that all overridden methods will be
+**                  converted in the correct order (the parent version first). 
 */
 
 /*
@@ -179,6 +181,9 @@
    /** Full details of this run. */
    protected JobDefinition job = null;
    
+   /** Base class for all OO classes. */
+   private static final String ROOT_CLASS = "Progress.Lang.Object".toLowerCase();
+   
    /**
     * Creates an instance with a specific configuration.
     *
@@ -1625,6 +1630,143 @@
    }
    
    /**
+    * Read the class hierarchy from the AST associated with the given filename and store the state in
+    * the given maps.
+    *  
+    * @param    mgr
+    *           AST manager.
+    * @param    cls2file
+    *           Map of qualified class names to file names.
+    * @param    clsHierarchy
+    *           Map of qualified class names to a set of subclasses.
+    * @param    file
+    *           The file name to process.
+    */
+   private static void processOODefinition(AstManager               mgr,
+                                           Map<String, String>      cls2file,
+                                           Map<String, Set<String>> clsHierarchy,
+                                           String                   file)
+   {
+      // temporarily load this AST and get:
+      // - the qualified class name
+      // - the super class (single inherits for a class) or super interfaces (multiple-inherits for 
+      //   interface)
+      // - all implemented interfaces (which we treat the same as a superclass) for a class
+      
+      Aast ast    = mgr.loadTree(file);
+      Aast def    = ast.getImmediateChild(ProgressParserTokenTypes.CLASS_DEF, null);
+      Aast header = null;
+      
+      if (def == null)
+      {
+         def = ast.getImmediateChild(ProgressParserTokenTypes.INTERFACE_DEF, null);
+         
+         if (def == null)
+         {
+            // must be an enum, nothing to do
+            return;
+         }
+         else
+         {
+            header = def.getImmediateChild(ProgressParserTokenTypes.KW_INTERFAC, null);
+         }
+      }
+      else
+      {
+         header = def.getImmediateChild(ProgressParserTokenTypes.KW_CLASS, null);
+      }
+      
+      List<String> supers = new LinkedList<String>();
+      
+      if (header.downPath("KW_INHERITS/CLASS_NAME"))
+      {
+         Aast ref = header.getImmediateChild(ProgressParserTokenTypes.KW_INHERITS, null);
+         
+         processClassNames(ref, clsHierarchy, supers);
+      }
+
+      if (header.downPath("KW_IMPLEMTS/CLASS_NAME"))
+      {
+         Aast ref2 = header.getImmediateChild(ProgressParserTokenTypes.KW_IMPLEMTS, null);
+         
+         processClassNames(ref2, clsHierarchy, supers);
+      }
+
+      if (supers.isEmpty())
+      {
+         supers.add(ROOT_CLASS);
+      }
+      
+      Aast   sym   = header.getImmediateChild(ProgressParserTokenTypes.SYMBOL, null);
+      String qname = sym.getText().toLowerCase();
+      
+      cls2file.put(qname, file);
+
+      // link the qname into the set for every super class/super interface/implemented interface
+      for (String superClass : supers)
+      {
+         Set<String> subClasses = clsHierarchy.get(superClass);
+
+         // create a new set if the superclass hasn't been processed yet
+         if (subClasses == null)
+         {
+            clsHierarchy.put(superClass, subClasses = new TreeSet<>());
+         }
+         
+         // Progress.Lang.Object will recurse endlessly if we try to make it a subclass of itself
+         if (!superClass.toLowerCase().equals(qname))
+         {
+            subClasses.add(qname);
+         }
+      }
+   }
+   
+   /**
+    * Process the given AST node for all {@code CLASS_NAME} child nodes.  For each child node, read
+    * the qualified class name and add it to the given list of super classes.  For built-in classes
+    * link them to the root class even if they are not direct children.
+    * 
+    * @param    ref
+    *           The {@code KW_INHERITS} or {@code KW_IMPLEMTS} node which has one or more class name
+    *           children. 
+    * @param    clsHierarchy
+    *           The map of subclasses for each parent class.
+    * @param    supers
+    *           The list to which to add any inherited super class, inherited interfaces and any
+    *           implemented interfaces.
+    */
+   private static void processClassNames(Aast                     ref,
+                                         Map<String, Set<String>> clsHierarchy,
+                                         List<String>             supers)
+   {
+      Aast child = ref.getImmediateChild(ProgressParserTokenTypes.CLASS_NAME, null);
+      
+      while (child != null)
+      {
+         Boolean builtinCls = (Boolean) child.getAnnotation("builtin-cls");
+         String  superClass = (String) child.getAnnotation("qualified");
+         superClass = superClass.toLowerCase();
+         
+         supers.add(superClass);
+         
+         if (builtinCls != null && builtinCls.booleanValue() && !ROOT_CLASS.equals(superClass))
+         {
+            // for all builtin classes, add them to the 'progress.lang.object' sub-classes
+            Set<String> subClasses = clsHierarchy.get(ROOT_CLASS);
+            
+            if (subClasses == null)
+            {
+               clsHierarchy.put(ROOT_CLASS, subClasses = new TreeSet<>());
+            }
+            
+            subClasses.add(superClass);
+         }
+         
+         child = child.getImmediateChild(ProgressParserTokenTypes.CLASS_NAME, child);
+      }
+   }
+   
+   /**
     * Sort all legacy classes in the specified sources, by computed the class hierarchy and 
     * performing a BFS walk starting from <code>Progress.Lang.Object</code>.  Interfaces are not
     * included in this sort.
@@ -1637,79 +1779,26 @@
     */
    private static List<String> sortLegacyClasses(Set<String> sources)
    {
-      String rootClass = "Progress.Lang.Object".toLowerCase();
-      
       AstManager mgr = AstManager.get();
       
-      Map<String, String> cls2file = new HashMap<>();
+      Map<String, String>      cls2file     = new HashMap<>();
       Map<String, Set<String>> clsHierarchy = new HashMap<>();
       
       for (String file : sources)
       {
          if (file.endsWith(".cls.ast"))
          {
-            // temporarily load this AST and get:
-            // - the qualified class name
-            // - the super class
-            Aast ast = mgr.loadTree(file);
-            Aast clsDef = ast.getImmediateChild(ProgressParserTokenTypes.CLASS_DEF, null);
-            if (clsDef == null)
-            {
-               // must be an interface
-               continue;
-            }
-            
-            String superClass = rootClass;
-            Aast kwClass = clsDef.getImmediateChild(ProgressParserTokenTypes.KW_CLASS, null);
-            
-            if (kwClass.downPath("KW_INHERITS/CLASS_NAME"))
-            {
-               Aast ref = kwClass.getImmediateChild(ProgressParserTokenTypes.KW_INHERITS, null);
-               ref = ref.getImmediateChild(ProgressParserTokenTypes.CLASS_NAME, null);
-               Boolean builtinCls = (Boolean) ref.getAnnotation("builtin-cls");
-               superClass = (String) ref.getAnnotation("qualified");
-               superClass = superClass.toLowerCase();
-               
-               if (builtinCls != null        &&
-                   builtinCls &&
-                   !rootClass.equals(superClass))
-               {
-                  // for all builtin classes, add them to the 'progress.lang.object' sub-classes
-                  Set<String> subClasses = clsHierarchy.get(rootClass);
-                  if (subClasses == null)
-                  {
-                     clsHierarchy.put(rootClass, subClasses = new TreeSet<>());
-                  }
-                  subClasses.add(superClass);
-               }
-            }
-            
-            Aast ref = kwClass.getImmediateChild(ProgressParserTokenTypes.SYMBOL, null);
-            String qname = ref.getText().toLowerCase();
-            
-            cls2file.put(qname, file);
-            
-            Set<String> subClasses = clsHierarchy.get(superClass);
-            if (subClasses == null)
-            {
-               clsHierarchy.put(superClass, subClasses = new TreeSet<>());
-            }
-            
-            // Progress.Lang.Object will recurse endlessly if we try to make it
-            // a subclass of itself
-            if (!superClass.toLowerCase().equals(qname))
-            {
-               subClasses.add(qname);
-            }
+            processOODefinition(mgr, cls2file, clsHierarchy, file);
          }
       }
       
-      List<String> bfs = walkHierarchy(clsHierarchy, rootClass);
-      
+      List<String> bfs   = walkHierarchy(clsHierarchy, ROOT_CLASS);
       List<String> files = new ArrayList<>();
+      
       for (String cls : bfs)
       {
          String file = cls2file.get(cls);
+         
          if (file != null)
          {
             // may be null in case of skeleton classes
@@ -1735,6 +1824,7 @@
       parent = parent.toLowerCase();
 
       Set<String> subClasses = hierarchy.get(parent);
+      
       if (subClasses == null)
       {
          return Collections.emptyList();

=== modified file 'src/com/goldencode/p2j/convert/db/ConversionData.java'
--- old/src/com/goldencode/p2j/convert/db/ConversionData.java	2021-10-12 08:35:45 +0000
+++ new/src/com/goldencode/p2j/convert/db/ConversionData.java	2022-05-12 15:03:49 +0000
@@ -2,7 +2,7 @@
 ** Module   : ConversionData.java
 ** Abstract : Main access to incremental conversion db-backed data.
 **
-** Copyright (c) 2020-2021, Golden Code Development Corporation.
+** Copyright (c) 2020-2022, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description----------------------------------
 ** 001 CA  20200412 Created initial version, for incremental conversion support.
@@ -14,7 +14,9 @@
 **                  keys must be considered as dependencies and their files reconverted, too.
 **     CA  20211012 Fixed generated WSDL and reworked SOAP support to rely on namespace, when resolving an
 **                  operation.
+**     GES 20220502 Match API method name change.
 */
+
 /*
 ** This program is free software: you can redistribute it and/or modify
 ** it under the terms of the GNU Affero General Public License as
@@ -701,7 +703,7 @@
       String filename = clsDef.getFilename();
       String qname = clsDef.getName();
       String jpkgname = clsDef.getJavaPackage();
-      String jclassname = clsDef.getJavaSimpleName();
+      String jclassname = clsDef.getSimpleJavaName();
       
       String insertSql = "MERGE INTO class_definitions KEY (filename) values (?, ?, ?, ?, ?, ?, ?)";
       helper.executeSQL(insertSql, 

=== modified file 'src/com/goldencode/p2j/oo/core/collections/AbstractTtcollection.java'
--- old/src/com/goldencode/p2j/oo/core/collections/AbstractTtcollection.java	2022-05-13 06:58:53 +0000
+++ new/src/com/goldencode/p2j/oo/core/collections/AbstractTtcollection.java	2022-05-20 12:11:18 +0000
@@ -9,13 +9,13 @@
 ** 002 CA  20210113 Renamed Iiterator.next to Iiterator.next_, to follow NameConverter's rules.
 **         20210113 Renamed clear() to clear_(), to follow NameConverter's rules.
 ** 003 ME  20210128 Keep only the default 4GL temp-table implementation.
-** 004 CA  20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy
-**                  signature.
+** 004 CA  20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy signature.
 **     CA  20210609 Updated INPUT/INPUT-OUTPUT parameters to the new approach, where they are explicitly 
 **                  initialized at the method's execution, and not at the caller's arguments.
 **     ME  20210928 Fix return value on clone method, defined out of the function block.
 **     CA  20220513 Renamed _BaseObject_.clone() to legacyClone(), as it can be overriden by sub-classes and
 **                  the access mode may collide with the Java's Object.clone().
+**     GES 20220520 Upgraded table-handle parameter processing to match API changes.
 */
 
 /*
@@ -70,6 +70,7 @@
 **     Corporation or FWD.  All modified versions must contain the notices of
 **     attribution required in this license.
 */
+
 package com.goldencode.p2j.oo.core.collections;
 
 import com.goldencode.p2j.util.*;
@@ -249,7 +250,7 @@
          if (getLegacyClass().ref()
                   .legacyEquals(p1.ref().getLegacyClass()).booleanValue())
          {
-            p1.ref().toTable(new TableParameter(tableHandle, true));
+            p1.ref().toTable(new OutputTableHandle(tableHandle, ParameterOption.APPEND));
             size.assign(size.intValue() + p1.ref().getSize().intValue());
          }
          else
@@ -342,13 +343,13 @@
    @LegacySignature(type = Type.METHOD, name = "ToTable", parameters = {
             @LegacyParameter(name = "p1", type = "HANDLE", mode = "OUTPUT") })
    @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL)
-   public void toTable(final TableParameter _p1)
+   public void toTable(final OutputTableHandle _p1)
    {
       handle p1 = new handle();
 
       internalProcedure(AbstractTtcollection.class, this, "ToTable", new Block((Body) () ->
       {
-         TemporaryBuffer.createDynamicTable(_p1, p1, false, true);
+         TemporaryBuffer.createDynamicTable(_p1, p1);
          p1.assign(tableHandle);
       }));
    }

=== modified file 'src/com/goldencode/p2j/oo/core/collections/Array.java'
--- old/src/com/goldencode/p2j/oo/core/collections/Array.java	2022-05-13 06:58:53 +0000
+++ new/src/com/goldencode/p2j/oo/core/collections/Array.java	2022-05-20 12:11:07 +0000
@@ -12,13 +12,13 @@
 ** 004 ME  20200529 Complete to match the 4GL implementation.
 ** 005 CA  20210113 Renamed Iiterator.next to Iiterator.next_, to follow NameConverter's rules.
 **         20210113 Renamed clear() to clear_(), to follow NameConverter's rules.
-** 006 CA  20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy
-**                  signature.
+** 006 CA  20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy signature.
 **     ME  20210916 Fix wrong method call on set (increment instead of decrement). Clean-up resize
 **                  and fix 'retainAll' method.
 **     ME  20210929 Fix clone return value.
 **     CA  20220513 Renamed _BaseObject_.clone() to legacyClone(), as it can be overriden by sub-classes and
 **                  the access mode may collide with the Java's Object.clone().
+**     GES 20220520 Upgraded table-handle parameter processing to match API changes.
 */
 
 /*
@@ -621,15 +621,16 @@
    }
 
    @LegacySignature(type = Type.METHOD, name = "ToTable", parameters = {
-            @LegacyParameter(name = "p1", type = "HANDLE", mode = "INPUT") })
+            @LegacyParameter(name = "p1", type = "HANDLE", mode = "OUTPUT") })
    @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
-   public void toTable(final TableParameter _p1)
+   public void toTable(final OutputTableHandle _p1)
    {
       handle p1 = new handle();
 
       // TODO: convert array to temp table
-      internalProcedure(this, "ToTable", new Block((Init) () -> {
-         TemporaryBuffer.createDynamicTable(_p1, p1, false, false);
+      internalProcedure(this, "ToTable", new Block((Init) () ->
+      {
+         TemporaryBuffer.createDynamicTable(_p1, p1);
       }));
    }
 }

=== modified file 'src/com/goldencode/p2j/oo/core/collections/Icollection.java'
--- old/src/com/goldencode/p2j/oo/core/collections/Icollection.java	2021-02-21 16:25:50 +0000
+++ new/src/com/goldencode/p2j/oo/core/collections/Icollection.java	2022-05-12 15:03:49 +0000
@@ -10,8 +10,8 @@
 ** 002 CA  20191024 Added method support levels and updated the class support level.
 ** 003 ME  20200729 Update parameter names to match skeleton.
 ** 004 CA  20210113 Renamed clear() to clear_(), to follow NameConverter's rules.
-** 005 CA  20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy
-**                  signature.
+** 005 CA  20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy signature.
+**     GES 20210501 Upgraded table-handle parameter processing to match API changes.
 */
 
 /*
@@ -157,8 +157,8 @@
 
    @LegacySignature(type = Type.METHOD, name = "ToTable", parameters = 
    {
-      @LegacyParameter(name = "p1", type = "HANDLE", mode = "INPUT")
+      @LegacyParameter(name = "p1", type = "HANDLE", mode = "OUTPUT")
    })
    @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL)
-   public void toTable(final TableParameter _p1);
+   public void toTable(final OutputTableHandle _p1);
 }

=== modified file 'src/com/goldencode/p2j/oo/core/collections/Ilist.java'
--- old/src/com/goldencode/p2j/oo/core/collections/Ilist.java	2021-02-21 16:25:50 +0000
+++ new/src/com/goldencode/p2j/oo/core/collections/Ilist.java	2022-05-12 15:03:49 +0000
@@ -2,13 +2,13 @@
 ** Module   : IList.java
 ** Abstract : Implementation of the builtin class.
 **
-** Copyright (c) 2019-2021, Golden Code Development Corporation.
+** Copyright (c) 2019-2022, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 ME  20200729 Convert from skeleton (OE12.2).
 ** 002 CA  20210113 Renamed clear() to clear_(), to follow NameConverter's rules.
-** 003 CA  20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy
-**                  signature.
+** 003 CA  20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy signature.
+** 004 GES 20220511 Upgraded table-handle parameter processing to match API changes.
 */
 
 /*
@@ -209,7 +209,7 @@
    {
       @LegacyParameter(name = "p1", type = "HANDLE", mode = "OUTPUT")
    })
-   public void toTable(final TableParameter _p1);
+   public void toTable(final OutputTableHandle _p1);
 
    @LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "ToArray", extent = -1, qualified = "progress.lang.object")
    public object<? extends com.goldencode.p2j.oo.lang._BaseObject_>[] toArray();

=== modified file 'src/com/goldencode/p2j/oo/core/collections/LegacyCollection.java'
--- old/src/com/goldencode/p2j/oo/core/collections/LegacyCollection.java	2022-01-20 16:27:48 +0000
+++ new/src/com/goldencode/p2j/oo/core/collections/LegacyCollection.java	2022-05-12 15:03:49 +0000
@@ -4,17 +4,18 @@
 **
 ** Copyright (c) 2019-2022, Golden Code Development Corporation.
 **
-** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
+** -#- -I- --Date-- --------------------------------------Description---------------------------------------
 ** 001 ME  20200519 First version, stubs taken by converting the skeleton using FWD.
 ** 002 ME  20210128 Implement this using a Java collection, simply override all from AbstratTTCollection.
 ** 003 CA  20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy signature.
-** 004 ME  20210325 Decrement object reference when removed from collection.
+**     ME  20210325 Decrement object reference when removed from collection.
 **     CA  20210609 Updated INPUT/INPUT-OUTPUT parameters to the new approach, where they are explicitly 
 **                  initialized at the method's execution, and not at the caller's arguments.
 **     CA  20220120 Do not used TypeFactory.object when the OO reference must not be tracked or registered.
 **                  Do not use TypeFactory.object for internal usages, use ObjectVar if the reference must 
 **                  be tracked.
 **                  All TypeFactory.object variable definitions must be done outside of the top-level block.
+**     GES 20210501 Upgraded table-handle parameter processing to match API changes.
 */
 
 /*
@@ -69,6 +70,7 @@
 **     Corporation or FWD.  All modified versions must contain the notices of
 **     attribution required in this license.
 */
+
 package com.goldencode.p2j.oo.core.collections;
 
 import com.goldencode.p2j.util.*;
@@ -323,14 +325,14 @@
             @LegacyParameter(name = "p1", type = "HANDLE", mode = "OUTPUT") })
    @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB)
    @Override
-   public void toTable(final TableParameter _p1)
+   public void toTable(final OutputTableHandle _p1)
    {
       handle p1 = new handle();
 
       internalProcedure(AbstractTtcollection.class, this, "ToTable", new Block((Body) () ->
       {
          // TODO: create table from collection
-         TemporaryBuffer.createDynamicTable(_p1, p1, false, true);
+         TemporaryBuffer.createDynamicTable(_p1, p1);
       }));
    }
 

=== modified file 'src/com/goldencode/p2j/oo/core/collections/MapBackedCollection.java'
--- old/src/com/goldencode/p2j/oo/core/collections/MapBackedCollection.java	2021-06-09 14:35:48 +0000
+++ new/src/com/goldencode/p2j/oo/core/collections/MapBackedCollection.java	2022-05-12 15:03:49 +0000
@@ -8,10 +8,10 @@
 ** 001 ME  20200514 First version, stubs taken by converting the skeleton using FWD.
 ** 002 CA  20210113 Renamed Iiterator.next to Iiterator.next_, to follow NameConverter's rules.
 **                  Renamed clear() to clear_(), to follow NameConverter's rules.
-** 003 CA  20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy
-**                  signature.
+** 003 CA  20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy signature.
 **     CA  20210609 Updated INPUT/INPUT-OUTPUT parameters to the new approach, where they are explicitly 
 **                  initialized at the method's execution, and not at the caller's arguments.
+**     GES 20210501 Upgraded table-handle parameter processing to match API changes.
 */
 
 /*
@@ -93,7 +93,9 @@
  */
 @LegacyResource(resource = "OpenEdge.Core.Collections.MapBackedCollection")
 @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_PARTIAL)
-public abstract class MapBackedCollection extends BaseObject implements Icollection
+public abstract class MapBackedCollection
+extends BaseObject
+implements Icollection
 {
    @LegacySignature(type = Type.PROPERTY, name = "OwningMap")
    private object<? extends Imap> owningMap = TypeFactory.object(Imap.class);
@@ -339,7 +341,7 @@
    @LegacySignature(type = Type.METHOD, name = "ToTable", parameters = {
             @LegacyParameter(name = "p1", type = "HANDLE", mode = "OUTPUT") })
    @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_PARTIAL)
-   public void toTable(final TableParameter _p1)
+   public void toTable(final OutputTableHandle _p1)
    {
       handle p1 = new handle();
 
@@ -347,7 +349,7 @@
       {
          if (objectsMap != Collections.EMPTY_MAP)
          {
-            TemporaryBuffer.createDynamicTable(_p1, p1, false, true);
+            TemporaryBuffer.createDynamicTable(_p1, p1);
             p1.assign(owningBuffer);
          }
          // TODO: add object to temp-table if java map is used

=== modified file 'src/com/goldencode/p2j/oo/lang/ParameterList.java'
--- old/src/com/goldencode/p2j/oo/lang/ParameterList.java	2021-05-10 06:17:30 +0000
+++ new/src/com/goldencode/p2j/oo/lang/ParameterList.java	2022-05-12 15:03:49 +0000
@@ -2,7 +2,7 @@
 ** Module   : ParameterList.java
 ** Abstract : Implementation of the Progress.Lang.ParameterList builtin class.
 **
-** Copyright (c) 2018-2021, Golden Code Development Corporation.
+** Copyright (c) 2018-2022, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
 ** 001 CA  20181213 First version.
@@ -10,11 +10,11 @@
 ** 003 CA  20190527 Added annotations for property accessors and class event methods.
 ** 005 CA  20190720 Emit the 'this' reference for the BlockManager APIs.
 ** 006 CA  20191024 Added method support levels and updated the class support level.
-** 003 CA  20210113 Renamed clear() to clear_(), to follow NameConverter's rules.
+** 007 CA  20210113 Renamed clear() to clear_(), to follow NameConverter's rules.
 **     CA  20210216 Methods which have an int parameter must use the BlockManager API for top-level block.
-** 004 CA  20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy
-**                  signature.
-** 005 ME  20210507 Improve parameter validation (call syntax uses '-' as separator instead space).
+** 008 CA  20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy signature.
+** 009 ME  20210507 Improve parameter validation (call syntax uses '-' as separator instead space).
+** 010 GES 20220511 Match changes in CallMode and ParameterOption.
 */
 
 /*
@@ -295,13 +295,10 @@
                if (cmode.inputOutput)
                   return false;
             case BY_REFERENCE:
-               if (cmode.append)
-                  return false;
                needDataStructure = true;
-
          }
          
-         needDataStructure = needDataStructure || cmode.append;
+         needDataStructure = needDataStructure || cmode.mode == ParameterOption.APPEND;
              
          if (needDataStructure)
             return ("TABLE".equalsIgnoreCase(type) || "TABLE-HANDLE".equalsIgnoreCase(type)

=== modified file 'src/com/goldencode/p2j/pattern/CommonAstSupport.java'
--- old/src/com/goldencode/p2j/pattern/CommonAstSupport.java	2022-03-07 07:56:55 +0000
+++ new/src/com/goldencode/p2j/pattern/CommonAstSupport.java	2022-05-12 15:03:49 +0000
@@ -2,9 +2,9 @@
 ** Module   : CommonAstSupport.java
 ** Abstract : Common symbol resolution and function support for AST processing
 **
-** Copyright (c) 2005-2021, Golden Code Development Corporation.
+** Copyright (c) 2005-2022, Golden Code Development Corporation.
 **
-** -#- -I- --Date-- --JPRM-- ----------------------------Description-----------------------------
+** -#- -I- --Date-- --JPRM-- ---------------------------------Description----------------------------------
 ** 001 ECF 20050228   @20100 Created initial version. Exposes basic AST
 **                           properties and provides AST-related actions,
 **                           including editing and annotation processing.
@@ -273,6 +273,9 @@
 **                           multi-context mode, as the pattern workers are singletons.
 **     HC  20211215          Added support for implicit OCX arguments.
 **     ME  20211025          Added AST node as input to loadConvertedClass method.
+**     GES 20220427          Modified to match code refactoring for OO method matching. Rework to eliminate
+**                           ConvertedClassName. Instead the ClassDefinition handles all converted name
+**                           processing.
 */
 
 /*
@@ -584,7 +587,7 @@
                      LegacySignature ls = mthd.getAnnotation(LegacySignature.class);
                      if (ls != null)
                      {
-                        String lsig = ClassDefinition.computeLegacySignature(ls.name(), ls);
+                        String lsig = SymbolResolver.calculateLegacySignature(ls.name(), ls);
                         if (lsig != null && lsig.equalsIgnoreCase(signature))
                         {
                            LegacyResourceSupport lrs = mthd.getAnnotation(LegacyResourceSupport.class);
@@ -612,9 +615,9 @@
       }
       
       /**
-       * Load the specified converted class.  This will either create a {@link ClassDefinition}
-       * structure for a new class (to be later populated with members by the parser), or will 
-       * restore an instance from an already-converted class.
+       * Load the specified class definition.  This will either create a {@link ClassDefinition}
+       * structure for a new class (to be later populated with members by the parser), or will restore
+       * an instance from an already-converted class.
        * 
        * @param    filename
        *           The file name for the legacy OO class.
@@ -624,22 +627,24 @@
        * @param    ast 
        *           The abstract syntax tree node.
        *           
-       * @return   See above.
+       * @return   The class definition.
        * 
        * @throws   ClassNotFoundException
        *           If the class can't be resolved or loaded.
        */
-      public ConvertedClassName loadConvertedClass(String filename, boolean isNew, Aast ast)
-               throws ClassNotFoundException
+      public ClassDefinition loadConvertedClass(String filename, boolean isNew, Aast ast)
+      throws ClassNotFoundException
       {
          if (filename == null)
-            throw new ClassNotFoundException(
-                     "Can not find class for accessing " + ast.toStringVerbose());
+         {
+            throw new ClassNotFoundException("Can not find class for accessing " + ast.toStringVerbose());
+         }
+         
          return SymbolResolver.loadConvertedClass(filename, isNew);
       }
       
       /**
-       * Load the specified converted class.  This lookup an instance from an already-converted class.
+       * Load the specified class definition.
        * 
        * @param    qname
        *           The legacy OO class qualified name.
@@ -649,7 +654,7 @@
        * @throws   ClassNotFoundException
        *           If the class can't be resolved or loaded.
        */
-      public ConvertedClassName loadConvertedClassByQname(String qname)
+      public ClassDefinition loadConvertedClassByQname(String qname)
       throws ClassNotFoundException
       {
          return SymbolResolver.loadConvertedClassByQname(qname);
@@ -5763,9 +5768,8 @@
                                         String pkgname, 
                                         String classname)
       {
-         String ooname = (pkgname.isEmpty() ? "" : pkgname + ".") + classname;
          ClassDefinition cls = SymbolResolver.getClassDefinition(legacyName);
-         cls.setJavaClassName(ooname);
+         cls.setJavaClassName(pkgname, classname);
          
          ConversionData.setConvertedClassName(filename, pkgname, classname);
       }

=== modified file 'src/com/goldencode/p2j/persist/DataSet.java'
--- old/src/com/goldencode/p2j/persist/DataSet.java	2022-04-22 22:17:35 +0000
+++ new/src/com/goldencode/p2j/persist/DataSet.java	2022-05-12 15:03:49 +0000
@@ -62,6 +62,8 @@
 **     CA  20220106 Fixed CREATE-LIKE when relations are involved, their active flag was not being copied 
 **                  properly.
 **     OM  20220422 Allow Xml/JsonImport to load records into buffer without exposing package private methods.
+**     GES 20210501 Upgraded dataset parameter processing to match API changes. Reworked associate() and
+**                  createDynamicDataSet() to remove unnecessary arguments.
 */
 
 /*
@@ -326,88 +328,88 @@
     *
     * @see TemporaryBuffer#associate(TableParameter, Temporary, boolean, boolean)
     */
-   public static void associate(final DataSetParameter dsLocal,
-                                final DataSet dsTarget,
-                                boolean input,
-                                boolean output)
-   {
-      associate(dsLocal, dsTarget, input, output, false, ParameterOption.NONE);
-   }
-   
-   /**
-    * Associate two {@code DataSet} with one another, such that all relevant records can be copied
-    * between them at prescribed times.
-    * 
-    * @param   dsSrc
-    *          The local object relative to the current procedure. If this is an INPUT parameter
-    *          then this is the 'source'. This encapsulates the actual parameter sent by caller.
-    * @param   dsTarget
-    *          A dataset defined in current external procedure. 
-    * @param   input
-    *          If {@code true}, records are copied from source table to destination table
-    *          immediately upon invocation of this method, otherwise they are not.
-    * @param   output
-    *          When {@code true}, the records are copied from destination table back to source
-    *          table when the current transaction or subtransaction is committed;  otherwise they
-    *          are not.
-    * @param   append
-    *          Use {@code true} to add new records without affecting existing records.<br>
-    *          Use {@code false} to remove all existing records before the copy takes place. This
-    *          option affects the {@code input} copy only, not the {@code output} copy.
-    * 
-    * @see TemporaryBuffer#associate(TableParameter, Temporary, boolean, boolean) 
-    */
-   public static void associate(final DataSetParameter dsSrc,
-                                final DataSet dsTarget,
-                                boolean input,
-                                boolean output,
-                                boolean append)
-   {
-      associate(dsSrc, dsTarget, input, output, append, null);
-   }
-   
-   /**
-    * Associate two {@code DataSet} with one another, such that all relevant records can be copied
-    * between them at prescribed times.
-    * 
-    * @param   dsSrc
-    *          The local object relative to the current procedure. If this is an INPUT parameter
-    *          then this is the 'source'. This encapsulates the actual parameter sent by caller.
-    * @param   dsTarget
-    *          A dataset defined in current external procedure. 
-    * @param   input
-    *          If {@code true}, records are copied from source table to destination table
-    *          immediately upon invocation of this method, otherwise they are not.
-    * @param   output
-    *          When {@code true}, the records are copied from destination table back to source
-    *          table when the current transaction or subtransaction is committed;  otherwise they
-    *          are not.
-    * @param   append
-    *          Use {@code true} to add new records without affecting existing records.<br>
-    *          Use {@code false} to remove all existing records before the copy takes place. This
-    *          option affects the {@code input} copy only, not the {@code output} copy.
-    * @param   mode
-    *          Parameter passing mode BY-VALUE, BIND or BY-REFERENCE option.
+   public static void associate(DataSetParameter dsLocal, DataSet dsTarget, boolean input, boolean output)
+   {
+      associate(dsLocal, dsTarget, input, output, ParameterOption.NONE);
+   }
+   
+   /**
+    * Associate two {@code DataSet} with one another, such that all relevant records can be copied
+    * between them at prescribed times.
+    * 
+    * @param   dsSrc
+    *          The local object relative to the current procedure. If this is an INPUT parameter
+    *          then this is the 'source'. This encapsulates the actual parameter sent by caller.
+    * @param   dsTarget
+    *          A dataset defined in current external procedure. 
+    * @param   input
+    *          If {@code true}, records are copied from source table to destination table
+    *          immediately upon invocation of this method, otherwise they are not.
+    * @param   output
+    *          When {@code true}, the records are copied from destination table back to source
+    *          table when the current transaction or subtransaction is committed;  otherwise they
+    *          are not.
+    * @param   option
+    *          Dataset parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
     * 
     * @see TemporaryBuffer#associate(TableParameter, Temporary, boolean, boolean)
     */
+   public static void associate(DataSetParameter dsSrc,
+                                DataSet          dsTarget,
+                                boolean          input,
+                                boolean          output,
+                                ParameterOption  option)
+   {
+      dsSrc.setInputMode(input);
+      dsSrc.setOutputMode(output);
+      
+      associate(dsSrc, dsTarget, option);
+   }
+
+   /**
+    * Associate two {@code DataSet} with one another, such that all relevant records can be copied
+    * between them at prescribed times.
+    *
+    * @param   dsLocal
+    *          The local object relative to the current procedure. If this is an INPUT parameter
+    *          then this is the 'source'. This encapsulates the actual parameter sent by caller.
+    * @param   dsTarget
+    *          A dataset defined in current external procedure. 
+    *
+    * @see TemporaryBuffer#associate(TableParameter, Temporary)
+    */
+   public static void associate(DataSetParameter dsLocal, DataSet dsTarget)
+   {
+      associate(dsLocal, dsTarget, ParameterOption.NONE);
+   }
+   
+   /**
+    * Associate two {@code DataSet} with one another, such that all relevant records can be copied
+    * between them at prescribed times.
+    * 
+    * @param   dsSrc
+    *          The local object relative to the current procedure. If this is an INPUT parameter
+    *          then this is the 'source'. This encapsulates the actual parameter sent by caller.
+    * @param   dsTarget
+    *          A dataset defined in current external procedure. 
+    * @param   option
+    *          Dataset parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    * 
+    * @see TemporaryBuffer#associate(TableParameter, Temporary)
+    */
    public static void associate(final DataSetParameter dsSrc,
-                                final DataSet dsTarget,
-                                boolean input,
-                                boolean output,
-                                boolean append,
-                                ParameterOption mode)
+                                final DataSet          dsTarget,
+                                ParameterOption        option)
    {
-      if (!(input || output))
-      {
-         throw new IllegalStateException("Parameter mode (INPUT/OUTPUT) was not specified!");
-      }
+      boolean input  = dsSrc.isInputMode();
+      boolean output = dsSrc.isOutputMode();
+      boolean append = (option == ParameterOption.APPEND);
       
       StaticDataSet localDS = (StaticDataSet) dsTarget;
       if (!dsSrc.isRemoteParameter())
       {
-         DataSet sourceDS = dsSrc.getOriginalDataSet();
-         if (dsSrc.isBind() && mode == ParameterOption.BIND && 
+         DataSet sourceDS = (DataSet) dsSrc.getOriginalDataSet();
+         if (dsSrc.isBind() && option == ParameterOption.BIND && 
              sourceDS != null && !localDS.isReferenceOnly())
          {
             if (sourceDS.isReferenceOnly())
@@ -424,11 +426,11 @@
          }
          if (localDS.isReferenceOnly())
          {
-            if (dsSrc.isByReference() || dsSrc.isBind() || mode == ParameterOption.BIND)
+            if (dsSrc.isByReference() || dsSrc.isBind() || option == ParameterOption.BIND)
             {
                // temporary bind dsSrc.getDataset() to dsTarget
                localDS.bind(dsSrc.getDataset(), input, output, 
-                            dsSrc.isBind() || mode == ParameterOption.BIND);
+                            dsSrc.isBind() || option == ParameterOption.BIND);
                {
                   // TODO: check for both usages of [bind] what happens if the check fails for a
                   //       subsequent parameter in case there are many DataSet: roll back the
@@ -481,7 +483,7 @@
          }
          // NONE to BIND will do nothing, unless the REFERENCE-ONLY is used at target
          // BIND to NONE will error out
-         else if (dsSrc.isBind() && mode == ParameterOption.NONE)
+         else if (dsSrc.isBind() && option == ParameterOption.NONE)
          {
             ErrorManager.recordOrThrowError(13009, "BIND failed TODO: correct msg");
             return;
@@ -586,22 +588,39 @@
     * a method/function in order to properly convert the {@code PARAMETER DATASET-HANDLE}
     * constructs.
     *
-    * @param   dsParam
-    *          The {@code DataSetParameter}. This is the actual parameter for the routine.
-    * @param   dsHandle
-    *          A handle to store the reference to the new {@code DataSet} during the scope of this
-    *          routine.
-    * @param   input
-    *          If {@code true} then this is an {@code INPUT} or {@code INPUT-OUTPUT} parameter.
-    * @param   output
-    *          If {@code true} then this is an {@code OUTPUT} or {@code INPUT-OUTPUT} parameter.
+    * @param    dsParam
+    *           The {@code DataSetParameter}. This is the actual parameter for the routine.
+    * @param    dsHandle
+    *           A handle to store the reference to the new {@code DataSet} during the scope of this
+    *           routine.
+    */
+   public static void createDynamicDataSet(DataSetParameter dsParam, handle dsHandle)
+   {
+      createDynamicDataSet(dsParam, dsHandle, ParameterOption.NONE);
+   }
+   
+   /**
+    * Dynamically create a {@code DataSet} from the {@code DataSetParameter} provided. The new
+    * {@code DataSet} will be stored in {@code dsHandle}. This method is used at the beginning of
+    * a method/function in order to properly convert the {@code PARAMETER DATASET-HANDLE}
+    * constructs.
+    *
+    * @param    dsParam
+    *           The {@code DataSetParameter}. This is the actual parameter for the routine.
+    * @param    dsHandle
+    *           A handle to store the reference to the new {@code DataSet} during the scope of this
+    *           routine.
+    * @param    input
+    *           If {@code true} then this is an {@code INPUT} or {@code INPUT-OUTPUT} parameter.
+    * @param    output
+    *           If {@code true} then this is an {@code OUTPUT} or {@code INPUT-OUTPUT} parameter.
     */
    public static void createDynamicDataSet(DataSetParameter dsParam,
-                                           handle dsHandle,
-                                           boolean input,
-                                           boolean output)
+                                           handle           dsHandle,
+                                           boolean          input,
+                                           boolean          output)
    {
-      createDynamicDataSet(dsParam, dsHandle, input, output, false, ParameterOption.NONE);
+      createDynamicDataSet(dsParam, dsHandle, input, output, ParameterOption.NONE);
    }
    
    /**
@@ -619,25 +638,43 @@
     *          If {@code true} then this is an {@code INPUT} or {@code INPUT-OUTPUT} parameter.
     * @param   output
     *          If {@code true} then this is an {@code OUTPUT} or {@code INPUT-OUTPUT} parameter.
-    * @param   append
-    *          {@code true} to add new records without affecting existing records;
-    *          {@code false} to remove all existing records before the copy takes place. This
-    *          option affects the {@code input} copy only, not the {@code output} copy.
-    * @param   mode
-    *          Parameter passing mode BY-VALUE, BIND or BY-REFERENCE option.
+    * @param   option
+    *          Dataset parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
     */
    public static void createDynamicDataSet(DataSetParameter dsParam,
-                                           handle dsHandle,
-                                           boolean input,
-                                           boolean output,
-                                           boolean append,
-                                           ParameterOption mode)
+                                           handle           dsHandle,
+                                           boolean          input,
+                                           boolean          output,
+                                           ParameterOption  option)
    {
-      if (!(input || output))
-      {
-         throw new IllegalStateException("Parameter mode (INPUT/OUTPUT) was not specified!");
-      }
+      dsParam.setInputMode(input);
+      dsParam.setOutputMode(output);
       
+      createDynamicDataSet(dsParam, dsHandle, option);
+   }
+
+   /**
+    * Dynamically create a {@code DataSet} from the {@code DataSetParameter} provided. The new
+    * {@code DataSet} will be stored in {@code dsHandle}. This method is used at the beginning of
+    * a method/function in order to properly convert the {@code PARAMETER DATASET-HANDLE}
+    * constructs.
+    *
+    * @param   dsParam
+    *          The {@code DataSetParameter}. This is the actual parameter for the routine (src).
+    * @param   dsHandle
+    *          A handle to store the reference to the new {@code DataSet} during the scope of this
+    *          routine (target).
+    * @param   option
+    *          Dataset parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public static void createDynamicDataSet(DataSetParameter dsParam,
+                                           handle           dsHandle,
+                                           ParameterOption  option)
+   {
+      boolean input  = dsParam.isInputMode();
+      boolean output = dsParam.isOutputMode();
+      boolean append = (option == ParameterOption.APPEND);
+
       if (output)
       {
          TransactionManager.registerCurrent(dsHandle);
@@ -5468,7 +5505,7 @@
          
          SelfManager.pushSelf(self);
          // reference already assumes input-output, do not emit append
-         DataSetParameter dsp = new DataSetParameter(this, false, ParameterOption.BY_REFERENCE);
+         DataSetParameter dsp = new DataSetParameter(this, ParameterOption.BY_REFERENCE);
          if (cbd.getObject() != null)
          {
             // TODO:

=== modified file 'src/com/goldencode/p2j/persist/DataSetParameter.java'
--- old/src/com/goldencode/p2j/persist/DataSetParameter.java	2021-08-31 10:54:19 +0000
+++ new/src/com/goldencode/p2j/persist/DataSetParameter.java	2022-05-12 15:03:49 +0000
@@ -27,6 +27,7 @@
 **                  with no input data rows, which allows to access table structure if non-unknown
 **                  table/dataset handle has been passed from the calling side.
 **     CA  20210831 Fixed deserialization of a remote dataset which has data in the before-table(s).
+**     GES 20210501 Reworked to enable subclasses which reduce parameter processing from common use cases.
 */
 
 /*
@@ -89,21 +90,24 @@
 import java.util.*;
 
 /**
- * Container for DataSet reference/handle and calling-side APPEND option. Serves two purposes:
- *    1. passes calling-side APPEND option to the called side;
- *    2. implements interchangeability between DATASET and DATASET-HANDLE parameters (in 4GL you
- *       can pass DATASET parameter to a procedure that takes DATASET-HANDLE and vice versa).
+ * Represents a dataset or dataset-handle parameter, the mode (INPUT, INPUT-OUTPUT or OUTPUT) and any
+ * parameter options (APPEND, BY-VALUE, BIND or BY-REFERENCE).  Allows subclasses to specialize this
+ * behavior.  A core feature is to implement interchangeability between DATASET and DATASET-HANDLE parameters
+ * (in the 4GL you can pass a DATASET parameter to a procedure that takes DATASET-HANDLE and vice versa).
+ * <p>
+ * The parameter mode can be implemented in this class or it can be overridden in a subclass.  If it is
+ * implemented here, then the input and output flags must be passed to the constructor.  If the subclass
+ * overrides the {@link #isInputMode} and {@link #isOutputMode} methods then the input and output boolean
+ * members will be ignored.  This is sub-optimal but it was done deliberately to enable the base class to
+ * be used as a full replacement for any of the subclasses.
  */
 public class DataSetParameter
 {
    /** Reference of the wrapped dataset. */
    private final handle /*DataSet*/ dataset;
    
-   /** Corresponds calling-side APPEND option. */
-   private final boolean append;
-   
    /** Parameter passing mode BY-VALUE, BIND or BY-REFERENCE option. */
-   private final ParameterOption mode;
+   private final ParameterOption option;
    
    /** Determines if this is a remote (passed to appserver) parameter. */
    private boolean remoteParameter = false;
@@ -123,6 +127,12 @@
 
    /** The original dataset passed as an argument. */
    private DataSet originalDataSet;
+
+   /** {@code true} if the parameter mode is INPUT or INPUT-OUTPUT. */   
+   private boolean input = false;   
+   
+   /** {@code true} if the parameter mode is OUTPUT or INPUT-OUTPUT. */
+   private boolean output = false;
    
    /**
     * Create a new parameter for the REMOTE dataset.
@@ -133,8 +143,7 @@
    public DataSetParameter(DatasetWrapper wrapper)
    {
       this.datasetWrapper = wrapper;
-      this.mode = ParameterOption.NONE;
-      this.append = wrapper.isAppend();
+      this.option = wrapper.isAppend() ? ParameterOption.APPEND : ParameterOption.NONE;
       this.remoteParameter = true;
       
       dataset = TypeFactory.handle();
@@ -152,7 +161,7 @@
     */
    public DataSetParameter(DataSetParameter dataset)
    {
-      this(dataset.getDataset(), false, ParameterOption.NONE);
+      this(dataset.getDataset(), ParameterOption.NONE);
    }
    
    /**
@@ -163,7 +172,7 @@
     */
    public DataSetParameter(DataSet dataset)
    {
-      this(dataset, false, ParameterOption.NONE);
+      this(dataset, ParameterOption.NONE);
    }
    
    /**
@@ -171,71 +180,77 @@
     *
     * @param   dataset
     *          Reference of the wrapped dataset.
-    * @param   append
-    *          Determines if APPEND option was specified on the calling side.
+    * @param   option
+    *          Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
     */
-   public DataSetParameter(DataSet dataset, boolean append)
+   public DataSetParameter(DataSet dataset, ParameterOption option)
    {
-      this(dataset, append, ParameterOption.NONE);
+      this(dataset, option, false, false);
    }
-   
+      
    /**
     * Create an instance using a dataset reference.
     *
-    * @param   dataset
-    *          Reference of the wrapped dataset.
-    * @param   append
-    *          Determines if APPEND option was specified on the calling side.
-    * @param   mode
-    *          Parameter passing mode BY-VALUE, BIND or BY-REFERENCE option.
-    */
-   public DataSetParameter(DataSet dataset, boolean append, ParameterOption mode)
+    * @param    dataset
+    *           Reference of the wrapped dataset.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    * @param    input           
+    *           {@code true} if the parameter mode is INPUT or INPUT-OUTPUT.
+    * @param    output
+    *           {@code true} if the parameter mode is OUTPUT or INPUT-OUTPUT.
+    */     
+   public DataSetParameter(DataSet dataset, ParameterOption option, boolean input, boolean output)
    {
       this.originalDataSet = dataset;
-      this.dataset = new handle(dataset);
-      this.append = append;
-      this.mode = mode; // TODO: check default value for mode if ParameterOption.NONE is passed in
+      this.dataset         = new handle(dataset);
+      this.option          = option; // TODO: check default value for mode if ParameterOption.NONE is passed in
+      this.input           = input;
+      this.output          = output;
    }
    
    /**
     * Create an instance using a dataset handle.
     *
-    * @param   dsHandle
-    *          Handle to the wrapped dataset.
+    * @param    dsHandle
+    *           Handle to the wrapped dataset.
     */
    public DataSetParameter(handle dsHandle)
    {
-      this(dsHandle, false, ParameterOption.NONE);
+      this(dsHandle, ParameterOption.NONE);
    }
    
    /**
     * Create an instance using a dataset handle.
     *
-    * @param   dsHandle
-    *          Handle to the wrapped dataset.
-    * @param   append
-    *          Determines if APPEND option was specified on the calling side.
+    * @param    dsHandle
+    *           Handle to the wrapped dataset.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
     */
-   public DataSetParameter(handle dsHandle, boolean append)
+   public DataSetParameter(handle dsHandle, ParameterOption option)
    {
-      this(dsHandle, append, ParameterOption.NONE);
+      this(dsHandle, option, false, false);
    }
    
    /**
     * Create an instance using a dataset handle.
     *
-    * @param   dsHandle
-    *          Handle to the wrapped dataset.
-    * @param   append
-    *          Determines if APPEND option was specified on the calling side.
-    * @param   mode
-    *          Parameter passing mode BY-VALUE, BIND or BY-REFERENCE option.
+    * @param    dsHandle
+    *           Handle to the wrapped dataset.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    * @param    input
+    *           {@code true} if the parameter mode is INPUT or INPUT-OUTPUT.
+    * @param    output
+    *           {@code true} if the parameter mode is OUTPUT or INPUT-OUTPUT.
     */
-   public DataSetParameter(handle dsHandle, boolean append, ParameterOption mode)
+   public DataSetParameter(handle dsHandle, ParameterOption option, boolean input, boolean output)   
    {
       this.dataset = dsHandle;
-      this.append = append;
-      this.mode = mode; // TODO: check default value for mode if ParameterOption.NONE is passed in
+      this.option  = option; // TODO: check default value for mode if ParameterOption.NONE is passed in
+      this.input   = input;
+      this.output  = output;
    }
 
    /**
@@ -257,8 +272,50 @@
    {
       return originalDataSet;
    }
+
+   /**
+    * Determines if this parameter is INPUT or INPUT-OUTPUT.
+    *
+    * @return   {@code true} if this is INPUT or INPUT-OUTPUT.
+    */
+   public boolean isInputMode()
+   {
+      return input;
+   }
+
+   /**
+    * Set the flag for whether this parameter is INPUT or INPUT-OUTPUT.
+    *
+    * @param    input
+    *           {@code true} if this is INPUT or INPUT-OUTPUT.
+    */
+   public void setInputMode(boolean input)
+   {
+      this.input = input;
+   }
+
+   /**
+    * Determines if this parameter is OUTPUT or INPUT-OUTPUT.
+    *
+    * @return   {@code true} if this is OUTPUT or INPUT-OUTPUT.
+    */
+   public boolean isOutputMode()
+   {
+      return output;
+   }
    
    /**
+    * Set the flag for whether this parameter is OUTPUT or INPUT-OUTPUT.
+    *
+    * @param    output
+    *           {@code true} if this is OUTPUT or INPUT-OUTPUT.
+    */
+   public void setOutputMode(boolean output)
+   {
+      this.output = output;
+   }
+
+   /**
     * Returns the unwrapped dataset instance.
     *
     * @return  see above.
@@ -281,11 +338,11 @@
    /**
     * Checks whether this parameter was passed with {@code APPEND} option.
     * 
-    * @return  {@code true} if this parameter was passed with {@code APPEND} option.
+    * @return   {@code true} if this parameter was passed with {@code APPEND} option.
     */
    public boolean isAppend()
    {
-      return append;
+      return option == ParameterOption.APPEND;
    }
    
    /**
@@ -295,7 +352,7 @@
     */
    public boolean isByReference()
    {
-      return mode == ParameterOption.BY_REFERENCE;
+      return option == ParameterOption.BY_REFERENCE;
    }
    
    /**
@@ -305,7 +362,7 @@
     */
    public boolean isByValue()
    {
-      return mode == ParameterOption.BY_VALUE;
+      return option == ParameterOption.BY_VALUE;
    }
    
    /**
@@ -315,7 +372,7 @@
     */
    public boolean isBind()
    {
-      return mode == ParameterOption.BIND;
+      return option == ParameterOption.BIND;
    }
    
    /**

=== added file 'src/com/goldencode/p2j/persist/InputDataSetHandle.java'
--- old/src/com/goldencode/p2j/persist/InputDataSetHandle.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/persist/InputDataSetHandle.java	2022-05-12 15:03:49 +0000
@@ -0,0 +1,166 @@
+/*
+** Module   : InputDataSetHandle.java
+** Abstract : INPUT DATASET-HANDLE parameter support
+**
+** Copyright (c) 2019-2021, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
+** 001 GES 20210429 Created initial version, by moving code from the parent class.
+*/
+
+/*
+** 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.persist;
+
+import com.goldencode.p2j.util.*;
+
+import java.util.*;
+
+/**
+ * Implements the INPUT DATASET-HANDLE parameter type. This hard codes the input/output flags and delegates
+ * all other processing to the parent class.
+ */
+public class InputDataSetHandle
+extends DataSetParameter
+{
+   /**
+    * Create a new parameter for the REMOTE dataset.
+    * 
+    * @param    wrapper
+    *           The REMOTE dataset.
+    */
+   public InputDataSetHandle(DatasetWrapper wrapper)
+   {
+      super(wrapper);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    */
+   public InputDataSetHandle(InputDataSetHandle dataset)
+   {
+      super(dataset.getDataset(), ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    */
+   public InputDataSetHandle(DataSet dataset)
+   {
+      super(dataset, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    * @param   option
+    *          Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputDataSetHandle(DataSet dataset, ParameterOption option)
+   {
+      super(dataset, option);
+   }
+      
+   /**
+    * Create an instance using a dataset handle.
+    *
+    * @param    dsHandle
+    *           Handle to the wrapped dataset.
+    */
+   public InputDataSetHandle(handle dsHandle)
+   {
+      super(dsHandle, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset handle.
+    *
+    * @param    dsHandle
+    *           Handle to the wrapped dataset.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputDataSetHandle(handle dsHandle, ParameterOption option)
+   {
+      super(dsHandle, option);
+   }
+   
+   /**
+    * Determines if this parameter is INPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code true}.
+    */
+   public boolean isInputMode()
+   {
+      return true;
+   }
+
+   /**
+    * Determines if this parameter is OUTPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code false}.
+    */
+   public boolean isOutputMode()
+   {
+      return false;
+   }
+}

=== added file 'src/com/goldencode/p2j/persist/InputDataSetParameter.java'
--- old/src/com/goldencode/p2j/persist/InputDataSetParameter.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/persist/InputDataSetParameter.java	2022-05-12 15:03:49 +0000
@@ -0,0 +1,166 @@
+/*
+** Module   : InputDataSetParameter.java
+** Abstract : INPUT DATASET parameter support
+**
+** Copyright (c) 2019-2021, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
+** 001 GES 20210429 Created initial version, by moving code from the parent class.
+*/
+
+/*
+** 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.persist;
+
+import com.goldencode.p2j.util.*;
+
+import java.util.*;
+
+/**
+ * Implements the INPUT DATASET parameter type. This hard codes the input/output flags and delegates
+ * all other processing to the parent class.
+ */
+public class InputDataSetParameter
+extends DataSetParameter
+{
+   /**
+    * Create a new parameter for the REMOTE dataset.
+    * 
+    * @param    wrapper
+    *           The REMOTE dataset.
+    */
+   public InputDataSetParameter(DatasetWrapper wrapper)
+   {
+      super(wrapper);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    */
+   public InputDataSetParameter(InputDataSetParameter dataset)
+   {
+      super(dataset.getDataset(), ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    */
+   public InputDataSetParameter(DataSet dataset)
+   {
+      super(dataset, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    * @param   option
+    *          Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputDataSetParameter(DataSet dataset, ParameterOption option)
+   {
+      super(dataset, option);
+   }
+      
+   /**
+    * Create an instance using a dataset handle.
+    *
+    * @param    dsHandle
+    *           Handle to the wrapped dataset.
+    */
+   public InputDataSetParameter(handle dsHandle)
+   {
+      super(dsHandle, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset handle.
+    *
+    * @param    dsHandle
+    *           Handle to the wrapped dataset.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputDataSetParameter(handle dsHandle, ParameterOption option)
+   {
+      super(dsHandle, option);
+   }
+   
+   /**
+    * Determines if this parameter is INPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code true}.
+    */
+   public boolean isInputMode()
+   {
+      return true;
+   }
+
+   /**
+    * Determines if this parameter is OUTPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code false}.
+    */
+   public boolean isOutputMode()
+   {
+      return false;
+   }
+}

=== added file 'src/com/goldencode/p2j/persist/InputOutputDataSetHandle.java'
--- old/src/com/goldencode/p2j/persist/InputOutputDataSetHandle.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/persist/InputOutputDataSetHandle.java	2022-05-12 15:03:49 +0000
@@ -0,0 +1,166 @@
+/*
+** Module   : InputOutputDataSetHandle.java
+** Abstract : INPUT-OUTPUT DATASET-HANDLE parameter support
+**
+** Copyright (c) 2019-2021, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
+** 001 GES 20210429 Created initial version, by moving code from the parent class.
+*/
+
+/*
+** 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.persist;
+
+import com.goldencode.p2j.util.*;
+
+import java.util.*;
+
+/**
+ * Implements the INPUT-OUTPUT DATASET-HANDLE parameter type. This hard codes the input/output flags and
+ * delegates all other processing to the parent class.
+ */
+public class InputOutputDataSetHandle
+extends DataSetParameter
+{
+   /**
+    * Create a new parameter for the REMOTE dataset.
+    * 
+    * @param    wrapper
+    *           The REMOTE dataset.
+    */
+   public InputOutputDataSetHandle(DatasetWrapper wrapper)
+   {
+      super(wrapper);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    */
+   public InputOutputDataSetHandle(InputOutputDataSetHandle dataset)
+   {
+      super(dataset.getDataset(), ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    */
+   public InputOutputDataSetHandle(DataSet dataset)
+   {
+      super(dataset, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    * @param   option
+    *          Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputOutputDataSetHandle(DataSet dataset, ParameterOption option)
+   {
+      super(dataset, option);
+   }
+      
+   /**
+    * Create an instance using a dataset handle.
+    *
+    * @param    dsHandle
+    *           Handle to the wrapped dataset.
+    */
+   public InputOutputDataSetHandle(handle dsHandle)
+   {
+      super(dsHandle, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset handle.
+    *
+    * @param    dsHandle
+    *           Handle to the wrapped dataset.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputOutputDataSetHandle(handle dsHandle, ParameterOption option)
+   {
+      super(dsHandle, option);
+   }
+   
+   /**
+    * Determines if this parameter is INPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code true}.
+    */
+   public boolean isInputMode()
+   {
+      return true;
+   }
+
+   /**
+    * Determines if this parameter is OUTPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code true}.
+    */
+   public boolean isOutputMode()
+   {
+      return true;
+   }
+}

=== added file 'src/com/goldencode/p2j/persist/InputOutputDataSetParameter.java'
--- old/src/com/goldencode/p2j/persist/InputOutputDataSetParameter.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/persist/InputOutputDataSetParameter.java	2022-05-12 15:03:49 +0000
@@ -0,0 +1,166 @@
+/*
+** Module   : InputOutputDataSetParameter.java
+** Abstract : INPUT-OUTPUT DATASET-HANDLE parameter support
+**
+** Copyright (c) 2019-2021, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
+** 001 GES 20210429 Created initial version, by moving code from the parent class.
+*/
+
+/*
+** 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.persist;
+
+import com.goldencode.p2j.util.*;
+
+import java.util.*;
+
+/**
+ * Implements the INPUT-OUTPUT DATASET parameter type. This hard codes the input/output flags and delegates
+ * all other processing to the parent class.
+ */
+public class InputOutputDataSetParameter
+extends DataSetParameter
+{
+   /**
+    * Create a new parameter for the REMOTE dataset.
+    * 
+    * @param    wrapper
+    *           The REMOTE dataset.
+    */
+   public InputOutputDataSetParameter(DatasetWrapper wrapper)
+   {
+      super(wrapper);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    */
+   public InputOutputDataSetParameter(InputOutputDataSetParameter dataset)
+   {
+      super(dataset.getDataset(), ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    */
+   public InputOutputDataSetParameter(DataSet dataset)
+   {
+      super(dataset, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    * @param   option
+    *          Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputOutputDataSetParameter(DataSet dataset, ParameterOption option)
+   {
+      super(dataset, option);
+   }
+      
+   /**
+    * Create an instance using a dataset handle.
+    *
+    * @param    dsHandle
+    *           Handle to the wrapped dataset.
+    */
+   public InputOutputDataSetParameter(handle dsHandle)
+   {
+      super(dsHandle, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset handle.
+    *
+    * @param    dsHandle
+    *           Handle to the wrapped dataset.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputOutputDataSetParameter(handle dsHandle, ParameterOption option)
+   {
+      super(dsHandle, option);
+   }
+   
+   /**
+    * Determines if this parameter is INPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code true}.
+    */
+   public boolean isInputMode()
+   {
+      return true;
+   }
+
+   /**
+    * Determines if this parameter is OUTPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code true}.
+    */
+   public boolean isOutputMode()
+   {
+      return true;
+   }
+}

=== added file 'src/com/goldencode/p2j/persist/OutputDataSetHandle.java'
--- old/src/com/goldencode/p2j/persist/OutputDataSetHandle.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/persist/OutputDataSetHandle.java	2022-05-12 15:03:49 +0000
@@ -0,0 +1,166 @@
+/*
+** Module   : OutputDataSetHandle.java
+** Abstract : OUTPUT DATASET-HANDLE parameter support
+**
+** Copyright (c) 2019-2021, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
+** 001 GES 20210429 Created initial version, by moving code from the parent class.
+*/
+
+/*
+** 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.persist;
+
+import com.goldencode.p2j.util.*;
+
+import java.util.*;
+
+/**
+ * Implements the OUTPUT DATASET-HANDLE parameter type. This hard codes the input/output flags and delegates
+ * all other processing to the parent class.
+ */
+public class OutputDataSetHandle
+extends DataSetParameter
+{
+   /**
+    * Create a new parameter for the REMOTE dataset.
+    * 
+    * @param    wrapper
+    *           The REMOTE dataset.
+    */
+   public OutputDataSetHandle(DatasetWrapper wrapper)
+   {
+      super(wrapper);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    */
+   public OutputDataSetHandle(OutputDataSetHandle dataset)
+   {
+      super(dataset.getDataset(), ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    */
+   public OutputDataSetHandle(DataSet dataset)
+   {
+      super(dataset, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    * @param   option
+    *          Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public OutputDataSetHandle(DataSet dataset, ParameterOption option)
+   {
+      super(dataset, option);
+   }
+      
+   /**
+    * Create an instance using a dataset handle.
+    *
+    * @param    dsHandle
+    *           Handle to the wrapped dataset.
+    */
+   public OutputDataSetHandle(handle dsHandle)
+   {
+      super(dsHandle, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset handle.
+    *
+    * @param    dsHandle
+    *           Handle to the wrapped dataset.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public OutputDataSetHandle(handle dsHandle, ParameterOption option)
+   {
+      super(dsHandle, option);
+   }
+   
+   /**
+    * Determines if this parameter is INPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code false}.
+    */
+   public boolean isInputMode()
+   {
+      return false;
+   }
+
+   /**
+    * Determines if this parameter is OUTPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code true}.
+    */
+   public boolean isOutputMode()
+   {
+      return true;
+   }
+}

=== added file 'src/com/goldencode/p2j/persist/OutputDataSetParameter.java'
--- old/src/com/goldencode/p2j/persist/OutputDataSetParameter.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/persist/OutputDataSetParameter.java	2022-05-12 15:03:49 +0000
@@ -0,0 +1,166 @@
+/*
+** Module   : OutputDataSetParameter.java
+** Abstract : OUTPUT DATASET parameter support
+**
+** Copyright (c) 2019-2021, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
+** 001 GES 20210429 Created initial version, by moving code from the parent class.
+*/
+
+/*
+** 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.persist;
+
+import com.goldencode.p2j.util.*;
+
+import java.util.*;
+
+/**
+ * Implements the OUTPUT DATASET parameter type. This hard codes the input/output flags and delegates
+ * all other processing to the parent class.
+ */
+public class OutputDataSetParameter
+extends DataSetParameter
+{
+   /**
+    * Create a new parameter for the REMOTE dataset.
+    * 
+    * @param    wrapper
+    *           The REMOTE dataset.
+    */
+   public OutputDataSetParameter(DatasetWrapper wrapper)
+   {
+      super(wrapper);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    */
+   public OutputDataSetParameter(OutputDataSetParameter dataset)
+   {
+      super(dataset.getDataset(), ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    */
+   public OutputDataSetParameter(DataSet dataset)
+   {
+      super(dataset, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset reference.
+    *
+    * @param   dataset
+    *          Reference of the wrapped dataset.
+    * @param   option
+    *          Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public OutputDataSetParameter(DataSet dataset, ParameterOption option)
+   {
+      super(dataset, option);
+   }
+      
+   /**
+    * Create an instance using a dataset handle.
+    *
+    * @param    dsHandle
+    *           Handle to the wrapped dataset.
+    */
+   public OutputDataSetParameter(handle dsHandle)
+   {
+      super(dsHandle, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a dataset handle.
+    *
+    * @param    dsHandle
+    *           Handle to the wrapped dataset.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public OutputDataSetParameter(handle dsHandle, ParameterOption option)
+   {
+      super(dsHandle, option);
+   }
+   
+   /**
+    * Determines if this parameter is INPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code false}.
+    */
+   public boolean isInputMode()
+   {
+      return false;
+   }
+
+   /**
+    * Determines if this parameter is OUTPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code true}.
+    */
+   public boolean isOutputMode()
+   {
+      return true;
+   }
+}

=== modified file 'src/com/goldencode/p2j/persist/QueryWrapper.java'
--- old/src/com/goldencode/p2j/persist/QueryWrapper.java	2022-01-14 23:11:41 +0000
+++ new/src/com/goldencode/p2j/persist/QueryWrapper.java	2022-05-12 15:03:49 +0000
@@ -257,6 +257,7 @@
 **                           buffer list).
 **     CA  20210923          assign(CompoundQuery) must not reset the query.  Removed the 'closing' flag.
 **     OM  20220112          Added implementation of SKIP-DELETED-RECORD attribute.
+**     GES 20210501          Upgraded dataset parameter processing to match API changes.
 */
 
 /*
@@ -6893,7 +6894,7 @@
          SelfManager.pushSelf(self);
          
          // reference already assumes input-output, do not emit append
-         DataSetParameter dsp = new DataSetParameter(getDataSet(), false, ParameterOption.BY_REFERENCE);
+         DataSetParameter dsp = new DataSetParameter(getDataSet(), ParameterOption.BY_REFERENCE);
          if (cbd.getObject() != null)
          {
             // TODO:

=== modified file 'src/com/goldencode/p2j/persist/StaticDataSet.java'
--- old/src/com/goldencode/p2j/persist/StaticDataSet.java	2021-10-13 22:53:24 +0000
+++ new/src/com/goldencode/p2j/persist/StaticDataSet.java	2022-05-12 15:03:49 +0000
@@ -13,6 +13,7 @@
 **     CA  20201003 Use an identity HashSet where possible.
 **     OM  20211006 Unbinding occurs on procedure return instead of TM's scope finished.
 **     OM  20211011 Improved debugging support for proxy objects.
+**     GES 20210501 Upgraded table-handle parameter processing to match API changes.
 */
 
 /*
@@ -375,17 +376,14 @@
          bound.removeReference(this);
       }
       
-      ParameterOption mode = permanent ? ParameterOption.BIND 
-                                       : ParameterOption.BY_REFERENCE;
+      ParameterOption option = permanent ? ParameterOption.BIND : ParameterOption.BY_REFERENCE;
+      
       for (int i = 0; i < dsDef.getBuffers().size(); i++)
       {
          Temporary defBuf = (Temporary) dsDef.getBuffers().get(i);
          Temporary boundBuf = (Temporary) newBind.getBuffers().get(i);
          
-         TemporaryBuffer.associate(new TableParameter(boundBuf, false, mode), 
-                                   defBuf, 
-                                   input, output, false, 
-                                   mode);
+         TemporaryBuffer.associate(new TableParameter(boundBuf, option, input, output), defBuf, option);
       }
       
       DataSet oldBound = bound;

=== modified file 'src/com/goldencode/p2j/persist/TemporaryBuffer.java'
--- old/src/com/goldencode/p2j/persist/TemporaryBuffer.java	2022-03-30 12:31:18 +0000
+++ new/src/com/goldencode/p2j/persist/TemporaryBuffer.java	2022-05-12 15:03:49 +0000
@@ -559,6 +559,7 @@
 **                           performance improvement.
 **     CA  20220330          Fixed a regression in CA/20220309 - nextPrimaryKey() will consume pending 
 **                           reclaimed PKs until they are exhausted.
+**     GES 20210430          Reworked associate() and createDynamicTable() to remove unnecessary arguments.
 */
 
 /*
@@ -1784,81 +1785,103 @@
       
       return child;
    }
-   
-   /**
-    * Associate two temp tables with one another, such that all relevant
-    * records can be copied between them at prescribed times.
-    *
-    * @param   src
-    *          <code>TableParameter</code> containing reference to the source
-    *          temporary buffer or result set.
-    * @param   dstDMO
-    *          DMO instance returned by a previous call to {@link #define} on
-    *          the destination temporary buffer.
-    * @param   input
-    *          if <code>true</code>, records are copied from source table to
-    *          destination table immediately upon invocation of this method,
-    *          otherwise they are not.
-    * @param   output
-    *          if <code>true</code>, records are copied from destination
-    *          table back to source table when the current transaction or
-    *          subtransaction is committed;  otherwise they are not.
-    *
-    * @throws  ErrorConditionException
-    *          if a recoverable error occurs flushing the source buffer in
-    *          preparation for an input copy or while copying records.
-    * @throws  RuntimeException
-    *          if an unrecoverable error occurs invoking methods on the
-    *          underlying DMOs to get or set data values.
-    */
-   public static void associate(final TableParameter src,
-                                final Temporary dstDMO,
-                                boolean input,
-                                boolean output)
-   {
-      associate(src, dstDMO, input, output, false, ParameterOption.NONE);
-   }
-   
-   /**
-    * Associate two temp tables with one another, such that all relevant
-    * records can be copied between them at prescribed times.
-    *
-    * @param   src
-    *          <code>TableParameter</code> containing reference to the source
-    *          temporary buffer or result set.
-    * @param   dstDMO
-    *          DMO instance returned by a previous call to {@link #define} on
-    *          the destination temporary buffer.
-    * @param   input
-    *          if <code>true</code>, records are copied from source table to
-    *          destination table immediately upon invocation of this method,
-    *          otherwise they are not.
-    * @param   output
-    *          if <code>true</code>, records are copied from destination
-    *          table back to source table when the current transaction or
-    *          subtransaction is committed;  otherwise they are not.
-    * @param   appendInput
-    *          <code>true</code> to add new records without affecting
-    *          existing records;  <code>false</code> to remove all existing
-    *          records before the copy takes place.  This option affects the
-    *          <code>input</code> copy only, not the <code>output</code> copy.
-    *
-    * @throws  ErrorConditionException
-    *          if a recoverable error occurs flushing the source buffer in
-    *          preparation for an input copy or while copying records.
-    * @throws  RuntimeException
-    *          if an unrecoverable error occurs invoking methods on the
-    *          underlying DMOs to get or set data values.
-    */
-   public static void associate(final TableParameter src,
-                                final Temporary dstDMO,
-                                boolean input,
-                                boolean output,
-                                boolean appendInput)
-   {
-      associate(src, dstDMO, input, output, appendInput, ParameterOption.NONE);
-   }
-   
+
+   /**
+    * Associate two temp tables with one another, such that all relevant
+    * records can be copied between them at prescribed times.
+    *
+    * @param    src
+    *           <code>TableParameter</code> containing reference to the source
+    *           temporary buffer or result set.
+    * @param    dstDMO
+    *           DMO instance returned by a previous call to {@link #define} on
+    *           the destination temporary buffer.
+    *
+    * @throws   ErrorConditionException
+    *           if a recoverable error occurs flushing the source buffer in
+    *           preparation for an input copy or while copying records.
+    * @throws   RuntimeException
+    *           if an unrecoverable error occurs invoking methods on the
+    *           underlying DMOs to get or set data values.
+    */
+   public static void associate(TableParameter src, Temporary dstDMO)
+   {
+      associate(src, dstDMO, ParameterOption.NONE);
+   }
+   
+   /**
+    * Associate two temp tables with one another, such that all relevant
+    * records can be copied between them at prescribed times.
+    *
+    * @param    src
+    *           <code>TableParameter</code> containing reference to the source
+    *           temporary buffer or result set.
+    * @param    dstDMO
+    *           DMO instance returned by a previous call to {@link #define} on
+    *           the destination temporary buffer.
+    * @param    input
+    *           if <code>true</code>, records are copied from source table to
+    *           destination table immediately upon invocation of this method,
+    *           otherwise they are not.
+    * @param    output
+    *           if <code>true</code>, records are copied from destination
+    *           table back to source table when the current transaction or
+    *           subtransaction is committed;  otherwise they are not.
+    *
+    * @throws   ErrorConditionException
+    *           if a recoverable error occurs flushing the source buffer in
+    *           preparation for an input copy or while copying records.
+    * @throws   RuntimeException
+    *           if an unrecoverable error occurs invoking methods on the
+    *           underlying DMOs to get or set data values.
+    */
+   public static void associate(TableParameter src,
+                                Temporary      dstDMO,
+                                boolean        input,
+                                boolean        output)
+   {
+      associate(src, dstDMO, input, output, ParameterOption.NONE);
+   }
+   
+   /**
+    * Associate two temp tables with one another, such that all relevant records can be copied
+    * between them at prescribed times.
+    *
+    * @param    src
+    *           {@code TableParameter} containing reference to the source temporary buffer or
+    *           result set.
+    * @param    dstDMO
+    *           DMO instance returned by a previous call to {@link #define} on the destination
+    *           temporary buffer.
+    * @param    input
+    *           If {@code true}, records are copied from source table to destination table
+    *           immediately upon invocation of this method, otherwise they are not.
+    * @param    output
+    *           If {@code true}, records are copied from destination table back to source table
+    *           when the current transaction or subtransaction is committed;  otherwise they are
+    *           not.
+    * @param    option
+    *           Parameter passing mode APPEND, BY-VALUE, BIND or BY-REFERENCE option.
+    *
+    * @throws   ErrorConditionException
+    *           if a recoverable error occurs flushing the source buffer in preparation for an
+    *           input copy or while copying records.
+    * @throws   RuntimeException
+    *           if an unrecoverable error occurs invoking methods on the underlying DMOs to get or
+    *           set data values.
+    */
+   public static void associate(TableParameter  src,
+                                Temporary       dstDMO,
+                                boolean         input,
+                                boolean         output,
+                                ParameterOption option)
+   {
+      src.setInputMode(input);
+      src.setOutputMode(output);
+      
+      associate(src, dstDMO, option);
+   }
+
    /**
     * Associate two temp tables with one another, such that all relevant records can be copied
     * between them at prescribed times.
@@ -1869,19 +1892,8 @@
     * @param   dstDMO
     *          DMO instance returned by a previous call to {@link #define} on the destination
     *          temporary buffer.
-    * @param   input
-    *          If {@code true}, records are copied from source table to destination table
-    *          immediately upon invocation of this method, otherwise they are not.
-    * @param   output
-    *          If {@code true}, records are copied from destination table back to source table
-    *          when the current transaction or subtransaction is committed;  otherwise they are
-    *          not.
-    * @param   appendInput
-    *          Use {@code true} to add new records without affecting existing records;
-    *          {@code false} to remove all existing records before the copy takes place. This
-    *          option affects the {@code input} copy only, not the {@code output} copy.
-    * @param   mode
-    *          Parameter passing mode BY-VALUE, BIND or BY-REFERENCE option.
+    * @param   option
+    *          Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
     *
     * @throws  ErrorConditionException
     *          if a recoverable error occurs flushing the source buffer in preparation for an
@@ -1890,12 +1902,7 @@
     *          if an unrecoverable error occurs invoking methods on the underlying DMOs to get or
     *          set data values.
     */
-   public static void associate(final TableParameter src,
-                                Temporary dstDMO,
-                                boolean input,
-                                boolean output,
-                                boolean appendInput,
-                                ParameterOption mode)
+   public static void associate(final TableParameter src, Temporary dstDMO, ParameterOption option)
    {
       if (src.getTable() == (Temporary) ((BufferImpl) dstDMO).ref())
       {
@@ -1903,6 +1910,10 @@
          return;
       }
       
+      boolean input  = src.isInputMode();
+      boolean output = src.isOutputMode();
+      boolean append = (option == ParameterOption.APPEND);
+      
       if (!(input || output))
       {
          throw new IllegalStateException("Parameter mode (INPUT/OUTPUT) was not specified!");
@@ -1937,7 +1948,8 @@
          return;
       }
       
-      boolean bind = src.isBind() || mode == ParameterOption.BIND;
+      boolean bind = src.isBind() || option == ParameterOption.BIND;
+      
       if (src.isByReference() || bind || (!src.isRemoteParameter() && buffer.referenceOnly))
       {
          if (src.getTable() == null)
@@ -1985,14 +1997,10 @@
                else
                {
                   TableParameter beforeParam = new TableParameter((Temporary) hSrcBefore.getResource(),
-                                                                  src.isAppend(),
-                                                                  src.getParameterMode());
-                  associate(beforeParam, 
-                            (Temporary) hDstBefore.getResource(), 
-                            input, 
-                            output, 
-                            appendInput, 
-                            mode);
+                                                                  src.getParameterOption(),
+                                                                  input,
+                                                                  output);
+                  associate(beforeParam, (Temporary) hDstBefore.getResource(), option);
                }
             }
             
@@ -2026,6 +2034,7 @@
             
             // find all other explicit buffers in this table and create a new buffer for them
             Set<Temporary> copy = new HashSet<>(dstBuf.explicitBuffers);
+            
             for (Temporary buf : copy)
             {
                TemporaryBuffer exBuf = (TemporaryBuffer) ((BufferReference) buf).definition();
@@ -2082,6 +2091,7 @@
          if (!src.isRemoteParameter())
          {
             Temporary srcDMO = src.getTable();
+
             if (srcDMO != null)
             {
                TemporaryBuffer srcBuf = (TemporaryBuffer) ((BufferReference) srcDMO).buffer();
@@ -2126,7 +2136,7 @@
                                  ? srcBuf.rowID() 
                                  : null;
                
-               boolean res = copyAllRows(srcRec,srcDMO, dstDMO, appendInput, CopyTableMode.INPUT_PARAM_MODE);
+               boolean res = copyAllRows(srcRec, srcDMO, dstDMO, append, CopyTableMode.INPUT_PARAM_MODE);
                if (!res)
                {
                   BlockManager.returnError();
@@ -2136,13 +2146,13 @@
          else if (src.getResultSet().getXmlTable() != null)
          {
             String xmlTable = src.getResultSet().getXmlTable();
-            readFromXml(xmlTable, appendInput, ((Buffer) dstDMO).tableHandle());
+            readFromXml(xmlTable, append, ((Buffer) dstDMO).tableHandle());
          }
          else
          {
             insertAllRows(src.getResultSet(),
                           dstDMO,
-                          appendInput,
+                          append,
                           CopyTableMode.INPUT_PARAM_MODE,
                           src.getParameterIndex());
          }
@@ -2164,61 +2174,93 @@
          ProcedureManager.executeOnReturn(copier::finished, copier.weight());
       }
    }
-   
-   /**
-    * Creates a dynamic temporary table for the given temp table handle. Intended to handle
-    * conversion of DEFINE PARAMETER TABLE-HANDLE.
-    *
-    * @param   src
-    *          <code>TableParameter</code> containing reference to the source table.
-    * @param   target
-    *          Handle for storing the target table.
-    * @param   input
-    *          if <code>true</code>, records are copied from source table to
-    *          destination table immediately upon invocation of this method,
-    *          otherwise they are not.
-    * @param   output
-    *          if <code>true</code>, records are copied from destination
-    *          table back to source table when the current transaction or
-    *          subtransaction is committed;  otherwise they are not.
-    */
-   public static void createDynamicTable(TableParameter src,
-                                         handle target,
-                                         boolean input,
-                                         boolean output)
-   {
-      createDynamicTable(src, target, input, output, false, ParameterOption.NONE);
-   }
-   
-   /**
-    * Creates a dynamic temporary table for the given temp table handle. Intended to handle
-    * conversion of DEFINE PARAMETER TABLE-HANDLE.
-    *
-    * @param   src
-    *          {@code TableParameter} containing reference to the source table.
-    * @param   target
-    *          Handle for storing the target table.
-    * @param   input
-    *          If {@code true}, records are copied from source table to destination table
-    *          immediately upon invocation of this method, otherwise they are not.
-    * @param   output
-    *          If {@code true}, records are copied from destination table back to source table
-    *          when the current transaction or subtransaction is committed;  otherwise they are
-    *          not.
-    * @param   append
-    *          {@code true} to add new records without affecting existing records; {@code false}
-    *          to remove all existing records before the copy takes place. This option affects the
-    *          {@code input} copy only, not the {@code output} copy.
-    * @param   mode
-    *          Parameter passing mode BY-VALUE, BIND or BY-REFERENCE option.
-    */
-   public static void createDynamicTable(TableParameter src,
-                                         handle target,
-                                         boolean input,
-                                         boolean output,
-                                         boolean append,
-                                         ParameterOption mode)
-   {
+
+   /**
+    * Creates a dynamic temporary table for the given temp table handle. Intended to handle
+    * conversion of DEFINE PARAMETER TABLE-HANDLE.
+    *
+    * @param    src
+    *           <code>TableParameter</code> containing reference to the source table.
+    * @param    target
+    *           Handle for storing the target table.
+    */
+   public static void createDynamicTable(TableParameter src, handle target)
+   {
+      createDynamicTable(src, target, ParameterOption.NONE);
+   }
+   
+   /**
+    * Creates a dynamic temporary table for the given temp table handle. Intended to handle
+    * conversion of DEFINE PARAMETER TABLE-HANDLE.
+    *
+    * @param    src
+    *           <code>TableParameter</code> containing reference to the source table.
+    * @param    target
+    *           Handle for storing the target table.
+    * @param    input
+    *           if <code>true</code>, records are copied from source table to
+    *           destination table immediately upon invocation of this method,
+    *           otherwise they are not.
+    * @param    output
+    *           if <code>true</code>, records are copied from destination
+    *           table back to source table when the current transaction or
+    *           subtransaction is committed;  otherwise they are not.
+    */
+   public static void createDynamicTable(TableParameter src,
+                                         handle         target,
+                                         boolean        input,
+                                         boolean        output)
+   {
+      createDynamicTable(src, target, input, output, ParameterOption.NONE);
+   }
+   
+   /**
+    * Creates a dynamic temporary table for the given temp table handle. Intended to handle
+    * conversion of DEFINE PARAMETER TABLE-HANDLE.
+    *
+    * @param    src
+    *           {@code TableParameter} containing reference to the source table.
+    * @param    target
+    *           Handle for storing the target table.
+    * @param    input
+    *           If {@code true}, records are copied from source table to destination table
+    *           immediately upon invocation of this method, otherwise they are not.
+    * @param    output
+    *           If {@code true}, records are copied from destination table back to source table
+    *           when the current transaction or subtransaction is committed;  otherwise they are
+    *           not.
+    * @param    option
+    *           Parameter passing mode APPEND, BY-VALUE, BIND or BY-REFERENCE option.
+    */
+   public static void createDynamicTable(TableParameter  src,
+                                         handle          target,
+                                         boolean         input,
+                                         boolean         output,
+                                         ParameterOption option)
+   {
+      src.setInputMode(input);
+      src.setOutputMode(output);
+      
+      createDynamicTable(src, target, option);
+   }
+
+   /**
+    * Creates a dynamic temporary table for the given temp table handle. Intended to handle
+    * conversion of DEFINE PARAMETER TABLE-HANDLE.
+    *
+    * @param    src
+    *           {@code TableParameter} containing reference to the source table.
+    * @param    target
+    *           Handle for storing the target table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public static void createDynamicTable(TableParameter src, handle target, ParameterOption option)
+   {
+      boolean input  = src.isInputMode();
+      boolean output = src.isOutputMode();
+      boolean append = (option == ParameterOption.APPEND);
+      
       if (!(input || output))
       {
          throw new IllegalStateException("Parameter mode (INPUT/OUTPUT) was not specified!");

=== modified file 'src/com/goldencode/p2j/schema/SchemaDictionary.java'
--- old/src/com/goldencode/p2j/schema/SchemaDictionary.java	2022-05-01 15:40:07 +0000
+++ new/src/com/goldencode/p2j/schema/SchemaDictionary.java	2022-05-12 15:30:45 +0000
@@ -515,6 +515,9 @@
 **                           profiles to be ran at once, with the conversion switching the state between 
 **                           profiles, when a resource (like a file or namespace) is being processed.  Only
 **                           front phase is supported at this time.
+**     GES 20210208          Added table-level signature calculation (saved as an annotation). Added
+**                           findTableInfo() to provide a set of related data about a matched record. This
+**                           saves multiple calls each having to resolve the same name node lookup.
 */
 
 /*
@@ -1789,6 +1792,9 @@
          copyTableProperties(ast, table);
       }
       
+      // calculate and save the table signature
+      calculateSignature(table);
+
       if (modelTable == null)
       {
          // Temp-table has been defined without using a "like" clause.
@@ -1939,6 +1945,65 @@
    }
    
    /**
+    * Get the Progress parser token type of a table, temp-table, work-table,
+    * or buffer. If <code>name</code> represents a buffer, the type of the
+    * backing record is returned. That is, a type of <code>BUFFER</code> is
+    * never returned, only the type of the record which backs the buffer.
+    * <p>
+    * If both forceTemp and forcePersistent are <code>false</code>, then the lookup is performed
+    * with no preference for temp-table or persistent table.
+    *
+    * @param   name
+    *          Case insensitive record name; may be qualified or not. If
+    *          qualified, only the table portion of the name may be
+    *          abbreviated.
+    * @param   forceTemp
+    *          If <code>true</code>, force a temp-table lookup.
+    * @param   forcePersistent
+    *          If <code>true</code>, force a persistent table lookup.
+    *
+    * @return  One of the Progress parser token types <code>TABLE</code>,
+    *          <code>TEMP_TABLE</code>, or <code>WORK_TABLE</code>, or
+    *          <code>UNKNOWN</code> if <code>name</code> cannot be found.
+    *
+    * @throws  AmbiguousSchemaNameException
+    *          if <code>name</code> is an ambiguous identifier for the
+    *          target record.
+    */
+   public TableInfo findTableInfo(String name, boolean forceTemp, boolean forcePersistent)
+   throws AmbiguousSchemaNameException
+   {
+      // TODO: The recordType lookup used scopes.size() - 1 instead of nearestEnclosingInternal(), is this
+      //       an issue?  It seems like it should have been consistent with the other lookups, so this is
+      //       being made consistent here.
+      int l1 = computeLevel(forceTemp, forcePersistent, true, nearestEnclosingInternal());
+      int l2 = computeLevel(forceTemp, forcePersistent, false, SCHEMA_GLOBAL_SCOPE);
+
+      // Look up record name node.
+      NameNode node = findEntry(name, EntityName.TABLE, l1, l2, false);
+      
+      TableInfo info = null;
+      
+      if (node != null)
+      {
+         NameNode dbNode  = node.getParent();
+         
+         // don't just use getAst() here, we need the AST for the backing table if this is a buffer
+         // (this will work for both the simple case and the buffer case)
+         Aast   ast   = getTable(node); 
+         String sig   = ast != null ? (String) ast.getAnnotation("db_signature") : null;
+         String sname = getQualifiedName(node);
+         String bname = getRecordName(node, EntityName.TABLE, name);
+         String dname = (dbNode != null) ? dbNode.getName() : "";
+         int    ttype = getTable(node).getType();
+         
+         info = new TableInfo(sig, sname, bname, dname, ttype);
+      }
+      
+      return info;
+   }
+   
+   /**
     * Get an object which exposes schema-related information about a field which is referenced in
     * source code.
     * 
@@ -5057,6 +5122,74 @@
    }
    
    /**
+    * Calculate and save the signature for the table.  Each field is represented by "datatype" or
+    * "datatype[extent]" format.  The table level signature is all of the field signatures separated
+    * by "|" delimiter with the number and order of the fields matching the order in the AST.
+    *  
+    * @param    table
+    *           The table AST substree.
+    */
+   private void calculateSignature(Aast table)
+   {
+      String signature = "";
+      
+      Aast next = (Aast) table.getFirstChild();
+
+      // iterate through all fields
+      while (next != null)
+      {
+         int type = next.getType();
+         
+         // if this is a field, calculate the field's portion of the signature
+         if (type >= BEGIN_FIELDTYPES && type <= END_FIELDTYPES ||
+             type >= BEGIN_METATYPES  && type <= END_METATYPES)
+         {
+            Aast ext = next.getImmediateChild(KW_EXTENT, null);
+            
+            String extent = null;
+            
+            // read the extent value if any
+            if (ext != null)
+            {
+               if (ext.getNumImmediateChildren() == 0)
+               {
+                  String err = String.format("Indeterminate EXTENT in a temp-table definition for %s",
+                                             next.dumpTree());
+                  
+                  // indeterminate extent does not make sense for a temp-table, should not happen
+                  throw new RuntimeException(err);
+               }
+               else
+               {
+                  // the only child is a NUM_LITERAL
+                  Aast num = (Aast) ext.getFirstChild();
+                  extent = num.getText();
+               }
+            }
+            
+            // create the field portion of the signature
+            String fsig = SchemaParser.fieldSignature(type, extent);
+            
+            // add it in to the table signature
+            signature += (signature.length() == 0) ? fsig : "|" + fsig;
+         }
+
+         next = (Aast) next.getNextSibling();
+      }
+
+      // save the result
+      if (signature.isEmpty())
+      {
+         // there should always be at least 1 field
+         throw new RuntimeException(String.format("Invalid temp-table, no fields!\n%s", table.dumpTree()));
+      }
+      else
+      {
+         table.putAnnotation("db_signature", signature);
+      }
+   }
+   
+   /**
     * Copy TABLE properties from a source AST to a destination AST. The set
     * of properties (essentially child AST nodes) copied will be limited to
     * those that are valid in a table (filtered by token type). It is assumed

=== added file 'src/com/goldencode/p2j/schema/TableInfo.java'
--- old/src/com/goldencode/p2j/schema/TableInfo.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/schema/TableInfo.java	2022-05-12 15:03:49 +0000
@@ -0,0 +1,166 @@
+/*
+** Module   : TableInfo.java
+** Abstract : Schema information about a database field.
+**
+** Copyright (c) 2021, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- --------------------------------Description----------------------------------
+** 001 GES 20210224 Created initial version.
+*/
+
+/*
+** 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.schema;
+
+/**
+ * A read-only bean which exposes schema-related information associated with a database table
+ * which is referenced in source code.
+ */
+public class TableInfo
+{
+   /** Table signature associated with the table. */
+   private final String signature;
+   
+   /** Fully qualified schema name of the table. */
+   private final String qualified;
+   
+   /** Table's buffer name as indicated in source. */
+   private final String buffer;
+   
+   /** Table's database logical name or alias. */
+   private final String database;
+   
+   /** Table's token type. */
+   private final int recordType;
+   
+   /**
+    * Constructor.
+    * 
+    * @param   signature
+    *          Structural signature for the table.
+    * @param   qualified
+    *          Fully qualified schema name of the table.
+    * @param   buffer
+    *          Name of the buffer indicated by the table reference in source code.
+    * @param   database
+    *          Name of the database which contains the table, as specified by the table reference
+    *          in source code, or looked up if the source reference was unqualified.
+    * @param   recordType
+    *          Token type of the table backing the buffer indicated by the table reference in
+    *          source code.
+    */
+   TableInfo(String signature, String qualified, String buffer, String database, int recordType)
+   {
+      this.signature  = signature;
+      this.qualified  = qualified;
+      this.buffer     = buffer;
+      this.database   = database;
+      this.recordType = recordType;
+   }
+   
+   /**
+    * Get the signature of the table in the schema.
+    * 
+    * @return   Table signature.
+    */
+   public String getSignature()
+   {
+      return signature;
+   }
+   
+   /**
+    * Get the fully qualified schema name of the field.
+    * 
+    * @return  Qualified field name.
+    */
+   public String getQualified()
+   {
+      return qualified;
+   }
+   
+   /**
+    * Get the buffer name associated with the field.
+    * 
+    * @return  Buffer name.
+    */
+   public String getBuffer()
+   {
+      return buffer;
+   }
+   
+   /**
+    * Get the logical database name or alias, if any, associated with the field.
+    * 
+    * @return  The logical name of the database associated with the buffer associated with the
+    *          field. An empty string is returned if the containing table is a temp- or
+    *          work-table.
+    */
+   public String getDatabase()
+   {
+      return database;
+   }
+   
+   /**
+    * Get the Progress parser token type of a table, temp-table, work-table, or buffer which
+    * contains the field. If the record containing the field is a buffer, the type of the backing
+    * record is returned. That is, a type of {@code BUFFER} is never returned, only the type of
+    * the record which backs the buffer.
+    * 
+    * @return  Record token type.
+    */
+   public int getRecordType()
+   {
+      return recordType;
+   }
+}
\ No newline at end of file

=== modified file 'src/com/goldencode/p2j/schema/schema.g'
--- old/src/com/goldencode/p2j/schema/schema.g	2022-04-29 11:01:19 +0000
+++ new/src/com/goldencode/p2j/schema/schema.g	2022-05-12 15:31:40 +0000
@@ -99,10 +99,11 @@
 ** 037 OM  20200530          Fix for datetime[tz].
 ** 038 CA  20200930          Use factory.setASTNodeClass(Class) instead of setASTNodeClass(String), to avoid
 **                           loadClass call.
-** 039 OM  20211122          Added hints-based support for 'unloading' schema.
+**     OM  20211122          Added hints-based support for 'unloading' schema.
 **     GES 20220429          Allow sequences and tables to be intermixed such that sequences do not have to
 **                           precede tables. The 4GL seems to allow this (at least schemata provided by
 **                           customers in compilable applications shows this behavior).
+**     GES 20210208          Added table-level signature calculation and it is saved as an annotation.
 */
 
 /*
@@ -245,6 +246,99 @@
    }
    
    /**
+    * Calculate the signature string for a field.
+    *
+    * @param    type
+    *           The field's data type.
+    * @param    extent
+    *           The extent size (as a num literal) or {@code null} if the field is scalar.
+    *
+    * @return   The signature in "datatype" or "datatype[extent]" format.
+    */
+   public static String fieldSignature(int type, String extent)
+   {
+      String code = null;
+      
+      switch (type)
+      {
+         case FIELD_BLOB:
+            code = "blob";
+            break;
+         case FIELD_CHAR:
+            code = "char";
+            break;
+         case FIELD_CLOB:
+            code = "clob";
+            break;
+         case FIELD_COM_HANDLE:
+            code = "comh";
+            break;
+         case FIELD_DATE:
+            code = "date";
+            break;
+         case FIELD_DATETIME:
+            code = "dt";
+            break;
+         case FIELD_DATETIME_TZ:
+            code = "dttz";
+            break;
+         case FIELD_DEC:
+            code = "dec";
+            break;
+         case FIELD_INT:
+            code = "int";
+            break;
+         case FIELD_INT64:
+            code = "i64";
+            break;
+         case FIELD_LOGICAL:
+            code = "log";
+            break;
+         case FIELD_RECID:
+            code = "rec";
+            break;
+         case FIELD_ROWID:
+            code = "row";
+            break;
+         case FIELD_RAW:
+            code = "raw";
+            break;
+         case FIELD_HANDLE:
+            code = "hndl";
+            break;
+         case FIELD_BIGINT:
+            code = "bigi";
+            break;
+         case FIELD_BYTE:
+            code = "byte";
+            break;
+         case FIELD_DOUBLE:
+            code = "dbl";
+            break;
+         case FIELD_FIXCHAR:
+            code = "fixc";
+            break;
+         case FIELD_FLOAT:
+            code = "flt";
+            break;
+         case FIELD_SHORT:
+            code = "shrt";
+            break;
+         case FIELD_TIMESTAMP:
+            code = "tmst";
+            break;
+         case FIELD_TIME:
+            code = "time";
+            break;
+         default:
+            code = "unkw";
+            break;
+      }
+      
+      return (extent == null) ? code : code + "[" + extent + "]";
+   }
+   
+   /**
     * Configure the hidden filter to ensure that token types that are not
     * normally recognized by the parser, are hidden (but still accessible)
     * using the filter.
@@ -703,7 +797,11 @@
  */
 table
    :
-      { astFactory.makeASTRoot(currentAST, #[TABLE]); }
+      {
+         astFactory.makeASTRoot(currentAST, #[TABLE]);
+         String signature = "";
+         String fsig = null;
+      }
       (
          KW_ADD! tab:KW_TABLE! s:STRING! (y:KW_TYPE! d:SYMBOL!)?
          {
@@ -726,13 +824,17 @@
          (
             options { generateAmbigWarnings = false; }
             :
-              field
+              fsig=field
+              {
+                 signature += (signature.length() == 0) ? fsig : "|" + fsig;
+              }
             | index
          )*
       )
       {
          currentTable = null;
          forceLineColumnNums(#tab, ##);
+         ##.putAnnotation("db_signature", signature);
       }
    ;
    
@@ -807,15 +909,18 @@
  * definition would always be a child of the most recently parsed table
  * definition.
  *
- * @throws  SemanticException
- *          if this field definition reports a different parent table name
- *          from the table definition most recently parsed.
+ * @return   The signature for this field in "datatype" or "datatype[extent]" format.
+ *
+ * @throws   SemanticException
+ *           if this field definition reports a different parent table name
+ *           from the table definition most recently parsed.
  */
-field
+field returns [String sig = null]
    :
       {
          int    ftype;
          String fmt = null;
+         String ext = null;
       }
       (
          KW_ADD! f:KW_FIELD^ s:STRING!
@@ -853,7 +958,7 @@
             | field_trigger
             | help
             | help_sa
-            | extent
+            | ext=extent
             | decimals
             | length
             | viewAs
@@ -879,6 +984,9 @@
             | unknownOption!
          )*
       )
+      {
+         sig = fieldSignature(ftype, ext);
+      }
    ;
    
 /**
@@ -1739,10 +1847,12 @@
  * <p>
  * The resulting sub-tree is the <code>KW_EXTENT</code> with a child of
  * <code>NUM_LITERAL</code>.
+ *
+ * @return   The size of the extent.
  */
-extent
+extent returns [String num = null]
    :
-      KW_EXTENT^ NUM_LITERAL
+      KW_EXTENT^ n:NUM_LITERAL { num = #n.getText(); }
    ;
    
 /**

=== modified file 'src/com/goldencode/p2j/uast/AstGenerator.java'
--- old/src/com/goldencode/p2j/uast/AstGenerator.java	2022-05-01 17:28:59 +0000
+++ new/src/com/goldencode/p2j/uast/AstGenerator.java	2022-05-19 13:32:07 +0000
@@ -178,6 +178,7 @@
 **     OM  20210913          Added flags capability to AST registry.
 **     OM  20211122          Added hints-based support for 'unloading' schema.
 **     OM  20220405          XmlFilePlugin can load resources from application's jar.
+**     GES 20220517          Added getAstName().
 */
 
 /*
@@ -941,6 +942,7 @@
       try
       {
          // exclude all other access to this file
+         // TODO: doesn't this locking need to be on filename.ast instead of just filename?
          mgr.lockTree(filename, list != null);
          
          // should we honor a previously persisted AST?
@@ -1028,6 +1030,19 @@
    }
    
    /**
+    * Given a 4GL source code filename return the name of the AST file.
+    *
+    * @param    filename
+    *           The original source filename.
+    *
+    * @return   The target AST filename.
+    */   
+   public static String getAstName(String filename)
+   {
+      return filename + AST_POSTFIX;
+   }
+   
+   /**
     * Builds a unique filename based on an original filename and a passed
     * postfix.  This method takes the original filename and appends a fixed
     * value to create a new, unique cache filename.

=== modified file 'src/com/goldencode/p2j/uast/ClassDefinition.java'
--- old/src/com/goldencode/p2j/uast/ClassDefinition.java	2022-05-16 16:24:45 +0000
+++ new/src/com/goldencode/p2j/uast/ClassDefinition.java	2022-05-23 20:10:15 +0000
@@ -117,6 +117,10 @@
 **     CA  20220428          Annotate THIS-OBJECT and SUPER constructor call parameters.
 **     CA  20220513          Track the Java field name for the builtin enums, so they can emit the exact name.
 **     CA  20220516          Fixed tracking of extent data members (variables or properties).
+**     GES 20210311          Rewrote exact and fuzzy method matching to support all data types and
+**                           overloading rules.  Fixed many latent problems.  Reworked how method names
+**                           convert to resolve conflicts within the class hierarchy instead of project
+**                           wide.
 */
 
 /*
@@ -177,13 +181,15 @@
 import java.lang.reflect.*;
 import java.util.*;
 import java.util.function.*;
+import java.util.stream.Collectors;
+
 import com.goldencode.ast.*;
 import com.goldencode.p2j.convert.*;
 import com.goldencode.p2j.convert.db.*;
 import com.goldencode.p2j.oo.lang.*;
 import com.goldencode.p2j.schema.*;
 import com.goldencode.p2j.util.*;
-import com.goldencode.p2j.util.Utils;
+import com.goldencode.p2j.util.Utils; // conflicts with ANTLR so it needs to be explicitly imported 
 
 import antlr.*;
 
@@ -206,11 +212,6 @@
 public class ClassDefinition
 implements ProgressParserTokenTypes
 {
-   /**
-    * Flag to indicate whether tracing is ON.
-    */
-   public static final boolean nowrapObjects = System.getProperty("nowrap.objects") != null;
-
    /** Data store identifier. */
    protected static enum DataStoreType { VAR, METHOD, TABLE, QRY, DATASET, DATASRC };
 
@@ -230,7 +231,17 @@
    private Map<String, Map<SignatureKey, MemberData>> imethods = null; 
    
    /** Static methods mapped by name. */
-   private Map<String, Map<SignatureKey, MemberData>> smethods = null; 
+   private Map<String, Map<SignatureKey, MemberData>> smethods = null;
+
+   /**
+    * A set of existing Java signatures to detect when there is a method overloading conflict (more than one
+    * method with the same java name and signature).  Some overloaded methods can convert to identical Java
+    * signatures when they are different in the 4GL.
+    */
+   private Set<String> javaMethodCollisions = new HashSet<>();
+
+   /** Lookup of the Java name (known to be unique) from the legacy signature (which also is be unique). */
+   private Map<String, String> legacyToJavaMethod = new HashMap<>();
    
    /** Internal lock object. */
    private Object lock = new Object();
@@ -244,8 +255,11 @@
    /** Super class definition. Only interfaces can have multiple parents. */
    private final ClassDefinition[] parents;
    
+   /** Implemented interfaces. */
+   private final ClassDefinition[] interfaces;
+   
    /** List of implemented interfaces. */
-   private final Set<String> interfaces;
+   private final Set<String> ifaces;
    
    /** Flag marking if this is a built-in Progress class. */
    private final boolean builtin;
@@ -277,17 +291,13 @@
    /** Flag indicating that this class was processed (fully parsed) after it was pre-scanned. */
    private boolean processed = false;
    
-   /**
-    * A map of legacy method signatures to Java names.  This is used for builtin 4GL classes
-    * which are implemented in FWD, so that any references or overridden methods will inherit this 
-    * name.
-    */
-   private Map<String, String> javaMethodNames = new HashMap<>();
+   /** Full Java package in dotted notation (no class name). */
+   private String javaPackage = null;
 
    /** The Java field names for skeleton enums. */
    private Map<String, String> javaEnumNames = new HashMap<>();
    
-   /** The fully qualified converted Java name for this legacy class. */
+   /** Simple Java class name, without any package prefix. */
    private String javaClassName = null;
    
    /**
@@ -317,7 +327,7 @@
     */
    public ClassDefinition(String            name,
                           ClassDefinition[] parents,
-                          Set<String>       interfaces,
+                          ClassDefinition[] interfaces,
                           boolean           builtin,
                           OOType            otype,
                           boolean           dotnet,
@@ -326,10 +336,19 @@
       this.name       = name;
       this.parents    = parents;
       this.interfaces = interfaces;
+      this.ifaces     = (interfaces != null) ? new HashSet<String>() : null;
       this.builtin    = builtin;
       this.otype      = otype;
       this.dotnet     = dotnet;
       this.filename   = filename;
+
+      if (interfaces != null)
+      {
+         for (ClassDefinition iface : interfaces)
+         {
+            ifaces.add(iface.getName());
+         }
+      }
       
       if (builtin && !dotnet)
       {
@@ -369,32 +388,30 @@
       // get from the root
       this.dotnet = Boolean.TRUE.equals(root.getAnnotation("dotnet-cls"));
       this.builtin = Boolean.TRUE.equals(root.getAnnotation("builtin-cls")) && !dotnet;
-      this.otype = root.downPath("INTERFACE_DEF") 
-                     ? OOType.INTERFACE
-                     : root.downPath("ENUM_DEF") 
-                         ? OOType.ENUM 
-                         : OOType.CLASS;
+      this.otype = root.downPath("INTERFACE_DEF") ? OOType.INTERFACE
+                                                  : root.downPath("ENUM_DEF") ? OOType.ENUM : OOType.CLASS;
       if (builtin && !dotnet)
       {
          // this must be before we walk the AST
          loadBuiltinDefinition();
       }
+      
       if (!builtin)
       {
          this.schemaDict = new SchemaDictionary(null);
          this.schemaDict.addScope(false);
          SchemaWorker.loadFromPersistence(schemaDict, filename);
          
-         String pkg = (String) root.getAnnotation("pkgname");
-         String cls = (String) root.getAnnotation("classname");
-         this.javaClassName = pkg + "." + cls;
+         this.javaPackage   = (String) root.getAnnotation("pkgname");
+         this.javaClassName = (String) root.getAnnotation("classname");
       }
       
-      Set<String> interfaces = new HashSet<>();
-      List<ClassDefinition> parents = new ArrayList<>();
+      Set<String>           ifaces     = new HashSet<>();
+      List<ClassDefinition> interfaces = new ArrayList<>();
+      List<ClassDefinition> parents    = new ArrayList<>();
       boolean hadRootClass = false;
 
-      Consumer<String> resolveParent = (pqname) ->
+      BiConsumer<List<ClassDefinition>, String> resolveParent = (list, pqname) ->
       {
          ClassDefinition clsdef = SymbolResolver.loadClassDefinition(sym, pqname);
          if (clsdef == null)
@@ -417,7 +434,8 @@
          {
             throw new RuntimeException("Could not load parent class: " + pqname);
          }
-         parents.add(clsdef);
+         
+         list.add(clsdef);
       };
       
       boolean isIface = this.otype == OOType.INTERFACE;
@@ -438,18 +456,19 @@
 
                if (isIface)
                {
-                  resolveParent.accept(pqname);
+                  resolveParent.accept(parents, pqname);
                }
                else
                {
-                  interfaces.add(pqname);
+                  resolveParent.accept(interfaces, pqname);
+                  ifaces.add(pqname);
                }
                break;
 
             case KW_INHERITS:
                ref = node.getImmediateChild(CLASS_NAME, null);
                pqname = (String) ref.getAnnotation("qualified");
-               resolveParent.accept(pqname);
+               resolveParent.accept(parents, pqname);
                hadRootClass = SymbolResolver.ROOT_OBJ_NAME.equalsIgnoreCase(pqname);
                break;
          }
@@ -460,54 +479,28 @@
             String storeName = (String) node.getAnnotation("oo-data-store");
             DataStoreType storeType = DataStoreType.valueOf(storeName);
             boolean isStatic = Boolean.TRUE.equals(node.getAnnotation("static"));
+            boolean isOverride = Boolean.TRUE.equals(node.getAnnotation("override"));
             
             if (storeType == DataStoreType.METHOD)
             {
-               String name = (String) node.getAnnotation("name");
-               String key = name.toLowerCase();
-               
-               // TODO: move this in common code with addMethod
-               if (imethods == null)
-               {
-                  imethods = new HashMap<>();
-                  smethods = new HashMap<>();
-               }
-
-               Map<String, Map<SignatureKey, MemberData>> store = isStatic ? smethods : imethods;
-               Map<SignatureKey, MemberData>              defs  = store.get(key);
-               if (defs == null)
-               {
-                  defs = new HashMap<>();
-                  store.put(key, defs);
-               }
-               
-               ParameterKey[] sig = SymbolResolver.calculateDefinitionSignature(node);
-               int accessMode = ((Long) node.getAnnotation("access-mode")).intValue();
-               int retType = ((Long) node.getAnnotation("return_type")).intValue();
-               MemberData mdat = createMethod(name,
-                                                sig, 
-                                                accessMode,
-                                                isStatic,
-                                                retType,
-                                                (String) node.getAnnotation("qualified"),
-                                                (String) node.getAnnotation("javaname"),
-                                                SymbolResolver.readExtent(node),
-                                                -1);
-               defs.put(new SignatureKey(sig), mdat);
+               String         name = (String) node.getAnnotation("name");
+               ParameterKey[] sig  = SymbolResolver.calculateDefinitionSignature(node);
+               
+               MemberData mdat = createMethod(null,
+                                              name,
+                                              sig, 
+                                              ((Long) node.getAnnotation("access-mode")).intValue(),
+                                              isStatic,
+                                              ((Long) node.getAnnotation("return_type")).intValue(),
+                                              (String) node.getAnnotation("qualified"),
+                                              (String) node.getAnnotation("javaname"),
+                                              SymbolResolver.readExtent(node),
+                                              -1,
+                                              node.getImmediateChild(KW_OVERRIDE, null) != null,
+                                              node.getImmediateChild(KW_ABSTRACT, null) != null);
                
                if (!builtin)
                {
-                  String legacySignature = SymbolResolver.calculateLegacySignature(name, sig);
-                  String jname = mdat.javaname;
-                  String oldJname = javaMethodNames.put(legacySignature, jname);
-                  if (oldJname != null && !oldJname.equals(jname))
-                  {
-                     // something is wrong... name must match!
-                     System.err.println("ERROR: method " + legacySignature + 
-                                        " accessible from class " + qname + 
-                                        " has two Java names: " + jname + " and " + oldJname);
-                  }
-                  
                   mdat.astId = node.getId();
                   mdat.tempIdx = ConversionData.getTempIdx(mdat.astId);
                }
@@ -629,14 +622,19 @@
                                            tokenType, 
                                            (String) node.getAnnotation("qualified"));
                         tokenType = VAR_CLASS;
+                        
+                        // TODO: for enums, do we need to lookup the javaname in javaEnumNames here or
+                        //       can we rely upon the AST node's "javaname" annotation?
                      }
+                     
                      var.setProperty(nodeType == DEFINE_PROPERTY);
                      var.setStatic(isStatic && !isEnum());
                      var.setAccess(accessMode);
                      var.setExtent(SymbolResolver.readExtent(node));
-
+                     var.setOverride(isOverride);
+                     
                      // we need to know the variable's state at this point
-                     SymbolResolver.trackMethodSignatures(nodeType, var, varName);
+                     SymbolResolver.trackPropertyOrEventMethodSignatures(nodeType, this, node, var, varName);
                      break;
                      
                    default:
@@ -686,10 +684,9 @@
          parents.add(parent);
       }
       
-      this.interfaces = interfaces.isEmpty() ? null : Collections.unmodifiableSet(interfaces);
-      this.parents = parents.isEmpty() 
-                        ? null 
-                        : (ClassDefinition[]) parents.toArray(new ClassDefinition[0]);
+      this.ifaces     = ifaces.isEmpty() ? null : Collections.unmodifiableSet(ifaces);
+      this.interfaces = interfaces.isEmpty() ? null : interfaces.toArray(new ClassDefinition[0]);
+      this.parents    = parents.isEmpty() ? null : parents.toArray(new ClassDefinition[0]);
    }
    
    /**
@@ -714,6 +711,268 @@
    }
    
    /**
+    * Lookup or calculate the Java method name for a property or event and store the data for method
+    * name disambiguation. 
+    * 
+    * @param    node
+    *           The AST for which the method is being registered.
+    * @param    prefix
+    *           The accessor prefix.
+    * @param    lname
+    *           The legacy property/event name.
+    * @param    override
+    *           {@code true} if the property or event is overridden.
+    * @param    abstr
+    *           {@code true} if the property or event is abstract.
+    * @param    sig
+    *           The parameter definition.
+    *           
+    * @return   The Java method name that is associated with the specific property accessor.
+    */
+   public String registerPropertyMethod(Aast           node,
+                                        String         prefix, 
+                                        String         lname, 
+                                        boolean        override,
+                                        boolean        abstr,
+                                        ParameterKey[] sig)
+   {
+      String  accessor  = prefix + "-" + lname;
+      String  javaSig   = null;
+      String  legacySig = SymbolResolver.calculateLegacySignature(accessor, sig);
+      String  jparms    = SymbolResolver.calculateJavaParameters(sig);
+      String  key       = prefix + "-javaname";
+      String  javaname  = null;
+      
+      if (node.isAnnotation(key))
+      {
+         // subsequent time in, read the stored name from the AST and update the in memory storage
+         javaname = (String) node.getAnnotation(key);
+         
+         addJavaMethodName(javaname, jparms, legacySig);
+      }
+      else if (override)
+      {
+         // first time in (for an override)
+         javaname = lookupOverrideName(legacySig, override, false);
+         addJavaMethodName(javaname, jparms, legacySig);
+
+         node.putAnnotation(key, javaname);
+      }
+      else
+      {
+         if (abstr)
+         {
+            // abstract properties/events that match an interface property/event should get the same name
+            javaname = lookupOverrideName(legacySig, false, abstr);
+            
+            if (javaname != null)
+            {
+               addJavaMethodName(javaname, jparms, legacySig);
+            }
+         }
+
+         if (javaname == null)
+         {
+            // first time in (for a non-override), calculate the name and store it in memory and in the AST
+            javaname = calculateUniqueJavaMethodName(accessor, jparms, legacySig);
+         }
+         
+         node.putAnnotation(key, javaname);
+      }
+
+      return javaname;
+   }
+   
+   /**
+    * Compute the Java name for a legacy method which when combined with the Java parameter list will
+    * be a unique method name.
+    * 
+    * @param    name
+    *           The legacy name.
+    * @param    jparms
+    *           The Java parameter list definition.
+    * @param    legacySig
+    *           The legacy signature that will be mapped to the Java name.
+    *           
+    * @return   The Java name.
+    */
+   private String calculateUniqueJavaMethodName(String name, String jparms, String legacySig)
+   {
+      String jbase   = nconvert.convert(name, MatchPhraseConstants.TYPE_METHOD);
+      String jname   = jbase;
+      String javaSig = null;
+      
+      // TODO: the suffix can be replaced with something more verbose, to indicate i.e. the
+      // collision reason
+      int suffix = 1;
+      
+      while (true)
+      {
+         javaSig = jname + "(" + jparms + ")"; 
+         
+         if (!hasCollision(javaSig))
+         {
+            addJavaMethodName(jname, jparms, legacySig);
+            break;
+         }
+         
+         jname = jbase + "_" + suffix;
+         suffix++;
+      }
+
+      return jname;
+   }
+   
+   /**
+    * Check the current class and all parent classes/interfaces for a Java signature collision.
+    *  
+    * @param    javaSig
+    *           The Java method signature for which to check.
+    *           
+    * @return   {@code true} if there is a collision (an existing registered method with the same Java
+    *           signature).
+    */
+   private boolean hasCollision(String javaSig)
+   {
+      // quick out, fail if there is a local collision
+      if (javaMethodCollisions.contains(javaSig))
+      {
+         return true;
+      }
+      
+      java.util.function.Function<ClassDefinition, Boolean> collide = (ClassDefinition cls) ->
+      {
+         return cls.javaMethodCollisions.contains(javaSig);
+      };
+
+      // no local collision, but now we need to check our parent hierarchy
+      return calcFromParentGraph(collide, (Boolean value) -> !value, Boolean.FALSE, true, true);
+   }
+
+   /**
+    * Update the internal maps to record a new Java name.
+    * 
+    * @param    javaname
+    *           The Java method name to add.
+    * @param    jparms
+    *           The Java parameter list definition.
+    * @param    legacySig
+    *           The legacy signature that will be mapped to the Java name.
+    */
+   private void addJavaMethodName(String javaname, String jparms, String legacySig)
+   {
+      String javaSig = javaname + "(" + jparms + ")";
+
+      if (!javaMethodCollisions.contains(javaSig))
+      {
+         javaMethodCollisions.add(javaSig);
+      }
+      else
+      {
+         // don't uncomment this except for debugging; built-in classes (and others?) will trigger this
+         // System.err.printf("ERROR: Java signature %s already exists in collisions list.\n", javaSig);
+      }
+      
+      String old = legacyToJavaMethod.put(legacySig, javaname);
+      
+      if (old != null && !old.equals(javaname))
+      {
+         // something is wrong... name must match!
+         System.err.println("ERROR: method " + legacySig + 
+                            " accessible from class " + getName() + 
+                            " has two Java names: " + javaname + " and " + old);
+      }
+   }
+
+   /**
+    * Register this method signature with a converted Java method name.  The member data structure will
+    * have its javaname member set as a result of this call.  4 cases are handled (in this order):
+    * <p>
+    * <ol>
+    *    <li> The caller has passed in a non-null javaname.  This will force the name.
+    *    <li> The method is a overridden method, lookup the method in the parent class and use that name.
+    *    <li> The method signature is already registered, use the registered name.
+    *    <li> Calculate a unique name and register it.
+    * </ol>
+    * 
+    * @param    javaname
+    *           The javaname to forcibly assign to this legacy signature, or <code>null</code> to
+    *           compute an unique one.
+    * @param    mdat
+    *           The method definition.  Must NOT be {@code null}.
+    */
+   private void registerMethod(String javaname, MemberData mdat)
+   {
+      String jparms    = SymbolResolver.calculateJavaParameters(mdat.signature);
+      String legacySig = SymbolResolver.calculateLegacySignature(mdat.name, mdat.signature);
+      
+      // case 1: the caller knows best, if javaname is passed then force it here
+      if (javaname != null)
+      {
+         mdat.javaname = javaname;
+         addJavaMethodName(mdat.javaname, jparms, legacySig);         
+      }
+      
+      // case 2: overrides use the name of the parent (inheritance hierarchy)
+      else if (mdat.override)
+      {
+         mdat.javaname = lookupOverrideName(legacySig, mdat.override, false);
+         addJavaMethodName(mdat.javaname, jparms, legacySig);
+      }
+      
+      // case 3: the method is already registered with a unique name
+      else if (legacyToJavaMethod.containsKey(legacySig))
+      {
+         mdat.javaname = legacyToJavaMethod.get(legacySig);
+      }
+      
+      else
+      {
+         if (mdat.abstr)
+         {
+            // case 4: abstract methods that match an interface method should get the same name
+            mdat.javaname = lookupOverrideName(legacySig, false, mdat.abstr);
+            
+            if (mdat.javaname != null)
+            {
+               addJavaMethodName(mdat.javaname, jparms, legacySig);
+            }
+         }
+
+         if (mdat.javaname == null)
+         {
+            // case 5: not registered, calculate a unique name, avoiding signature conflicts
+            mdat.javaname = calculateUniqueJavaMethodName(mdat.name, jparms, legacySig);
+         }
+      }
+   }
+   
+   /**
+    * Lookup the Java method name in the parent/interface hierarchy for the given legacy signature.  The
+    * first match is returned.
+    *  
+    * @param    legacySig
+    *           The legacy signature.  This should uniquely identify a single method.
+    * @param    override
+    *           {@code true} is the method name is an override.  This will force a search of parent
+    *           (inheritance hierarchy).
+    * @param    abstr
+    *           {@code true} is the method name is abstract. This will force a search of any implemented
+    *           interfaces.
+    *           
+    * @return   The first Java name found for this legacy signature or {@code null} if there is no match.
+    */
+   private String lookupOverrideName(final String legacySig, boolean override, boolean abstr)
+   {
+      java.util.function.Function<ClassDefinition, String> oride = (ClassDefinition cls) ->
+      {
+         return cls.legacyToJavaMethod.get(legacySig);
+      };
+      
+      return calcFromParentGraph(oride, (String value) -> value == null, null, override, abstr);
+   }
+   
+   /**
     * Load the FWD implementation for a builtin, non-.NET class.  This maps legacy signatures to
     * Java method names, so that they will be reused for method references or override cases.
     */
@@ -721,9 +980,9 @@
    {
       Class<? extends _BaseObject_> cls = null;
 
-      javaClassName = SymbolResolver.findConvertedLegacyClass(name);
+      String fullname = SymbolResolver.findConvertedLegacyClass(name);
       
-      if (javaClassName == null)
+      if (fullname == null)
       {
          String clsPkg = (name.indexOf(".") < 0 ? "" : name.substring(0, name.lastIndexOf(".")));
          String clsName = clsPkg.isEmpty() ? name : name.substring(clsPkg.length() + 1);
@@ -758,16 +1017,18 @@
             clsName = "LegacyError";
          }
       
-         clsPkg = "com.goldencode.p2j.oo." + clsPkg + (clsPkg.isEmpty() ? "" : ".");
-         javaClassName = clsPkg + clsName;
+         javaPackage   = "com.goldencode.p2j.oo." + clsPkg;
+         javaClassName = clsName;
+         
+         fullname = javaPackage + "." + javaClassName;
 
          try
          {
-            cls = (Class<? extends _BaseObject_>) Class.forName(javaClassName);
+            cls = (Class<? extends _BaseObject_>) Class.forName(fullname);
          }
          catch (ClassNotFoundException e)
          {
-            System.err.println("Builtin class " + clsPkg + clsName + " is not implemented in FWD!");
+            System.err.println("Builtin class " + fullname + " is not implemented in FWD!");
             return;
          }
       }
@@ -775,11 +1036,16 @@
       {
          try
          {
-            cls = (Class<? extends _BaseObject_>) Class.forName(javaClassName);
+            cls = (Class<? extends _BaseObject_>) Class.forName(fullname);
+            
+            int dotIdx = fullname.lastIndexOf('.');
+            
+            javaPackage   = dotIdx < 0 ? "" : fullname.substring(0, dotIdx);
+            javaClassName = dotIdx < 0 ? fullname : fullname.substring(dotIdx + 1);            
          }
          catch (ClassNotFoundException e)
          {
-            System.err.println("Builtin class " + javaClassName + " is not implemented in FWD!");
+            System.err.println("Builtin class " + fullname + " is not implemented in FWD!");
             return;
          }
       }
@@ -821,7 +1087,7 @@
                      // c'tors only from the current definition
                      continue;
                   }
-                  String qname = getJavaSimpleName();
+                  String qname = getSimpleJavaName();
                   String qpkg = getJavaPackage().substring("com.goldencode.p2j.oo.".length());
                   qname = qpkg + "." + qname;
                   qname = qname.replace('.', '_');
@@ -832,21 +1098,10 @@
                   }
                   break;
             }
-            String legacySignature = computeLegacySignature(mname, lsig);
-            String jname = mthd.getName();
-            if (mname.equalsIgnoreCase("build"))
-            {
-               System.out.printf("LEGACY SIGNATURE %s %s %s\n", mname, legacySignature, jname);
-            }
+            String legacySig = SymbolResolver.calculateLegacySignature(mname, lsig);
+            String javaname  = mthd.getName();
             
-            String oldJname = javaMethodNames.put(legacySignature, jname);
-            if (oldJname != null && !oldJname.equals(jname))
-            {
-               // something is wrong... name must match!
-               System.err.println("ERROR: method " + legacySignature + 
-                                  " accessible from class " + clz + 
-                                  " has two Java names: " + jname + " and " + oldJname);
-            }
+            addJavaMethodName(javaname, "", legacySig);
          }
       };
 
@@ -899,60 +1154,23 @@
    }
    
    /**
-    * Given a Java method with a {@link LegacySignature}, compute its 4GL signature.
+    * Get the converted java method name for a method - this ensures the same Java name is used 
+    * for methods which are overridden, across the entire converted legacy classes.
+    * <p>
+    * Legacy methods for which there are collisions with the converted Java signatures, the names
+    * are made unique, but still ensures that any overridden versions will reuse the same name.
     * 
-    * @param    mname
-    *           The legacy method name.
-    * @param    lsig
-    *           The legacy signature annotation.
+    * @param    legacy
+    *           The legacy name.
+    * @param    mAst
+    *           The method definition AST.
+    *           
+    * @return   The converted Java name to use for this legacy method.
     */
-   public static String computeLegacySignature(String mname, LegacySignature lsig)
+   public String getConvertedMethodName(String legacy, Aast mAst)
    {
-      String legacySignature = mname.toLowerCase() + "(";
-      for (LegacyParameter lparam : lsig.parameters())
-      {
-         String pmode = lparam.mode();
-         if ("INPUT".equals(pmode))
-         {
-            pmode = ProgressParser.lookupTokenName(KW_INPUT);
-         }
-         else if ("OUTPUT".equals(pmode))
-         {
-            pmode = ProgressParser.lookupTokenName(KW_OUTPUT);
-         }
-         else if ("INPUT-OUTPUT".equals(pmode))
-         {
-            pmode = ProgressParser.lookupTokenName(KW_IN_OUT);
-         }
-         
-         String ptype = lparam.type().toLowerCase();
-         if ("bdt".equals(ptype))
-         {
-            // OO legacy classes which have POLY parameters have BDT as their type, at the Java implementation.
-            ptype = "Object";
-         }
-         
-         String psig = pmode + " " + ptype;
-         if (!lparam.qualified().isEmpty())
-         {
-            psig = psig + "<? extends " + lparam.qualified().toLowerCase() + ">";
-         }
-         else if ("object".equalsIgnoreCase(lparam.type()))
-         {
-            psig = psig + "<? extends progress.lang.object>";
-         }
-         if (lparam.extent() == -1)
-         {
-            psig = psig + "[]";
-         }
-         else if (lparam.extent() != -2)
-         {
-            psig = psig + "[" + lparam.extent() + "]";
-         }
-         legacySignature = legacySignature + (legacySignature.endsWith("(") ? "" : ", ") + psig;
-      }
-      
-      return legacySignature + ")";
+      ParameterKey[] signature = SymbolResolver.calculateDefinitionSignature(mAst);
+      return getConvertedMethodName(SymbolResolver.calculateLegacySignature(legacy, signature));
    }
 
    /**
@@ -965,9 +1183,9 @@
     */
    public String getConvertedMethodName(String legacySig)
    {
-      if (javaMethodNames.get(legacySig) != null)
+      if (legacyToJavaMethod.get(legacySig) != null)
       {
-         return javaMethodNames.get(legacySig);
+         return legacyToJavaMethod.get(legacySig);
       }
       
       if (parents != null)
@@ -982,7 +1200,7 @@
             }
          }
       }
-      
+
       return null;
    }
 
@@ -1258,46 +1476,57 @@
    }
 
    /**
-    * Set the converted, fully qualified Java name, for this class.
-    * 
-    * @param    name
-    *           The qualified Java name.
-    */
-   public void setJavaClassName(String name)
-   {
-      this.javaClassName = name;
+    * Set the converted Java package and Java class name.
+    * 
+    * @param    pkg
+    *           Full Java package in dotted notation (no class name).
+    * @param    cls
+    *           Simple Java class name, without any package prefix.
+    */
+   public void setJavaClassName(String pkg, String cls)
+   {
+      this.javaPackage   = pkg;
+      this.javaClassName = cls;
+   }
+   
+   /**
+    * Get the converted fully qualified Java class name for this definition.
+    * 
+    * @return   The converted fully qualified Java class name or {@code null} if it has not yet been
+    *           computed.
+    */
+   public String getQualifiedJavaName()
+   {
+      String clsName = getSimpleJavaName();
+      
+      if (clsName == null)
+      {
+         return null;
+      }
+      
+      String pkg = getJavaPackage();
+      
+      return (pkg == null || pkg.isEmpty()) ? clsName : pkg + "." + clsName; 
    }
    
    /**
     * Get the converted simple Java class name for this definition.
     * 
-    * @return   See above.
+    * @return   The converted Java class name or {@code null} if it has not yet been computed.
     */
-   public String getJavaSimpleName()
+   public String getSimpleJavaName()
    {
-      if (javaClassName == null)
-      {
-         return null; // not yet computed
-      }
-      
-      int dotIdx = javaClassName.lastIndexOf('.');
-      return dotIdx < 0 ? javaClassName : javaClassName.substring(dotIdx + 1);
+      return javaClassName;
    }
    
    /**
     * Get the converted Java package for this definition.
     * 
-    * @return   See above.
+    * @return   The converted Java package name or {@code null} if it has not yet been computed.
     */
    public String getJavaPackage()
    {
-      if (javaClassName == null)
-      {
-         return null; // not yet computed
-      }
-
-      int dotIdx = javaClassName.lastIndexOf('.');
-      return dotIdx < 0 ? "" : javaClassName.substring(0, dotIdx);
+      return javaPackage;
    }
    
    /**
@@ -1328,7 +1557,7 @@
     */
    public Set<String> getInterfaces()
    {
-      return interfaces == null ? null : new HashSet<String>(interfaces);
+      return ifaces == null ? null : new HashSet<String>(ifaces);
    }
    
    /**
@@ -1415,7 +1644,7 @@
    }
 
    /**
-    * Add the named method to this class.
+    * Add the named method to this class. This is only called during parsing. 
     *
     * @param    name
     *           Method name.
@@ -1433,18 +1662,12 @@
     *           The AST for the method's return type.  This is used to copy OO annotations back to 
     *           the method node.
     */
-   public void addMethod(String  name,
-                         int     type,
-                         int     access,
-                         boolean isStatic,
-                         Aast    node,
-                         Aast    ret)
+   public void addMethod(String name, int type, int access, boolean isStatic, Aast node, Aast ret)
    {
       ParameterKey[] sig = SymbolResolver.calculateDefinitionSignature(node);
       
       // methods provide overloading so we can't use the simple member store mapping approach
       
-      String key = name.toLowerCase();
       boolean isPreScan = SymbolResolver.isPreScan();
       
       int extent = 0;
@@ -1452,59 +1675,50 @@
       
       synchronized (lock)
       {
-         if (imethods == null)
-         {
-            imethods = new HashMap<>();
-            smethods = new HashMap<>();
-         }
-         
-         Map<String, Map<SignatureKey, MemberData>> store = isStatic ? smethods : imethods;
-         Map<SignatureKey, MemberData>              defs  = store.get(key);
+         Map<SignatureKey, MemberData> defs = getMethodStore(name, isStatic);
       
-         if (defs == null)
-         {
-            defs = new HashMap<>();
-            store.put(key, defs);
-         }
-         
          Map<String, Object> retType = new HashMap<>();
+         
          if (ret != null)
          {
             Iterator<String> iter = ret.annotationKeys();
+            
             while (iter.hasNext())
             {
                String akey = iter.next();
                retType.put(akey, ret.getAnnotation(akey));
             }
          }
-         String qname = (String) retType.get("qualified");
-         MemberData mdat =
-            new MemberData(name, null, sig, type, access, isStatic, qname, this, tempIdx, retType);
-         mdat.ast = node;
-
-         extent = mdat.extent = SymbolResolver.readExtent(node);
          
+         String     qname    = (String) retType.get("qualified");
+         boolean    override = node.getImmediateChild(KW_OVERRIDE, null) != null;
+         boolean    abstr    = node.getImmediateChild(KW_ABSTRACT, null) != null;
+         boolean    register = !isPreScan || (builtin && !dotnet);
          MemberData prevMdat = defs.get(new SignatureKey(sig));
+         
+         extent = SymbolResolver.readExtent(node);
+         
+         MemberData mdat = createMethod(defs, name, sig, access, isStatic, type, qname, 
+                                        null, extent, tempIdx, override, abstr, retType, register);
+         
+         mdat.ast = node;
+         
          if (prevMdat != null)
          {
             if (isPreScan)
             {
                // this can happen if we are referencing this class via a USING from an USING defined
-               // in this file... in this case, inherit the tempidx, but show an warning.
-               MemberData dat = defs.get(new SignatureKey(sig));
+               // in this file... in this case, inherit the tempidx, but show a warning.
                
-               // GES_TODO: re-enable this
+               // TODO: these show up for constructors and many other cases; we can't fill up the logs with
+               //       useless warnings, so we will disable for now
                // System.out.printf("\nERROR: duplicate method definition!\n");
-               // System.out.printf("EXISTING %s\n", dat.toString());
+               // System.out.printf("EXISTING %s\n", prevMdat.toString());
                // System.out.printf("DUPLICATE %s\n at %s", mdat.toString(), node.dumpTree());
-               
-               // return;
-            }
-            // else
-            {
-               // inherit the tempIdx computed at the pre-scan phase.
-               mdat.tempIdx = prevMdat.tempIdx;
-            }
+            }
+            
+            // inherit the tempIdx computed at the pre-scan phase.
+            mdat.tempIdx = prevMdat.tempIdx;
          }
          else
          {
@@ -1521,35 +1735,23 @@
          }
          
          mTempIdx = mdat.tempIdx;
-         defs.put(new SignatureKey(sig), mdat);
-         
-         if (!isPreScan || (builtin && !dotnet))
-         {
-            String javaname = null;
-            if (builtin)
-            {
-               String legacySignature = SymbolResolver.calculateLegacySignature(name, sig);
-               javaname = javaMethodNames.get(legacySignature);
-            }
-            SymbolResolver.trackMethodSignature(javaname, name, sig);
-         }
       }
       
       // annotate the given node
       if (node != null)
       {
          node.putAnnotation("name", name);
-         node.putAnnotation("return_type", new Long(type));
-         node.putAnnotation("signature", renderSignature(name, sig)); 
-         node.putAnnotation("access-mode", new Long(access));
-         node.putAnnotation("static", new Boolean(isStatic));
+         node.putAnnotation("return_type", (long) type);
+         node.putAnnotation("signature", SymbolResolver.calculateLegacySignature(name, sig)); 
+         node.putAnnotation("access-mode", (long) access);
+         node.putAnnotation("static", isStatic);
          node.putAnnotation("oo-data-store", DataStoreType.METHOD.toString());
-         node.putAnnotation("tempidx", new Long(mTempIdx));
+         node.putAnnotation("tempidx", (long) mTempIdx);
          node.putAnnotation("tempidx-file", this.filename);
          
          if (extent != 0)
          {
-            node.putAnnotation("extent", new Long(extent));
+            node.putAnnotation("extent", (long) extent);
          }
          
          if (ret != null && ret.isAnnotation("qualified"))
@@ -1589,6 +1791,12 @@
                }         
             }
          }
+         
+         // annotate the parameters
+         SymbolResolver.processDefinitionSignature(node, (Aast ast, ParameterKey pk, int idx) -> 
+         {
+            ast.putAnnotation("jtype", pk.toJava());
+         });
       }
    }
       
@@ -1661,7 +1869,8 @@
       if (node != null)
       {
          // go ahead and annotate the chp_wrapper right now too
-         MemberData mdat = lookupMethodWorker(mname, signature, access, isStatic, internal, node);
+         MethodSearchResult found = lookupMethodWorker(mname, signature, access, isStatic, internal, node);
+         MemberData         mdat    = (found == null) ?  null : found.data;
          
          if (mdat != null)
          {
@@ -1673,14 +1882,14 @@
             }
             
             // the access mode won't necessarily be the same as that passed in
-            node.putAnnotation("access-mode", new Long(mdat.access));
-            node.putAnnotation("static", new Boolean(mdat.isStatic));
+            node.putAnnotation("access-mode", (long) mdat.access);
+            node.putAnnotation("static", mdat.isStatic);
             if (mdat.modes != null && !mdat.modes.isEmpty())
             {
                node.putAnnotation("param_modes", mdat.modes);
             }
             
-            node.putAnnotation("tempidx", new Long(mdat.tempIdx));
+            node.putAnnotation("tempidx", (long) mdat.tempIdx);
             node.putAnnotation("tempidx-file", mdat.container.filename);
             
             node.putAnnotation("found-in-cls", mdat.container.name);
@@ -1692,21 +1901,37 @@
             
             if (mdat.extent != 0)
             {
-               node.putAnnotation("extent", new Long(mdat.extent));
-            }
-            
-            if (mdat.container.builtin && !mdat.container.dotnet)
-            {
-               String legacySignature = SymbolResolver.calculateLegacySignature(mdat.name, mdat.signature);
-               String javaname = mdat.container.getConvertedMethodName(legacySignature);
-               if (javaname != null)
-               {
-                  node.putAnnotation("javaname", javaname);
-               }
-            }
-            
-            annotateCallSignature(node, signature, mdat.signature);
-            node.putAnnotation("signature", renderSignature(mdat.name, mdat.signature));
+               node.putAnnotation("extent", (long) mdat.extent);
+            }
+
+            // we cannot annotate the javaname at this point because we are currently in the non-pre-scan 
+            // parse of a file and:
+            // - a method call can reference a method definition that appears later in the file so the
+            //   parsing must rely upon the pre-scan method def instead of the regular parse method def
+            //   where the javaname is calculated
+            // - can be a reference to a constructor which we currently don't calculate java names
+            //
+            // if (mdat.javaname != null)
+            // {
+            //    node.putAnnotation("javaname", mdat.javaname);
+            // }
+            // else
+            // {
+            //    // constructors don't have a javaname set
+            //    if (!getSimpleName().equalsIgnoreCase(mname))
+            //    {
+            //       System.out.printf("WARNING: Missing javaname in %s\n\nfound %s\nfor location %s\n",
+            //                         getName(),
+            //                         mdat.toString(),
+            //                         node.dumpTree(true));
+            //    }
+            // }
+            
+            annotateCallSignature(node, signature, mdat.signature, found.getOverrides());
+            
+            String legacySig = SymbolResolver.calculateLegacySignature(mdat.name, mdat.signature);
+            node.putAnnotation("signature", legacySig);
+            
             if (isBuiltIn())
             {
                // for builtin method calls, save the typelist too
@@ -1751,19 +1976,9 @@
          }
          else
          {
-            // check if there are POLY arguments (BaseDataType signature); if so, mark this call
-            // with 'dynamic', so conversion rules can emit a dynamic call for it
-            boolean dynamic = false;
-            for (ParameterKey key : signature)
-            {
-               if ("BaseDataType".equals(key.type))
-               {
-                  dynamic = true;
-                  break;
-               }
-            }
-            
-            if (dynamic)
+            // dynamic cases will have a non-null result; this may be for table-handle parms, dataset-handle
+            // parms or POLY parms
+            if (found != null && found.isDynamic())
             {
                node.putAnnotation("dynamic", true);
                if (isStatic)
@@ -1792,8 +2007,11 @@
    }
       
    /**
-    * Add a data member (variable) or property to this class.
+    * Add a data member (variable, property, enum or event) to this class.
     *
+    * @param    type
+    *           The token type of the member being added. This will be DEFINE_VARIABLE, DEFINE_PROPERTY,
+    *           DEFINE_EVENT or DEFINE_ENUM.
     * @param    name
     *           Variable or property name.
     * @param    var
@@ -1811,7 +2029,8 @@
     * @param    provisional
     *           {@code true} if this definition has not been finalized.
     */
-   public void addVariable(String   name,
+   public void addVariable(int      type,
+                           String   name,
                            Variable var,
                            Aast     node,
                            int      access,
@@ -1831,9 +2050,25 @@
       
       mdat.setProvisional(provisional);
       
+      if (type == DEFINE_ENUM)
+      {
+         String ename = name.toLowerCase();
+         
+         if (javaEnumNames.containsKey(ename))
+         {
+            mdat.javaname = javaEnumNames.get(ename);
+         }
+      }
+      
+      if (mdat.javaname == null)
+      {
+         mdat.javaname = nconvert.convert(name, MatchPhraseConstants.TYPE_VARIABLE);
+      }
+      
       if (node != null)
       {
          node.putAnnotation("oo-data-store", DataStoreType.VAR.toString());
+         node.putAnnotation("javaname", mdat.javaname);
       }
    }
    
@@ -1863,6 +2098,50 @@
    }
    
    /**
+    * Obtain the Java name of the data member the name and access mode.
+    * <p>
+    * If the resource does not exist in this class definition the parent (if one exists) will be checked.
+    * This means a recursive call can occur here.
+    *
+    * @param    name
+    *           Variable or property name.
+    * @param    access
+    *           Access mode ({@code KW_PUBLIC}, {@code KW_PROTECTD} or {@code KW_PRIVATE}).
+    * @param    isStatic    
+    *           <code>true</code> if this is a static reference.
+    *
+    * @return   The Java name of the data member or {@code null} if no such variable exists. 
+    */
+   public String lookupVariableJavaName(String name, int access, boolean isStatic)
+   {
+      MemberData mdat = lookupHierarchy(DataStoreType.VAR, name, access, isStatic);
+      return (mdat == null) ? null : mdat.javaname;
+   }
+   
+   /**
+    * Obtain the extent of the data member the name and access mode.
+    * <p>
+    * If the resource does not exist in this class definition the parent (if one exists) will be checked.
+    * This means a recursive call can occur here.
+    *
+    * @param    name
+    *           Variable or property name.
+    * @param    access
+    *           Access mode ({@code KW_PUBLIC}, {@code KW_PROTECTD} or {@code KW_PRIVATE}).
+    * @param    isStatic    
+    *           <code>true</code> if this is a static reference.
+    *
+    * @return   The extent of the data member or 0 if the variable is not an extent var or if no such
+    *           variable exists. 
+    */
+   public long lookupVariableExtent(String name, int access, boolean isStatic)
+   {
+      MemberData mdat = lookupHierarchy(DataStoreType.VAR, name, access, isStatic);
+      return (mdat == null || mdat.var.getExtent() == 0) ? 0L : (long) mdat.var.getExtent();
+   }
+   
+   
+   /**
     * Obtain the fully qualified class name of the object instance 
     * represented by the data member (variable) or property given
     * the name and access mode.
@@ -1883,9 +2162,7 @@
     *           represented by this variable or data member, or 
     *           <code>null</code> if no such variable exists. 
     */
-   public String lookupVariableClassName(String  name,
-                                         int     access,
-                                         boolean isStatic)
+   public String lookupVariableClassName(String  name, int access, boolean isStatic)
    {
       MemberData mdat = lookupHierarchy(DataStoreType.VAR, name, access, isStatic);
       return (mdat == null) ? null : mdat.qname;
@@ -2362,44 +2639,26 @@
     * @param    func
     *           Delegated code to calculate the value.
     * @param    continu
-    *           A condition which if it returns <code>true</code>, then the next schema
-    *           dictionary should be tried.  On <code>false</code>, the current result is
-    *           returned.
+    *           A condition which if it returns <code>true</code>, then the next parent should be tried.
+    *           On <code>false</code>, the current result is returned.
     * @param    defVal
     *           Default value to return if no parents exist.
+    * @param    inherit
+    *           {@code true} to search the inheritance hierarchy for a match.
+    * @param    ifaces
+    *           {@code true} to search interfaces (if any exist) in addition to direct parents.
     *
     * @return   The calculated value or {@code null} if no value is calculated.
     */
    public <R> R calcFromParentGraph(java.util.function.Function<ClassDefinition, R> func,
                                     Predicate<R>                                    continu,
-                                    R                                               defVal)
+                                    R                                               defVal,
+                                    boolean                                         inherit,
+                                    boolean                                         ifaces)
    {
-      R result = defVal;
-      
-      if (parents != null)
-      {
-         for (ClassDefinition parent : parents)
-         {
-            result = func.apply(parent);
-            
-            if (!continu.test(result))
-            {
-               break;
-            }
-            else
-            {
-               // recurse
-               result = parent.calcFromParentGraph(func, continu, defVal);
-               
-               if (!continu.test(result))
-               {
-                  break;
-               }
-            }
-         }
-      }
-      
-      return result;
+      Set<String> processed = new HashSet<>();
+      
+      return calcFromParentGraphWorker(func, continu, defVal, processed, inherit, ifaces);
    }
    
    /**
@@ -2422,183 +2681,78 @@
    }
    
    /**
-    * Initialize the converted class details using this definition.
-    * <p>
-    * This will expose to the conversion rules details about the class member's, like converted
-    * Java name.
-    * 
-    * @param    cclass
-    *           The instance which needs to be initialized.
+    * Recursively process the parent hierarchy to calculate a value.  This searches the list of
+    * parents, checking UP before SIDEWAYS.
+    *
+    * @param    func
+    *           Delegated code to calculate the value.
+    * @param    continu
+    *           A condition which if it returns <code>true</code>, then the next parent should be tried.
+    *           On <code>false</code>, the current result is returned.
+    * @param    defVal
+    *           Default value to return if no parents exist.
+    * @param    inherit
+    *           {@code true} to search the inheritance hierarchy for a match.
+    * @param    ifaces
+    *           {@code true} to search interfaces (if any exist) in addition to direct parents.
+    *
+    * @return   The calculated value or {@code null} if no value is calculated.
     */
-   void initialize(ConvertedClassName cclass)
+   private <R> R calcFromParentGraphWorker(java.util.function.Function<ClassDefinition, R> func,
+                                          Predicate<R>                                    continu,
+                                          R                                               defVal,
+                                          Set<String>                                     processed,
+                                          boolean                                         inherit,
+                                          boolean                                         ifaces)
    {
-      Map<String, Map<SignatureKey, MemberData>>[] methods = new Map[] { imethods, smethods };
-
-      // methods. this includes the get-,set-,length-of, resize- methods for OO properties, but
-      // only for non-builtin classes
-      for (Map<String, Map<SignatureKey, MemberData>> mthds : methods)
-      {
-         if (mthds == null)
-         {
-            continue;
-         }
-         
-         for (String key : mthds.keySet())
-         {
-            Map<SignatureKey, MemberData> imthds = mthds.get(key);
-            for (SignatureKey mkey : imthds.keySet())
-            {
-               MemberData mdata = imthds.get(mkey);
-               
-               String legacySignature = SymbolResolver.calculateLegacySignature(mdata.name, 
-                                                                                mdata.signature);
-               String javaname = javaMethodNames.get(legacySignature);
-               
-               if (builtin || isEnum())
-               {
-                  if (javaname == null)
-                  {
-                     System.out.println("WARNING: Can't locate converted method for " + 
-                                        legacySignature  + " in " + javaClassName);
-
-                     javaname = nconvert.convert(mdata.name, MatchPhraseConstants.TYPE_METHOD);
-                  }
-
-                  cclass.addMethod(mdata.name, javaname, mdata.signature);
-               }
-               else
-               {
-                  cclass.addMethod(mdata.name, javaname, mdata.astId);
-               }
-            }
-         }
-      }
-      
-      Map<String, MemberData> ivar = getMemberStore(DataStoreType.VAR, false);
-      Map<String, MemberData> svar = getMemberStore(DataStoreType.VAR, true);
-      Map<String, MemberData>[] variables = new Map[] { ivar, svar };
-
-      // variables[extents] and properties
-      String[] extPrefixes = {"get", "set", "length-of", "resize" };
-      String[] propPrefixes = {"get", "set" };
-      for (Map<String, MemberData> vars : variables)
-      {
-         if (vars == null)
-         {
-            continue;
-         }
-         
-         for (String key : vars.keySet())
-         {
-            MemberData vdata = vars.get(key);
-            
-            if (builtin)
-            {
-               if (vdata.var.isProp())
-               {
-                  String datatype = SymbolResolver.translateType(vdata.type, null);
-                  if ("object".equals(datatype))
-                  {
-                     datatype = String.format("object <? extends %s>", vdata.qname);
-                  }
-                  int extent = vdata.var.getExtent();
-                  String ext = extent == 0 
-                                 ? "" 
-                                 : "[" + (extent > 0 ? Integer.toString(extent) : "") +"]";
-
-                  // add property methods
-                  String[] prefixes = extent != 0 ? extPrefixes : propPrefixes;
-                  for (String mprefix : prefixes)
-                  {
-                     ParameterKey[] signature = null;
-                     switch (mprefix)
-                     {
-                        case "get":
-                           signature = new ParameterKey[] {};
-                           break;
-                        case "set":
-                           signature = new ParameterKey[]
-                           {
-                              new ParameterKey(ProgressParserTokenTypes.KW_INPUT, datatype + ext)
-                           };
-                           break;
-                        case "length-of":
-                           signature = new ParameterKey[] {};
-                           break;
-                        case "resize":
-                           signature = new ParameterKey[]
-                           {
-                              new ParameterKey(ProgressParserTokenTypes.KW_INPUT, "int64")
-                           };
-                           break;
-                     }
-                     
-                     String legacyName = cclass.disambiguateLegacyPropertyName(mprefix, vdata.name);
-                     String legacySignature = 
-                        SymbolResolver.calculateLegacySignature(legacyName, signature);
-                     String javaname = javaMethodNames.get(legacySignature);
-                     if (javaname == null)
-                     {
-                        System.out.println("WARNING: Can't locate converted method for " + 
-                                           legacySignature  + " in " + javaClassName);
-                        
-                        javaname = nconvert.convert(legacyName, MatchPhraseConstants.TYPE_METHOD);
-                     }
-
-                     cclass.addMethod(legacyName, javaname, signature);
-                     
-                     // add the indexed getter and setter for the extent property
-                     if (vdata.var.getExtent() != 0 && ("get".equals(mprefix) || "set".equals(mprefix)))
-                     {
-                        signature = null;
-                        switch (mprefix)
-                        {
-                           case "get":
-                              signature = new ParameterKey[]
-                              {
-                                 new ParameterKey(ProgressParserTokenTypes.KW_INPUT, "int64"),
-                              };
-                              break;
-                           case "set":
-                              signature = new ParameterKey[]
-                              {
-                                 new ParameterKey(ProgressParserTokenTypes.KW_INPUT, datatype),
-                                 new ParameterKey(ProgressParserTokenTypes.KW_INPUT, "int64")
-                              };
-                              break;
-                        }
-                        
-                        legacyName = cclass.disambiguateLegacyPropertyName(mprefix, vdata.name);
-                        legacySignature = SymbolResolver.calculateLegacySignature(legacyName, signature);
-                        javaname = javaMethodNames.get(legacySignature);
-                        if (javaname == null)
-                        {
-                           System.out.println("WARNING: Can't locate converted method for " + 
-                                              legacySignature  + " in " + javaClassName);
-                           
-                           javaname = nconvert.convert(legacyName, MatchPhraseConstants.TYPE_METHOD);
-                        }
-
-                        cclass.addMethod(legacyName, javaname, signature);
-                     }
-                  }
-               }
-               else if (isEnum() && builtin)
-               {
-                  vdata.javaname = javaEnumNames.get(vdata.name.toLowerCase());
-               }
-               
-               if (vdata.javaname == null)
-               {
-                  vdata.javaname = nconvert.convert(vdata.name, MatchPhraseConstants.TYPE_VARIABLE);
-               }
-            }
-            
-            cclass.addVariable(vdata.name, 
-                               vdata.javaname, 
-                               vdata.var.getExtent() == 0 ? null : (long) vdata.var.getExtent());
-         }
-      }
+      R result = defVal;
+      
+      int start = 0;
+      int num   = ((inherit && parents != null) ? parents.length : 0) +
+                  ((ifaces && interfaces != null) ? interfaces.length : 0);
+      
+      ClassDefinition[] list = new ClassDefinition[num];
+      
+      if (inherit && parents != null)
+      {
+         System.arraycopy(parents, 0, list, start, parents.length);
+         start = parents.length;
+      }
+      
+      if (ifaces && interfaces != null)
+      {
+         System.arraycopy(interfaces, 0, list, start, interfaces.length);
+      }
+      
+      for (ClassDefinition parent : list)
+      {
+         // avoid duplicate processing
+         if (processed.contains(parent.getName()))
+         {
+            continue;
+         }
+         
+         processed.add(parent.getName());
+         
+         result = func.apply(parent);
+
+         if (!continu.test(result))
+         {
+            break;
+         }
+         else
+         {
+            // recurse
+            result = parent.calcFromParentGraphWorker(func, continu, defVal, processed, inherit, ifaces);
+            
+            if (!continu.test(result))
+            {
+               break;
+            }
+         }
+      }
+      
+      return result;
    }
    
    /**
@@ -2649,6 +2803,38 @@
    }
    
    /**
+    * Get the correct map of method signatures to member data instances.  This must only be called when the
+    * lock instance is owned. 
+    *
+    * @param    name
+    *           Method name.
+    * @param    isStatic
+    *           <code>true</code> if the method is static.
+    *           
+    * @return   The method map.
+    */   
+   private Map<SignatureKey, MemberData> getMethodStore(String name, boolean isStatic)
+   {
+      if (imethods == null)
+      {
+         imethods = new HashMap<>();
+         smethods = new HashMap<>();
+      }
+
+      Map<String, Map<SignatureKey, MemberData>> store = isStatic ? smethods : imethods;
+      String                                     key   = name.toLowerCase();
+      Map<SignatureKey, MemberData>              defs  = store.get(key);
+   
+      if (defs == null)
+      {
+         defs = new HashMap<>();
+         store.put(key, defs);
+      }
+
+      return defs;
+   }
+   
+   /**
     * Obtain the member given the name and access mode. If the member does not exist in
     * this class definition the parent (if one exists) will be checked.
     *
@@ -2718,39 +2904,6 @@
    }
    
    /**
-    * Generate a text descriptor of the method signature that includes the mode and type of
-    * each parameter.  Multiple parameters are comma separated and the entire string will have
-    * the method name and the parameters will be inside open and close parenthesis.
-    *
-    * @param    name
-    *           Method name.
-    * @param    sig
-    *           The method signature.
-    *
-    * @return   The signature descriptor.
-    */
-   private String renderSignature(String name, ParameterKey[] sig)
-   {
-      if (sig == null)
-         sig = new ParameterKey[0];
-      
-      StringBuilder sb = new StringBuilder();
-      sb.append(name.toLowerCase()).append("(");
-      
-      for (int i = 0; i < sig.length; i++)
-      {
-         if (i > 0)
-         {
-            sb.append(", ");
-         }
-         
-         sb.append(sig[i].toString());
-      }
-      
-      return sb.append(")").toString();
-   }
-   
-   /**
     * Process a method call to mark any "fuzzy" parameters with the wrapper type that must
     * be used for the actual call.
     * 
@@ -2758,8 +2911,15 @@
     *           The OO_METH_* node whose parameters will be annotated.
     * @param    callSig
     *           The array of parameter types that is the method definition signature.
+    * @param    defSig
+    *           The array of parameter types that is the method definition signature.
+    * @param    overrides
+    *           Type overrides found by any fuzzy matching for this method.
     */
-   private void annotateCallSignature(Aast call, ParameterKey[] callSig, ParameterKey[] defSig)
+   private void annotateCallSignature(Aast           call,
+                                      ParameterKey[] callSig,
+                                      ParameterKey[] defSig,
+                                      String[]       overrides)
    {
       int idx = 0;
       
@@ -2786,6 +2946,8 @@
              type != KW_BY_REF  &&
              type != KW_BY_VALUE)
          {
+            parm.putAnnotation("jtype", defSig[idx].toJava());
+            
             parm.putAnnotation("parmtype", defSig[idx].mode.longValue());
             if (parm.getType() == PARAMETER)
             {
@@ -2796,9 +2958,18 @@
             boolean outputMode = defSig[idx].mode != null && defSig[idx].mode.intValue() != KW_INPUT;
 
             String classname = defSig[idx].type;
-            String callType = callSig[idx] == null ? null : callSig[idx].type;
+            String callType = callSig[idx].type;
             String defType = defSig[idx].type;
             
+            int idx1 = defType.indexOf("[");
+            int idx2 = callType.indexOf("[");
+            
+            // remove the [] from the classname/callType
+            if (idx1 > 0)
+            {
+               classname = defType.substring(0, idx1);
+            }
+            
             if (outputMode)
             {
                if (!callSig[idx].isExtent())
@@ -2821,61 +2992,20 @@
                      }
                   }
                }
-
-               if ("integer".equals(classname) || 
-                   "int64".equals(classname)   || 
-                   "character".equals(classname))
-               {
-                  // for OUTPUT and INPUT-OUTPUT variables, we need to force the classname in
-                  // certain cases
-                  if (!defType.equals(callType))
-                  {
-                     parm.putAnnotation("classname", classname);
-                  }
-               }
             }
-            else if (!defType.equals(callType))
+            
+            if (!defType.equals(callType))
             {
                // force everything for now
                boolean force = true;
 
-               // unknown value or BDT (POLY cases) match anything
-               //if (callType == null                ||
-               //    "BaseDataType".equals(callType) ||
-               //    "unknown".equals(callType))
-               //{
-               //   force = true;
-               //}
-               //// narrowing matches are OK, widening are NOT OK 
-               //else if ("int64".equals(defType) && "integer".equals(callType))
-               //{
-               //   force = true;
-               //}
-               //else if ("decimal".equals(defType) &&
-               //         ("integer".equals(callType) || "int64".equals(callType)))
-               //{
-               //   force = true;
-               //}
-               //else if ("longchar".equals(defType) &&
-               //         "character".equals(callType))
-               //{
-               //   force = true;
-               //}
-               //else if ("datetime".equals(defType) && "date".equals(callType))
-               //{
-               //   force = true;
-               //}
-               //else if ("datetimetz".equals(defType) &&
-               //         ("date".equals(callType) || "datetime".equals(callType)))
-               //{
-               //   force = true;
-               //}
-               
-               if (classname.startsWith("BUFFER ")  || 
-                   classname.equals("TEMP-TABLE")   ||
-                   classname.equals("TABLE-HANDLE") ||
-                   classname.equals("DATASET")      ||
-                   classname.equals("DATASET-HANDLE"))
+               if (classname.startsWith("BUFFER ")       || 
+                   classname.startsWith("TEMP-TABLE")    ||
+                   classname.equals("TABLE-HANDLE")      ||
+                   classname.startsWith("DATASET ")      ||
+                   classname.equals("DATASET-HANDLE")    ||
+                   classname.startsWith("object<")       ||
+                   classname.startsWith("jobject<"))
                {
                   force = false;
                }
@@ -2883,14 +3013,13 @@
                {
                   // non-extents and extents are matched here
 
-                  int idx1 = defType.indexOf("[");
-                  int idx2 = callType.indexOf("[");
-                  
+                  // both caller and def are extent
                   if (idx1 != -1 && idx2 != -1)
                   {
                      String prefix1 = defType.substring(0, idx1);
                      String prefix2 = callType.substring(0, idx2);
                      
+                     // compare data types
                      if (prefix1.equals(prefix2))
                      {
                         // extent differences alone don't need wrapping
@@ -2907,6 +3036,7 @@
                                           call.dumpTree());
                      }
                   }
+                  // the caller is extent or the def is extent but not both
                   else if (idx1 != -1 || idx2 != -1)
                   {
                      // TODO: conversion needs to be enhanced to support wrapping array parms
@@ -2917,13 +3047,7 @@
                                        callSig[idx],
                                        call.dumpTree());
                   }
-                  else if (parm.getType() == UNKNOWN_VAL)
-                  {
-                     if (classname.startsWith("object"))
-                     {
-                        classname = "object";
-                     }
-                  }
+                  // neither caller nor def are extent
                   else if (parm.getType() == FUNC_CLASS    || 
                            parm.getType() == VAR_CLASS     ||
                            parm.getType() == FIELD_CLASS   ||
@@ -2935,51 +3059,41 @@
                      force = false;
                   }
                }
-               
-               if (force && 
-                   !(parm.getIndexPos() == 3                                     && 
+
+               // do not wrap the last parameter for Progress.Lang.Class:setParameter
+               if (!(parm.getIndexPos() == 3                                     && 
                      parm.getParent().getType() == OO_METH_LOGICAL               && 
                      parm.getParent().getText().equalsIgnoreCase("setParameter") &&
                      parm.getParent().getAnnotation("found-in-cls")
                                      .toString()
                                      .equalsIgnoreCase("Progress.Lang.ParameterList")))
                {
-                  if (classname.startsWith("object<"))
-                  {
-                     classname = "object";
-                     force = false;
-                  }
-                  else if (classname.startsWith("jobject<"))
-                  {
-                     force = false;
-                     classname = "jobject";
-                  }
+                  if (force)
+                  {
+                     if (callType.indexOf('[') < 0)
+                     {
+                        if (parm.getType() == UNKNOWN_VAL || 
+                            (parm.getType() == PARAMETER && parm.getFirstChild().getType() == UNKNOWN_VAL))
+                        {
+                           if (parm.getType() == PARAMETER)
+                           {
+                              parm.putAnnotation("wrap_parameter", true);
+                           }
+                        }
+                        else if (!classname.equals("BaseDataType") &&
+                                 !classname.equals("Object"))
+                        {
+                           parm.putAnnotation("wrap_parameter", true);
+                        }
+                     }
 
-                  // do not wrap the last parameter for Progress.Lang.Class:setParameter
-                  
-                  // parm.putAnnotation("wrap", new Boolean(true));
-                  // parm.putAnnotation("chp_wrapper", classname);
-                  if (force && callType.indexOf('[') < 0)
-                  {
-                     if (parm.getType() == UNKNOWN_VAL || 
-                         (parm.getType() == PARAMETER && 
-                          parm.getFirstChild().getType() == UNKNOWN_VAL))
-                     {
-                        if (parm.getType() == PARAMETER)
-                        {
-                           parm.putAnnotation("wrap_parameter", new Boolean(true));
-                        }
-                     }
-                     else if ((!nowrapObjects || !classname.startsWith("object<")) && 
-                              !classname.startsWith("jobject<") && 
-                              !classname.equals("BaseDataType") &&
-                              !classname.equals("Object"))
-                     {
-                        // TODO: CA: what's the reason for nowrapObjects?
-                        parm.putAnnotation("wrap_parameter", Boolean.TRUE);
-                     }
+                     if (idx2 > 0)
+                     {
+                        callType = callType.substring(0, idx2);
+                     }                     
                      
                      parm.putAnnotation("classname", classname);
+                     parm.putAnnotation("calltype", callType);
                   }
                }
             }
@@ -3062,7 +3176,6 @@
             {
                Iterator<MemberData> iter = defs.values().iterator();
                
-               outer:
                while (iter.hasNext())
                {
                   MemberData dat = checkAccessRights(iter.next(), access);
@@ -3131,47 +3244,56 @@
     * @return   Member data or <code>null</code> if no such variable or data
     *           member exists. 
     */
-   private MemberData lookupMethodWorker(String         name,
-                                         ParameterKey[] sig,
-                                         int            access,
-                                         boolean        isStatic,
-                                         boolean        internal,
-                                         Aast           node)
+   private MethodSearchResult lookupMethodWorker(String         name,
+                                                 ParameterKey[] sig,
+                                                 int            access,
+                                                 boolean        isStatic,
+                                                 boolean        internal,
+                                                 Aast           node)
    {
-      MemberData mdat = null;
+      MethodSearchResult result = null;
       
       synchronized (lock)
       {
-         mdat = exactMethodLookup(name, sig, access, isStatic, internal);
+         result = exactMethodLookup(name, sig, access, isStatic, internal);
 
-         if (mdat == null && !isStatic && internal)
+         if (result == null && !isStatic && internal)
          {
             // can't find an instance member, check for static
-            mdat = exactMethodLookup(name, sig, access, true, internal);
+            result = exactMethodLookup(name, sig, access, true, internal);
          }
          
-         if (mdat == null)
+         if (result == null)
          {
-            mdat = fuzzyMethodLookup(name, sig, access, isStatic, internal, node);
+            result = fuzzyMethodLookup(name, sig, access, isStatic, internal, node);
    
-            if (mdat == null && !isStatic && internal)
+            if (result == null && !isStatic && internal)
             {
                // can't find an instance member, check for static
-               mdat = fuzzyMethodLookup(name, sig, access, true, internal, node);
+               result = fuzzyMethodLookup(name, sig, access, true, internal, node);
             }
          }
       }
       
-      return mdat;
+      return result;
    }
    
    /**
-    * Find the named method based on an exact signature match. This method will search up the
-    * parent hierarchy (recursively) if no exact match is found in the current class.
+    * Find the named method based on an exact signature match. This method searches all methods in the
+    * parent hierarchy not just in the current class.  This search the hierarchy approach is needed so
+    * that some special exceptions can be honored:
+    * <p>
+    * <ul>
+    *    <li>If the caller does not specify a mode and there is an otherwise exact match, that method is
+    *        selected.  Buffers have no mode processing so this case is ignored for them.</li>
+    *    <li>A caller's table-handle or dataset-handle parameter which has an exact match is disallowed
+    *        if there are any alternatives with a table/dataset at that parameter AND that parameter type
+    *        is what determines that the signatures are different.</li>
+    * </ul>
     *
     * @param    name
     *           Resource name.
-    * @param    sig
+    * @param    caller
     *           Method call signature.
     * @param    access
     *           Access mode (<code>KW_PUBLIC</code>, <code>KW_PROTECTD</code> or
@@ -3184,62 +3306,220 @@
     *
     * @return   Resource found or <code>null</code> if no match exists.
     */
-   private MemberData exactMethodLookup(String         name,
-                                        ParameterKey[] sig,
-                                        int            access,
-                                        boolean        isStatic,
-                                        boolean        internal)
-   {
-      MemberData mdat = null;
-      
-      synchronized (lock)
-      {
-         Map<String, Map<SignatureKey, MemberData>> map = isStatic ? smethods : imethods;
-         
-         if (map != null)
-         {
-            Map<SignatureKey, MemberData> defs = map.get(name.toLowerCase());
-            
-            if (defs != null)
-            {
-               // exact signature match is based on the key lookup
-               mdat = checkAccessRights(defs.get(new SignatureKey(sig)), access);
-            }
-         }
-         
-         // ignore if the static/instance does not match
-         if (mdat != null && !((!isStatic && (!mdat.isStatic || internal)) ||
-                               (isStatic && mdat.isStatic)))
-         {
-            // clear our result
-            mdat = null;
-         }
-         
-         // only look up the parent hierarchy if we haven't got a match yet; static lookups only
-         // work for internal usage, non-static lookups always work
-         if (mdat == null && parents != null)
-         {
-            // change access mode since we aren't allowed to see private stuff in parent
-            int _access = (access == KW_PRIVATE) ? KW_PROTECTD : access;
-            
-            for (ClassDefinition parent : parents)
-            {
-               if (parent == null)
-               {
-                  String theSig = Arrays.toString(sig);
-                  throw new NullPointerException("Null parent found for " + name + " " + theSig);
-               }
-               
-               // recurse up inheritence hierarchy
-               mdat = parent.exactMethodLookup(name, sig, _access, isStatic, internal);
-               
-               if (mdat != null)
-                  break;
-            }
-         }
-      }
-      
-      return mdat;
+   private MethodSearchResult exactMethodLookup(final String         name,
+                                                final ParameterKey[] caller,
+                                                final int            access,
+                                                final boolean        isStatic,
+                                                final boolean        internal)
+   {
+      List<MatchMetrics> list = candidates(name, caller.length, access, isStatic, internal, false, null);
+      
+      // quick out if there are no possible matches
+      if (list.isEmpty())
+      {
+         return null;
+      }
+
+      final boolean[] ignore = new boolean[caller.length];
+      
+      Predicate<MatchMetrics> exactMatch = (mem) -> 
+      {
+         for (int i = 0; i < caller.length; i++)
+         {
+            if (!ignore[i] && !caller[i].equals(mem.data.signature[i]))
+            {
+               return false;
+            }
+         }
+          
+         return true;
+      };
+       
+      List<MatchMetrics> exact = list.stream().filter(exactMatch).collect(Collectors.toList());
+
+      Predicate<MatchMetrics> flexModeMatch = (mem) -> 
+      {
+         for (int i = 0; i < caller.length; i++)
+         {
+            if (!ignore[i])
+            {
+               if (caller[i].mode != null)
+               {
+                  if (!caller[i].equals(mem.data.signature[i]))
+                  {
+                     return false;
+                  }
+               }
+               else
+               {
+                  if (!caller[i].typeEquivalent(mem.data.signature[i]))
+                  {
+                     return false;
+                  }
+               }
+            }
+         }
+         
+         return true;
+      };
+      
+      MethodSearchResult result = null;
+      boolean       flex   = false;
+      
+      if (exact.isEmpty())
+      {
+         List<MatchMetrics> flexMode = list.stream().filter(flexModeMatch).collect(Collectors.toList());
+         
+         if (flexMode.size() == 1)
+         {
+            result = new MethodSearchResult(flexMode.get(0).data, null);
+            flex   = true;
+         }
+      }
+      else
+      {
+         if (exact.size() == 1)
+         {
+            result = new MethodSearchResult(exact.get(0).data, null);
+         }
+      }
+      
+      if (result != null)
+      {
+         // table-handle/dataset-handle matches are only valid when the only difference in signature is
+         // between a table/dataset and table-handle/dataset-handle
+
+         Iterator<MatchMetrics> iter = list.iterator();
+         
+         // calculate which parameters are both table-handles/dataset-handles and have conflicting
+         // signature (another signature that has a table/dataset in the same parameter position)
+         while (iter.hasNext())
+         {
+            MatchMetrics m = iter.next();
+            
+            for (int i = 0; i < caller.length; i++)
+            {
+               // quick iteration if we already know this parameter should be ignored 
+               if (ignore[i])
+                  continue;
+               
+               String type = m.data.signature[i].type;
+
+               if ("TABLE-HANDLE".equals(caller[i].type))
+               {
+                  if (type != null && type.startsWith("TEMP-TABLE"))
+                  {
+                     ignore[i] = true;
+                  }
+               }
+               else if ("DATASET-HANDLE".equals(caller[i].type))
+               {
+                  if (type != null && type.startsWith("DATA-SET "))
+                  {
+                     ignore[i] = true;
+                  }
+               }
+            }
+         }
+
+         // ignoring the conflicting parameters, can we still calculate find a unique match?
+         List<MatchMetrics> ignoreTH = list.stream()
+                                            .filter(flex ? flexModeMatch : exactMatch)
+                                            .collect(Collectors.toList());
+         if (ignoreTH.size() != 1)
+         {
+            result = null;
+         }
+      }
+      
+      return result;
+   }
+   
+   /**
+    * Remove any candidates from the list which do not match the caller's extent.  The following are
+    * possible matches:
+    * <p>
+    * <ul>
+    *    <li> Neither the caller nor the parameter definition have an extent specification.
+    *    <li> Both the caller and the parameter definition have an indeterminate extent.
+    *    <li> The caller has a fixed extent value and the parameter definition has the same fixed extent
+    *         value.
+    *    <li> The caller has a fixed extent value and the parameter definition has an indeterminate
+    *         extent.  This is a fuzzy match (all the above criteria are the same as exact matches). 
+    *
+    * @param    list
+    *           The list of candidates.
+    * @param    callerExtVal
+    *           Caller's extent value (-1 means indeterminate, 0 is scalar and any positive value is
+    *           the fixed extent).
+    * @param    idx
+    *           Parameter number being processed.
+    */
+   private void processExtentMatches(List<MatchMetrics> list, int callerExtVal, int idx)
+   {
+      Predicate<MatchMetrics> test = null;
+      Consumer<MatchMetrics>  mark = (m) -> m.extExact++;
+      
+      // no extent in caller
+      if (callerExtVal == 0)
+      {
+         // no extent in the parameter definition
+         test = (mem) -> mem.data.signature[idx].type.indexOf("[") == -1;
+         
+         // no marking needed here
+         mark = (m) -> {};
+      }
+      // indeterminate extent in caller
+      else if (callerExtVal == -1)
+      {
+         // indeterminate extent in the parameter definition
+         test = (mem) -> mem.data.signature[idx].type.indexOf("[") != -1 && 
+                         getExtent(mem.data.signature[idx].type) == -1;
+      }
+      // fixed extent in caller
+      else
+      {
+         test = (mem) -> 
+         {
+            int extIdx = mem.data.signature[idx].type.indexOf("[");
+            
+            if (extIdx != -1)
+            {
+               // the parameter definition is an extent
+               int parmExtVal = getExtent(mem.data.signature[idx].type);
+               
+               // fixed extents match exactly or the parm is indeterminate
+               return callerExtVal == parmExtVal || parmExtVal == -1;
+            }
+            else
+            {
+               // the parameter definition is NOT an extent
+               return false;
+            }
+         };
+
+         // this will only be used for where we already know extIdx != -1 above which means it is an
+         // extent at the parameter definition (it may be either fixed or indeterminate) 
+         mark = (mem) -> 
+         {
+            // the parameter definition is an extent
+            int parmExtVal = getExtent(mem.data.signature[idx].type);
+            
+            if (callerExtVal == parmExtVal)
+            {
+               // both are fixed extents match exactly
+               mem.extExact++;
+            }
+            else if (parmExtVal == -1) 
+            {
+               // or the parm is indeterminate
+               mem.extCvt++;
+            }
+         };
+      }
+      
+      list.removeIf(test.negate());
+      list.stream().forEach(mark);
    }
    
    /**
@@ -3296,6 +3576,87 @@
    }
    
    /**
+    * Check if there is only 1 possible match for the given predicate, if so then remove all candidates
+    * from the list except for that one possible match.
+    * 
+    * @param    list
+    *           The list to edit.
+    * @param    test
+    *           The predicate to test.
+    * @param    idx
+    *           The parameter index for error messages.
+    * @param    callerType
+    *           The caller type for error messages.
+    * @param    error
+    *           A location to store an error message for the caller.
+    */
+   private boolean allowIfOnlyOne(List<MatchMetrics>      list, 
+                                  Predicate<MatchMetrics> test,
+                                  int                      idx, 
+                                  String                   callerType,
+                                  String[]                 error)
+   {
+      boolean result = false;
+      
+      List<MatchMetrics> matches = list.stream().filter(test).collect(Collectors.toList());
+
+      int num = matches.size();
+
+      if (num == 1)
+      {
+         result = true;
+         matches.stream().forEach((m) -> m.cvt++);
+         list.removeIf(test.negate());
+      }
+      else if (num > 1)
+      {
+         error[0] = String.format("Ambiguous matches (%d) for parameter %d of type %s.",
+                               num,
+                               idx, 
+                               callerType);
+      }
+      else
+      {
+         error[0] = String.format("No possible matches for parameter %d of type %s.", idx, callerType);
+      }
+      
+      if (!result)
+      {
+         list.clear();
+      }
+      
+      return result;
+   }
+   
+   /**
+    * Remove any non-exact matches from the given list.
+    * 
+    * @param    list
+    *           The list to edit.
+    * @param    exactMatch
+    *           The test for an exact match.
+    *           
+    * @return   {@code true} if edits were made.
+    */
+   private boolean processExactMatches(List<MatchMetrics> list, Predicate<MatchMetrics> exactMatch)
+   {
+      // calculate the exact TYPE matches
+      List<MatchMetrics> exacts = list.stream().filter(exactMatch).collect(Collectors.toList());
+      
+      if (!exacts.isEmpty())
+      {
+         exacts.stream().forEach((m) -> m.exact++);
+         
+         // there is at least 1 exact match, remove everything else
+         list.removeIf(exactMatch.negate());
+         
+         return true;
+      }
+
+      return false;
+   }
+   
+   /**
     * Find the named method based on a fuzzy signature match. This method will search up the
     * parent hierarchy (recursively) if no fuzzy match is found in the current class.
     *
@@ -3316,381 +3677,466 @@
     *
     * @return   Resource found or <code>null</code> if no match exists.
     */
-   private MemberData fuzzyMethodLookup(String         name,
-                                        ParameterKey[] caller,
-                                        int            access,
-                                        boolean        isStatic,
-                                        boolean        internal,
-                                        Aast           node)
+   private MethodSearchResult fuzzyMethodLookup(String         name,
+                                                ParameterKey[] caller,
+                                                int            access,
+                                                boolean        isStatic,
+                                                boolean        internal,
+                                                Aast           node)
    {
-      MemberData mdat = null;
-      
-      boolean polyParams = false;
-      for (ParameterKey p : caller)
-      {
-         if ("BaseDataType".equals(p.type))
-         {
-            polyParams = true;
-            break;
-         }
-      }
-      
       boolean first = isSetParam(name);
       
-      List<MemberData> list = candidates(name, caller.length, access, isStatic, internal, first, null);
+      List<MatchMetrics> list = candidates(name, caller.length, access, isStatic, internal, first, null);
+      
+      boolean debug = false;
+      
+      if (debug)
+      {
+         System.out.println("\n\n--------------------fuzzyMethodLookup START--------------------\n");
+         
+         System.out.printf("\n   CALLER %s\n   PARAMETERS: %d\n", node.dumpTree(true), caller.length);
+         
+         for (int i = 0; i < caller.length; i++)
+         {
+            System.out.printf("      %s\n", caller[i]);
+         }
+         
+         for (MatchMetrics m : list)
+         {
+            System.out.printf("DEBUG: %s\n", m.data);
+         }
+      }
             
       // quick out if there are no possible matches
       if (list.isEmpty())
       {
+         if (debug)
+            System.out.println("--------------------fuzzyMethodLookup QUICK OUT--------------------");
+         
          return null;
       }
       
       // special case for ParameterList.setParameter()
       if (first && list.size() == 1)
       {
-         return list.get(0);
+         return new MethodSearchResult(list.get(0).data, null);
       }
-      
-      // the fuzzy lookup is done in phases
-      // 1. collect all matches, regardless of parameter modes
-      // 2. if only one match found, use that
-      // 3. if multiple matches found:
-      // - if one exact match by exact parameter type, use that
-      // - if multiple matches, get by parameter modes
-      //   > if caller hasn't specified a mode for an argument, use wildcard
-      //   > only one match should be found
-      //   > if multiple matches, show warning
-      
-      List<MemberData> matches = new ArrayList<>();
-      List<MemberData> exactMatches = new ArrayList<>();
-      
-      // TODOS:
-      // - constructors
-      // - handle dynamic invocation marking here
-      // - tables/table handles
-      // - datasets/dataset handles
-      // - object fuzziness
-      // - does primitive type fuzziness have less priority than primitive type exact matches?
+
+      String[] error = new String[1];
+      
+      boolean polyArgs = false;
+      
+      // TODO: constructors
+
+      // PHASE 1: cut down the list of all matches to include only those which are potentially valid
+      
+      if (debug)
+         System.out.println("\n\n---PHASE 1 START---\n");
 
       // process each parameter from left to right; the processing will vary by the parameter type; some
-      // types (e.g. objects) have multi-phase checks and other types are a simple comparison; at each
+      // types (e.g. objects) have multi-step checks and other types are a simple comparison; at each
       // step of the way the list of possible candidates will be reduced until there are only candidates
-      // left which match all criteria; at that point if there are more than one then we either have a 
-      // dynamic invocation scenario or there is some ambiguity (which should not happen if the code
-      // compiles in the 4GL)
+      // left which COULD match all criteria; at that point if there are more than one then we have an
+      // additional disambiguation step, a dynamic invocation scenario or there is some ambiguity (which
+      // should not happen if the code compiles in the 4GL)
       
-      // OUTLINE FOR NEXT CHANGES
-      /* 
-      caller:
+      // process the caller's parameters, left to right; for each caller parameter we examine the matching
+      // parameter in the list of candidates, removing any candidates in the list that cannot match
       for (int i = 0; i < caller.length; i++)
       {
+         final int idx = i;
+         
+         final Integer callerMode = caller[i].mode;
+         
          // if the caller is passing Java types, then we match them as if they were 4GL types so the get
          // converted here; if they are already 4GL types then no change will happen
-         String callerType = fromJava(caller[i].type);
-         
-         // wildcards: unknown value or BDT (POLY cases) match anything (no cases can be excluded)
-         if (callerType == null                     ||
-             callerType.equals("BaseDataType")      ||
-             callerType.equals("unknown"))
-         {
-            continue caller;
-         }
-         
-         // primitive types
-         
-         // primitive types widening/narrowing
-         
-         // tables/table handles
-         
-         // handles passed to table
-         
-         // datasets/dataset handles
-         
-         // handles passed to datasets
-         
-         // buffers
-         
-         // extents
-         
-         // object direct match
-         
-         // object fuzziness
-         
-         // parameter modes
-      }
-      */
-      
-      Iterator<MemberData> iter = list.iterator();
-      
-      outer:
-      while (iter.hasNext())
-      {
-         MemberData dat = iter.next();
-         
-         ParameterKey[] candidate = dat.signature;
-         
-         boolean exact = true;
-         
-         inner:
+         final String  callerType = fromJava(caller[i].type);
+         
+         // these are all specific to the current parameter
+         Predicate<MatchMetrics> exactMatch = (m) -> callerType.equals(fromJava(m.data.signature[idx].type)); 
+         Predicate<MatchMetrics> isTable    = (m) -> m.data.signature[idx].type.startsWith("TEMP-TABLE");
+         Predicate<MatchMetrics> isTH       = (m) -> "TABLE-HANDLE".equals(m.data.signature[idx].type);
+         Predicate<MatchMetrics> isDataset  = (m) -> m.data.signature[idx].type.startsWith("DATASET <");
+         Predicate<MatchMetrics> isDH       = (m) -> "DATASET-HANDLE".equals(m.data.signature[idx].type);
+         Predicate<MatchMetrics> modeMatch  = (m) -> callerMode.equals(m.data.signature[idx].mode);
+
+         // exclude any candidates which cannot ever match due to a caller MODE constraint; a null for the
+         // caller's mode is a wildcard that can match any mode in the parameter definition; this means that
+         // when the caller's mode is null, no matches can be excluded
+         if (callerMode != null)
+         {
+            list.removeIf(modeMatch.negate());
+            
+            if (debug)
+               System.out.printf("PARM %d: mode %d\n", idx, list.size());
+         }
+
+         // TYPE-specific processing
+         
+         boolean poly = (callerType == null) || callerType.equals("BaseDataType");
+         
+         // TYPE wildcards: unknown value or BDT (POLY cases) match any TYPE
+         if (poly || callerType.equals("unknown"))
+         {
+            list.stream().forEach((m) -> m.wildcard++);
+            
+            if (poly)
+               polyArgs = true;
+            
+            // no cases can be excluded based on type; the list remains unchanged
+            if (debug)
+               System.out.printf("PARM %d: type wildcard %d\n", idx, list.size());
+         }
+         else
+         {
+            // table parameters
+            if (callerType.startsWith("TEMP-TABLE"))
+            {
+               // check for any exact TYPE matches
+               if (!processExactMatches(list, exactMatch))
+               {
+                  // allow a match to a table-handle if there is only 1 table-handle option
+                  allowIfOnlyOne(list, isTH, idx, callerType, error);
+               }
+               
+               if (debug)
+                  System.out.printf("PARM %d: TT %d\n", idx, list.size());
+            }
+
+            // table handles
+            else if (callerType.equals("TABLE-HANDLE"))
+            {
+               // at this point we can't know if we have a match, we can just remove anything that is not
+               // a table-handle or table; we defer the matching to the next phase once all other removals
+               // are done               
+               list.removeIf(isTH.or(isTable).negate());
+               list.stream().forEach((m) -> m.cvt++);
+               
+               if (debug)
+                  System.out.printf("PARM %d: TH %d\n", idx, list.size());
+            }
+            
+            // dataset parameters
+            else if (callerType.startsWith("DATASET <"))
+            {
+               // check for any exact TYPE matches
+               if (!processExactMatches(list, exactMatch))
+               {
+                  // allow a match to a dataset-handle if there is only 1 dataset-handle option
+                  allowIfOnlyOne(list, isDH, idx, callerType, error);
+               }
+               
+               if (debug)
+                  System.out.printf("PARM %d: DS %d\n", idx, list.size());
+            }
+            
+            // dataset handles
+            else if (callerType.equals("DATASET-HANDLE"))
+            {
+               // at this point we can't know if we have a match, we can just remove anything that is not
+               // a dataset-handle or dataset; we defer the matching to the next phase once all other
+               // removals are done               
+               list.removeIf(isDH.or(isDataset).negate());
+               list.stream().forEach((m) -> m.cvt++);
+               
+               if (debug)
+                  System.out.printf("PARM %d: DH %d\n", idx, list.size());
+            }
+
+            // buffers
+            else if (callerType.startsWith("BUFFER"))
+            {
+               // only an exact match works (handled above); no fuzzy matching here
+               list.removeIf(exactMatch.negate());
+               list.stream().forEach((m) -> m.exact++);
+               
+               if (debug)
+                  System.out.printf("PARM %d: buf %d\n", idx, list.size());
+            }
+
+            // must be one of the BaseDataType wrapper classes
+            else
+            {
+               final int     extIdx    = callerType.indexOf("[");
+               final int     extVal    = (extIdx == -1) ? 0 : getExtent(callerType);
+               final String  basicType = (extIdx == -1) ? callerType : callerType.substring(0, extIdx);
+
+               // extents
+               processExtentMatches(list, extVal, idx);
+               
+               if (debug)
+                  System.out.printf("PARM %d: after extent processing %d\n", idx, list.size());
+
+               // primitive and OO types widening/narrowing
+               // WARNING: this code is not idempotent, we modify the MemberMetrics instances of any that
+               //          are matched; this is BAD BAD BAD, but the widening/narrowing calculation is very
+               //          expensive so it makes no sense to duplicate that work
+               Predicate<MatchMetrics> isTypeMatch = (mem) -> 
+               {
+                  String base = mem.data.signature[idx].scalarType();
+                  
+                  Runnable increment = () -> mem.ooLvl[idx]++;
+                  
+                  // exact match to the base portion of the type will match for any mode (and is the only
+                  // possible match for input-output mode); for the rest of the cases we check the 
+                  // parameter mode at the definition (the caller might be unspecified (wildcard) but the
+                  // definition must be specified
+                  if (basicType.equals(base))
+                  {
+                     // yes, this is not idempotent; get over it
+                     mem.exact++;
+                     
+                     return true;
+                  }
+                  else if (mem.data.signature[idx].mode == KW_INPUT)
+                  {
+                     boolean widen = isWideningTypeMatch(basicType, base, increment);
+                     
+                     if (widen)
+                     {
+                        // yes, this is not idempotent; get over it
+                        mem.cvt++;
+                     }
+                     
+                     return widen;
+                  }
+                  else if (mem.data.signature[idx].mode == KW_OUTPUT)
+                  {
+                     boolean narrow = isNarrowingTypeMatch(basicType, base, increment);
+                     
+                     if (narrow)
+                     {
+                        // yes, this is not idempotent; get over it
+                        mem.cvt++;
+                     }
+                     
+                     return narrow;
+                  }
+                  
+                  return false;
+               };
+   
+               // WARNING: because isTypeMatch is not idempotent we make sure to only process it once
+               list.removeIf(isTypeMatch.negate());
+               
+               if (debug)
+                  System.out.printf("PARM %d: BDT type match %d\n", idx, list.size());
+            }
+         }
+      }
+
+      if (debug)
+      {
+         System.out.println("\n\n---PHASE 1 END---\n\n");
+         
+         for (MatchMetrics m : list)
+         {
+            System.out.printf("DEBUG: %s\n", m);
+         }
+      }
+      
+      // PHASE 2: if there are > 1 candidates we need to disambiguate based on priorities
+      
+      // unknown value, wildcard modes cannot be used to disambiguate
+      
+      if (list.size() > 1)
+      {
+         MethodSearchResult dyn = new MethodSearchResult();
+         
+         // POLY arguments activates runtime/dynamic mode
+         if (polyArgs)
+         {
+            if (debug)
+               System.out.println("\n\n---------fuzzyMethodLookup DYNAMIC POLY!---------\n");
+            
+            return dyn; 
+         }
+
+         // TABLE-HANDLE/DATASET-HANDLE arguments deferred processing; we don't calculate this earlier
+         // because some possible matches that would have been seen then may have been dropped by
+         // processing of later parameters; this means that we must handle the possible compile time and
+         // runtime matches here
+         final int[] numTB = new int[caller.length];
+         final int[] numTH = new int[caller.length];
+         final int[] numDS = new int[caller.length];
+         final int[] numDH = new int[caller.length];
+
          for (int i = 0; i < caller.length; i++)
          {
-            String candidateType = candidate[i].type;
-            String callerType = caller[i].type;
-            candidateType = fromJava(candidateType);
-            callerType = fromJava(callerType);
-
-            if (candidateType.equals(callerType))
-            {
-               // exact match, try the next one
-               continue inner;
-            }
-            else
-            {
-               if ("TEMP-TABLE".equals(candidateType) || 
-                   "TABLE-HANDLE".equals(candidateType))
-               {
-                  // handle can be passed to TABLE-HANDLE or DATASET-HANDLE in 4GL
-                  if ("TEMP-TABLE".equals(callerType) || 
-                      "TABLE-HANDLE".equals(callerType) ||
-                      "handle".equals(callerType))
-                  {
-                     continue inner;
-                  }
-                  
-                  continue outer;
-               }
-               else if ("DATASET".equals(candidateType) || 
-                        "DATASET-HANDLE".equals(candidateType))
-               {
-                  // handle can be passed to TABLE-HANDLE or DATASET-HANDLE in 4GL,
-                  // FWD doesn't support this at this time, so ignore it here.
-                  if ("DATASET".equals(callerType) || 
-                      "DATASET-HANDLE".equals(callerType))
-                  {
-                     continue inner;
-                  }
-                  
-                  continue outer;
-               }
-               else if (candidateType.startsWith("BUFFER"))
-               {
-                  if (callerType.equals(candidateType))
-                  {
-                     continue inner;
-                  }
-                  
-                  continue outer;
-               }
-               
-               // unknown value or BDT (POLY cases) match anything
-               if (callerType == null                     ||
-                   callerType.equals("BaseDataType")      ||
-                   callerType.equals("unknown")           || 
-                   candidateType.equals("Object"))
-               {
-                  exact = false;
-                  continue inner;
-               }
-               
-               // exact match on type but fuzzy extent spec match
-               int idx1 = candidateType.indexOf("[");
-               int idx2 = callerType.indexOf("[");
-               
-               boolean extentMatch = isExtentMatch(candidateType, callerType);
-               
-               if (idx1 != -1 && idx2 != -1)
-               {
-                  int ex1 = getExtent(candidateType);
-                  int ex2 = getExtent(callerType);
-                  
-                  candidateType = candidateType.substring(0, idx1);
-                  callerType = callerType.substring(0, idx2);
-                  
-                  extentMatch = extentMatch || ex1 == ex2 || ex1 == -1 || ex2 == -1;
-                  
-                  if (candidateType.equals(callerType))
-                  {
-                     if (extentMatch)
-                     {
-                        // match
-                        continue inner;
-                     }
-                     else
-                     {
-                        // not a match
-                        continue outer;
-                     }
-                  }
-               }
-               
-               exact = false;
-
-               // check the parameter mode at the definition
-               if (candidate[i].mode == KW_INPUT)
-               {
-                  // widening matches are OK, narrowing are NOT OK
-                  if (isWideningTypeMatch(callerType, candidateType) && extentMatch)
-                  {
-                     // match
-                     continue inner;
-                  }
-                  else
-                  {
-                     // not a match
-                     continue outer;
-                  }
-               }
-               else if (candidate[i].mode == KW_OUTPUT)
-               {
-                  // narrowing matches are OK, widening are NOT OK
-                  if (isNarrowingTypeMatch(callerType, candidateType) && extentMatch)
-                  {
-                     // match
-                     continue inner;
-                  }
-                  else
-                  {
-                     // not a match
-                     continue outer;
-                  }
-               }
-               else if (candidate[i].mode == KW_IN_OUT)
-               {
-                  // exact match is required
-                  if (!candidateType.equals(callerType))
-                  {
-                     // not a match
-                     continue outer;
-                  }
-               }
-            }
-         }
-         
-         // if we are here, we found a signature match; ignore not the right type
-         if (dat != null && ((!isStatic && (!dat.isStatic || internal)) ||
-                             (isStatic && dat.isStatic)))
-         {
-            matches.add(dat);
-            
-            if (exact)
-            {
-               exactMatches.add(dat);
-            }
-         }
-         else
-         {
-            // clear our result
-            dat = null;
-         }
-      }
-
-      if (matches.size() == 1)
-      {
-         mdat = matches.get(0);
-         return mdat;
-      }
-      else if (exactMatches.size() == 1)
-      {
-         mdat = exactMatches.get(0);
-         return mdat;
-      }
-      
-      // multiple fuzzy matches, get by parameter mode
-      iter = matches.iterator();
-      
-      outer:
+            final int idx = i;
+            
+            String callerType = fromJava(caller[i].type);
+         
+            if ("TABLE-HANDLE".equals(callerType))
+            {
+               Predicate<MatchMetrics> isTable = (m) -> m.data.signature[idx].type.startsWith("TEMP-TABLE");
+               Predicate<MatchMetrics> isTH    = (m) -> "TABLE-HANDLE".equals(m.data.signature[idx].type);
+
+               Consumer<MatchMetrics> trackT = (m) ->
+               {
+                  if (isTable.test(m))
+                  {
+                     numTB[idx]++;
+                  }
+                  else if (isTH.test(m))
+                  {
+                     numTH[idx]++;
+                  }
+               };
+
+               list.forEach(trackT);
+               
+               if (numTH[idx] == 1 && numTB[idx] == 0)
+               {
+                  list.removeIf(isTH.negate());
+                  break;
+               }
+               else if (numTH[idx] == 0 && numTB[idx] == 1)
+               {
+                  list.removeIf(isTable.negate());
+                  break;
+               }
+               else if ((numTH[idx] + numTB[idx]) > 1)
+               {
+                  // dynamic mode
+                  if (debug)
+                     System.out.println("\n\n---------fuzzyMethodLookup DYNAMIC TABLE-HANDLE!---------\n");
+                  
+                  return dyn;
+               }
+               else
+               {
+                  // no matches
+                  list.clear();
+                  error[0] = String.format("No possible matches for parameter %d of type %s.",
+                                           idx,
+                                           callerType);
+                  break;
+               }
+            }
+            
+            if ("DATASET-HANDLE".equals(callerType))
+            {
+               Predicate<MatchMetrics> isDataset = (m) -> m.data.signature[idx].type.startsWith("DATASET <");
+               Predicate<MatchMetrics> isDH      = (m) -> "DATASET-HANDLE".equals(m.data.signature[idx].type);
+            
+               Consumer<MatchMetrics> trackD = (m) ->
+               {
+                  if (isDataset.test(m))
+                  {
+                     numDS[idx]++;
+                  }
+                  else if (isDH.test(m))
+                  {
+                     numDH[idx]++;
+                  }
+               };
+               
+               list.forEach(trackD);
+            
+               if (numDH[idx] == 1 && numDS[idx] == 0)
+               {
+                  list.removeIf(isDH.negate());
+                  break;
+               }
+               else if (numDH[idx] == 0 && numDS[idx] == 1)
+               {
+                  list.removeIf(isDataset.negate());
+                  break;
+               }
+               else if ((numDH[idx] + numDS[idx]) > 1)
+               {
+                  // dynamic mode
+                  if (debug)
+                     System.out.println("\n\n---------fuzzyMethodLookup DYNAMIC DATASET-HANDLE!---------\n");
+                  
+                  return dyn;
+               }
+               else
+               {
+                  // no matches
+                  list.clear();
+                  error[0] = String.format("No possible matches for parameter %d of type %s.",
+                           idx,
+                           callerType);
+                  break;
+               }
+            }            
+         }
+      }
+
+      // exact vs fuzzy weighting disambiguation if needed
+      if (list.size() > 1)
+      {
+         // use the natural ordering of the metrics class to find the best match
+         Collections.sort(list, Collections.reverseOrder());
+         
+         MatchMetrics best     = list.get(0);
+         MatchMetrics runnerUp = list.get(1);
+         
+         // safety check; if it fails we report the ambiguity below
+         if (best.compareTo(runnerUp) == 1)
+         {
+            // clear everything except the best match
+            list = new LinkedList<>();
+            list.add(best);
+         }
+      }
+      
+      if (list.size() == 1)
+      {
+         // "there can be only one"
+         if (debug)
+            System.out.println("\n\n----------------fuzzyMethodLookup HIGHLANDER!----------------\n");
+         
+         MatchMetrics match = list.get(0);
+         
+         return new MethodSearchResult(match.data, match.overrides);
+      }
+      else if (list.isEmpty())
+      {
+         if (debug)
+         {
+            // if we still have ambiguous matches, display any stored error
+            if (error[0] != null)
+            {
+               System.out.println(error[0]);
+            }
+
+            System.out.println("\n\n----------------fuzzyMethodLookup NO MATCH SUCKA!----------------\n");
+         }
+         
+         return null;
+      }
+
+      System.out.println("WARNING: more than one method def found using fuzzy lookup: " + name + 
+                         " from class " + getName());
+      
+      System.out.printf("\n   CALLER %s\n   PARAMETERS: %d\n", node.dumpTree(true), caller.length);
+      
+      for (int i = 0; i < caller.length; i++)
+      {
+         System.out.printf("      %s\n", caller[i]);
+      }
+ 
+      Iterator<MatchMetrics> iter = list.iterator();
+      int num = 0;
+      
       while (iter.hasNext())
       {
-         MemberData dat = iter.next();
-         ParameterKey[] candidate = dat.signature;
-         
-         inner:
-         for (int i = 0; i < candidate.length; i++)
-         {
-            Integer candidateMode = candidate[i].mode;
-            Integer callerMode = caller[i].mode;
-            
-            if (Objects.equals(candidateMode, callerMode) || callerMode == null)
-            {
-               // either the same or the caller hasn't specified the argument mode
-               // TODO: the mode is not mandatory at the definition... assume INPUT?
-               continue inner;
-            }
-            else
-            {
-               // mismatch
-               continue outer;
-            }
-         }
-         
-         // found a match
-         if (mdat != null)
-         {
-            // add a logging that more than one definition matches this fuzzy
-            // signature
-            System.out.println("WARNING: more than one method def found " + 
-                               "using fuzzy lookup: " + mdat.name + 
-                               " from class " + getName());
-            
-            boolean print = true;
-            
-            if (fuzzyErrors != null)
-            {
-               if (fuzzyErrors.contains(mdat.name))
-               {
-                  print = false;
-               }
-            }
-            else
-            {
-               fuzzyErrors = new HashSet();
-            }
-            
-            if (print)
-            {
-               fuzzyErrors.add(mdat.name);
-               
-               System.out.printf("\n- access %s, static %b\n" +
-                                 "   PARAMETERS: %d\n", 
-                                 ProgressParser.lookupTokenName(mdat.access),
-                                 mdat.isStatic,
-                                 mdat.signature.length);
-               for (int i = 0; i < mdat.signature.length; i++)
-               {
-                  System.out.printf("      %s\n", mdat.signature[i]);
-               }
-               
-               System.out.printf("\n- access %s, static %b\n" +
-                                 "   PARAMETERS: %d\n", 
-                                 ProgressParser.lookupTokenName(dat.access),
-                                 dat.isStatic,
-                                 dat.signature.length);
-               for (int i = 0; i < dat.signature.length; i++)
-               {
-                  System.out.printf("      %s\n", dat.signature[i]);
-               }
-               
-               System.out.printf("\nby caller %s\n   PARAMETERS: %d\n", 
-                                 node.dumpTree(true), 
-                                 caller.length);
-               for (int i = 0; i < caller.length; i++)
-               {
-                  System.out.printf("      %s\n", caller[i]);
-               }
-            }
-            
-            if (polyParams)
-            {
-               return null;
-            }
-            
-            break outer;
-         }
+         MatchMetrics dat = iter.next();
+         
+         System.out.printf("   MATCH %d: %s\n\n", num, dat.toString());
+         num++;
+      }
+      
+      if (debug)
+         System.out.println("\n\n--------------------fuzzyMethodLookup BITTER END---------------------\n");
 
-         mdat = dat;
-      }
+      // return the first one in the sorted list
+      MatchMetrics match = list.get(0);
       
-      return mdat;
+      return new MethodSearchResult(match.data, match.overrides);
    }
    
    /**
@@ -3730,16 +4176,16 @@
     *
     * @return   List of possible methods that match.  This may be an empty list if no match exists.
     */
-   private List<MemberData> candidates(String            name,
-                                       int               num,
-                                       int               access,
-                                       boolean           isStatic,
-                                       boolean           internal,
-                                       boolean           first,
-                                       Set<SignatureKey> exist)
+   private List<MatchMetrics> candidates(String            name,
+                                          int               num,
+                                          int               access,
+                                          boolean           isStatic,
+                                          boolean           internal,
+                                          boolean           first,
+                                          Set<SignatureKey> exist)
    {
-      Set<SignatureKey> existing = exist == null ? new HashSet<>() : exist;
-      List<MemberData>  results  = new ArrayList<>();
+      Set<SignatureKey>   existing = exist == null ? new HashSet<>() : exist;
+      List<MatchMetrics> results  = new LinkedList<>();
       
       synchronized (lock)
       {
@@ -3753,25 +4199,32 @@
             {
                if (first)
                {
-                  results.add(defs.values().iterator().next());
+                  results.add(new MatchMetrics(defs.values().iterator().next()));
                   
                   return results;
                }
                
                Iterator<MemberData> iter = defs.values().iterator();
                
-               outer:
                while (iter.hasNext())
                {
                   MemberData dat = checkAccessRights(iter.next(), access);
                   
                   if (dat != null && dat.signature.length == num)
                   {
-                     SignatureKey key = (num > 0) ? new SignatureKey(dat.signature) : null;
-                     
-                     if (num == 0 || !existing.contains(key))
-                     {
-                        results.add(dat);
+                     // special case: no parameters; this is Highlander (i.e. "there can be only one")
+                     // and we don't have to search the parents, this is the match
+                     if (num == 0)
+                     {
+                        results.add(new MatchMetrics(dat));
+                        return results;
+                     }
+                     
+                     SignatureKey key = new SignatureKey(dat.signature);
+                     
+                     if (!existing.contains(key))
+                     {
+                        results.add(new MatchMetrics(dat));
                         existing.add(key);
                      }
                   }
@@ -3788,7 +4241,7 @@
             
             for (ClassDefinition parent : parents)
             {
-               // recurse up inheritence hierarchy
+               // recurse up inheritance hierarchy
                results.addAll(parent.candidates(name, num, _access, isStatic, internal, first, existing));
             }
          }
@@ -3850,6 +4303,101 @@
       
       return type;
    }
+
+   /**
+    * Detect if the given class is an instance of an implemented interface of this class.
+    * 
+    * @param    cls
+    *           The class to check.
+    *           
+    * @return   {@code true} if the given class is an implemented interface in this class. 
+    */
+   private boolean isImplemented(ClassDefinition cls)
+   {
+      if (ifaces == null)
+      {
+         return false;
+      }
+      
+      return ifaces.contains(cls.getName()) || ifaces.contains(cls.getName().toLowerCase());
+   }
+   
+   /**
+    * Detect if the given class is an instance of a parent class or an implemented interface of a parent
+    * class.
+    * 
+    * @param    cls
+    *           The class to check.
+    * @param    ifaces
+    *           {@code true} if implemented interfaces should be checked.
+    * @param    incrementer
+    *           Code to be executed to increment the inheritance level.
+    *
+    * @return   {@code true} if the given class is a parent class or an implemented interface of a parent
+    *           class. 
+    */
+   private boolean isInheritedFrom(ClassDefinition cls, boolean ifaces, Runnable incrementer)
+   {
+      if (parents != null)
+      {
+         for (ClassDefinition parent : parents)
+         {
+            if (cls == parent || (ifaces && parent.isImplemented(cls)))
+            {
+               // track the current parent level
+               incrementer.run();
+
+               return true;
+            }
+            
+            if (parent.isInheritedFrom(cls, ifaces, incrementer))
+            {
+               // track our level's contribution to the parent level
+               incrementer.run();
+               
+               return true;
+            }
+         }
+      }
+      
+      return false;
+   }
+   
+   /**
+    * Parse the given object spec (in the form "object<? extends _CLS_>" or "jobject<? extends _CLS_>") and
+    * return the class definition associated with _CLS_.
+    * 
+    * @param    spec
+    *           The specification to parse.
+    *           
+    * @return   The class definition or {@code null} if the spec was malformed or if there is no such
+    *           definition.
+    */
+   private ClassDefinition parseObjectSpec(String spec)
+   {
+      String sig = "object<? extends ";
+      int    idx = spec.indexOf(sig);
+      
+      ClassDefinition cls = null;
+      
+      if (idx != -1)
+      {
+         String cname = spec.substring(idx + sig.length(), spec.length() - 1);
+         
+         cls = SymbolResolver.getClassDefinition(cname);
+         
+         if (cls == null)
+         {
+            System.err.printf("ERROR: No class found for %s (object spec %s)!\n", cname, spec);
+         }
+      }
+      else
+      {
+         System.err.printf("ERROR: Malformed object spec %s!\n", spec);
+      }
+      
+      return cls;
+   }
    
    /**
     * Check if the callee's type can be written back to a wider caller's type, when viewed from the
@@ -3867,30 +4415,47 @@
     * </pre>
     * <p>
     * Object types narrow by these rules:
-    *
+    * <ol>
+    *    <li> We check if the callee matches a child class of the caller type's specific class.  The
+    *         nearest child class is matched with highest priority.
+    * </ol>
     *
     * @param    caller
     *           The caller's passed instance's data type.
     * @param    callee
     *           The candidate callee method's parameter data type.
+    * @param    incrementer
+    *           Code to be executed to increment the inheritance level.
     *
     * @return   {@code true} if a narrowing operation is possible.
     */
-   private boolean isNarrowingTypeMatch(String caller, String callee)
+   private boolean isNarrowingTypeMatch(String caller, String callee, Runnable incrementer)
    {
-      if (("int64".startsWith(caller)      && "integer".startsWith(callee))               ||
-          ("longchar".startsWith(caller)   && "character".startsWith(callee))             ||
-          ("datetime".startsWith(caller)   && "date".startsWith(callee))                  ||
-          ("datetimetz".startsWith(caller) && ("date".startsWith(callee) || "datetime".startsWith(callee))))
+      if (("int64".equals(caller)      && "integer".equals(callee))                             ||
+          ("decimal".equals(caller)    && ("integer".equals(callee) || "int64".equals(callee))) ||
+          ("longchar".equals(caller)   && "character".equals(callee))                           ||
+          ("datetime".equals(caller)   && "date".equals(callee))                                ||
+          ("datetimetz".equals(caller) && ("date".equals(callee) || "datetime".equals(callee))))
       {
          return true;
       }
           
       if (caller.startsWith("object") && callee.startsWith("object"))
       {
-         return true;
+         // parse out the classes
+         ClassDefinition src = parseObjectSpec(caller);
+         ClassDefinition tgt = parseObjectSpec(callee);
+         
+         // check if the target parameter is a child class of the caller (this explicitly ignores
+         // implemented interfaces which is how the 4GL does it)
+         if (tgt.isInheritedFrom(src, false, incrementer))
+         {
+            return true;
+         }
+         
+         return false;
       }
-      
+                
       return false;
    }
    
@@ -3908,32 +4473,57 @@
     * datetime           datetime-tz
     * </pre>
     * <p>
-    * Object types narrow by these rules:
-    *
+    * Object types widen by these rules:
+    * <ol>
+    *    <li> We check if the callee matches any of the interfaces implemented by the caller type's specific class.
+    *    <li> We check if the callee matches any of the parent class hierarchy (and their implemented interfaces)
+    *         of the caller type's specific class.
+    *    <li> We check if the callee is Progress.Lang.Object.  This check has lowest priority.
+    * </ol>
     *
     * @param    caller
     *           The caller's passed instance's data type.
     * @param    callee
     *           The candidate callee method's parameter data type.
+    * @param    incrementer
+    *           Code to be executed to increment the inheritance level.
     *
     * @return   {@code true} if a widening operation is possible.
     */
-   private boolean isWideningTypeMatch(String caller, String callee)
+   private boolean isWideningTypeMatch(String caller, String callee, Runnable incrementer)
    {
-      if (("int64".startsWith(caller) && "decimal".startsWith(callee))                                   ||
-          ("integer".startsWith(caller) && ("int64".startsWith(callee) || "decimal".startsWith(callee))) ||
-          ("character".startsWith(caller) && "longchar".startsWith(callee))                              ||
-          ("datetime".startsWith(caller) && "datetimetz".startsWith(callee))                             ||
-          ("date".startsWith(caller) && ("datetime".startsWith(callee) || "datetimetz".startsWith(callee))))
+      if (("int64".equals(caller) && "decimal".equals(callee))                                   ||
+          ("integer".equals(caller) && ("int64".equals(callee) || "decimal".equals(callee)))     ||
+          ("character".equals(caller) && "longchar".equals(callee))                              ||
+          ("datetime".equals(caller) && "datetimetz".equals(callee))                             ||
+          ("date".equals(caller) && ("datetime".equals(callee) || "datetimetz".equals(callee))))
       {
          return true;
       }
+      
+      if (caller.startsWith("object") && callee.startsWith("object"))
+      {
+         // parse out the classes
+         ClassDefinition src = parseObjectSpec(caller);
+         ClassDefinition tgt = parseObjectSpec(callee);
+         
+         // check if the target is in the interface list of the caller type's specific class
+         if (src.isImplemented(tgt))
+         {
+            return true;
+         }
+         
+         // check if the target matches the caller type's parent class hierarchy (including implemented
+         // interfaces)
+         if (src.isInheritedFrom(tgt, true, incrementer))
+         {
+            return true;
+         }
+
+         // lowest priority/last ditch check
+         return "progress.lang.object".equals(tgt.getName());
+      }
                 
-      if (caller.startsWith("object") && callee.startsWith("object"))
-      {
-         return true;
-      }
-      
       return false;
    }
    
@@ -4155,15 +4745,15 @@
    }
    
    /**
-    * Helper method to register a virtual enum method.
+    * Helper method to register a public virtual method.
     *
     * @param    name
-    *           Fully qualified class name of this enum.
+    *           Fully qualified class name.
     * @param    ori
     *           Legacy method name.
     * @param    meth
     *           Converted method name.
-    * @param    statc
+    * @param    isStatic
     *           {@code true} if this is a static method, {@code false} for instance methods.
     * @param    rtype
     *           Method's return token type.
@@ -4175,325 +4765,186 @@
    private void registerVirtual(String         name,
                                 String         ori,
                                 String         meth,
-                                boolean        statc,
+                                boolean        isStatic,
                                 int            rtype,
                                 String         qname,
                                 ParameterKey[] parms)
    {
-      String key = ori.toLowerCase();
-      
-      if (imethods == null)
-      {
-         imethods = new HashMap<>();
-         smethods = new HashMap<>();
-      }
-      
-      Map<String, Map<SignatureKey, MemberData>> methods = statc ? smethods : imethods;
-      Map<SignatureKey, MemberData>              defs    = methods.get(key);
-      
-      if (defs == null)
-      {
-         defs = new HashMap<>();
-         methods.put(key, defs);
-      }
-      
-      MemberData mdat = createMethod(ori, parms, KW_PUBLIC, true, rtype, qname, meth, -1, nextTempIdx());
-      defs.put(new SignatureKey(parms), mdat);
-      
-      String legacySignature = SymbolResolver.calculateLegacySignature(mdat.name, mdat.signature);
-      javaMethodNames.put(legacySignature, meth);
-   }
-   
-   /**
-    * Create a new method definition and track it.
-    *  
-    * @param    name
-    *           The legacy method name.
-    * @param    sig
-    *           The method signature.
-    * @param    accessMode
-    *           The access mode.
-    * @param    isStatic
-    *           Flag indicating if the method is static or not.
-    * @param    retType
-    *           The return type.
-    * @param    qualified
-    *           The qualified legacy class name, for methods returning object.
-    * @param    javaname
-    *           The converted java name.
-    * @param    extent
-    *           The extent (for the return type).
-    * @param    tempIdx
-    *           The tempIdx to use.
-    *           
-    * @return   The created method.
-    */
-   private MemberData createMethod(String name,
-                                   ParameterKey[] sig, 
-                                   int accessMode, 
-                                   boolean isStatic,
-                                   int retType,
-                                   String qualified,
-                                   String javaname,
-                                   int extent,
-                                   int tempIdx)
-   {
-      MemberData mdat = new MemberData(name, 
-                                       null, 
-                                       sig, 
-                                       retType, 
-                                       accessMode,
-                                       isStatic, 
-                                       qualified, 
-                                       this, 
-                                       tempIdx,  
-                                       null); // TODO: this is not used by MemberData
-      mdat.javaname = javaname;
-      mdat.extent = extent;
-      SymbolResolver.trackMethodSignature(mdat.javaname, name, sig);
-      
-      return mdat;
-   }
-   
-   /**
-    * Stores a member's data.
-    */
-   private static class MemberData
-   {
-      /** Text name. */
-      private String name = null;
-      
-      /** Token type. */
-      private int type = -1;
-      
-      /** Extent value (defaults to 0, which means scalar) of the return type. */
-      private int extent = 0;
-      
-      /** Access mode. */
-      private int access = -1;
-      
-      /** Temporary ID. */
-      private int tempIdx = -1;
-      
-      /** Static or instance member. */
-      private boolean isStatic = false;
-      
-      /** Class type name for members that return an object instance. */
-      private String qname = null;
-      
-      /** Parameter signature for methods. */
-      private ParameterKey[] signature = null;
-      
-      /** Associated variable instance for data members. */
-      private Variable var = null; // used only during parsing/front phase
-      
-      /** The containing class where this was defined. */
-      private ClassDefinition container = null;
-      
-      /** The parameter modes. */
-      private String modes = null;
-      
-      /**
-       * Marker for OO vars which are gathered during pre-scan, and they might not be actual class
-       * members (as pre-scan processes DEFINE VAR statements at all levels, including methods).
-       */
-      private boolean provisional = false;
-      
-      /** The AST for this member definition. After parsing is finished, this is cleared. */
-      private Aast ast = null;
-      
-      /** The AST's ID for this member definition. */
-      private Long astId = null;
+      createMethod(null, ori, parms, KW_PUBLIC, true, rtype, qname, meth, -1, nextTempIdx(), false, false);
+   }
+   
+   /**
+    * Create a new method definition and track it.
+    *  
+    * @param    name
+    *           The legacy method name.
+    * @param    sig
+    *           The method signature.
+    * @param    accessMode
+    *           The access mode.
+    * @param    isStatic
+    *           Flag indicating if the method is static or not.
+    * @param    retType
+    *           The return type.
+    * @param    qualified
+    *           The qualified legacy class name, for methods returning object.
+    * @param    javaname
+    *           The converted java name.
+    * @param    extent
+    *           The extent (for the return type).
+    * @param    tempIdx
+    *           The tempIdx to use.
+    * @param    override
+    *           {@code true} if the method is an override of a parent class/interface method.
+    * @param    abstr
+    *           {@code true} if the method is abstract of a parent class/interface method.
+    *           
+    * @return   The created method.
+    */
+   private MemberData createMethod(Map<SignatureKey, MemberData> defs,
+                                   String                        name,
+                                   ParameterKey[]                sig, 
+                                   int                           accessMode, 
+                                   boolean                       isStatic,
+                                   int                           retType,
+                                   String                        qualified,
+                                   String                        javaname,
+                                   int                           extent,
+                                   int                           tempIdx,
+                                   boolean                       override,
+                                   boolean                       abstr)
+   {
+      return createMethod(defs, name, sig, accessMode, isStatic, retType, qualified,
+                          javaname, extent, tempIdx, override, abstr, null, false);
+   }
+   
+   /**
+    * Create a new method definition and track it.
+    *  
+    * @param    name
+    *           The legacy method name.
+    * @param    sig
+    *           The method signature.
+    * @param    accessMode
+    *           The access mode.
+    * @param    isStatic
+    *           Flag indicating if the method is static or not.
+    * @param    retType
+    *           The return type.
+    * @param    qualified
+    *           The qualified legacy class name, for methods returning object.
+    * @param    javaname
+    *           The converted java name.
+    * @param    extent
+    *           The extent (for the return type).
+    * @param    tempIdx
+    *           The tempIdx to use.
+    * @param    override
+    *           {@code true} if the method is an override of a parent class/interface method.
+    * @param    abstr
+    *           {@code true} if the method is abstract of a parent class/interface method.
+    * @param    register
+    *           {@code true} if the name should be registered in the maps.
+    *           
+    * @return   The created method.
+    */
+   private MemberData createMethod(Map<SignatureKey, MemberData> defs,
+                                   String                        name,
+                                   ParameterKey[]                sig, 
+                                   int                           accessMode, 
+                                   boolean                       isStatic,
+                                   int                           retType,
+                                   String                        qualified,
+                                   String                        javaname,
+                                   int                           extent,
+                                   int                           tempIdx,
+                                   boolean                       override,
+                                   boolean                       abstr,
+                                   Map<String, Object>           ret,
+                                   boolean                       register)
+   {
+      synchronized (lock)
+      {
+         if (defs == null)
+         {
+            defs = getMethodStore(name, isStatic);
+         }
+         
+         MemberData mdat = new MemberData(name, 
+                                          null, 
+                                          sig, 
+                                          retType, 
+                                          accessMode,
+                                          isStatic, 
+                                          qualified, 
+                                          this, 
+                                          tempIdx,  
+                                          ret);
+         mdat.extent   = extent;
+         mdat.override = override;
+         mdat.abstr = abstr;
+         
+         defs.put(new SignatureKey(sig), mdat);
+         
+         if (register)
+         {
+            registerMethod(javaname, mdat);
+         }
+         
+         return mdat;
+      }
+   }
+   
+   /**
+    * Returns the result from a method lookup.
+    */
+   private static class MethodSearchResult
+   {
+      /** The method that was found. */
+      final private MemberData data;
+      
+      /** Type overrides for fuzzy matches. */
+      final private String[] overrides;
 
-      /** All annotations set at the method's return type. */
-      // WARNING: this is not set by initialize, in case of METHOD
-      private Map<String, Object> retType;
-      
-      /** 
-       * The converted Java name for this member. Used only for previously parsed classes, or
-       * legacy builtin classes.
-       */
-      private String javaname = null;
-      
-      /**
-       * Create an instance, setting the data members.
-       *
-       * @param    name
-       *           The name of the member.
-       * @param    var
-       *           Associated variable instance for data members or <code>null</code>.
-       * @param    signature
-       *           Parameter signature for methods or <code>null</code>.
-       * @param    type
-       *           The token type associated with the member.
-       * @param    access
-       *           Access mode (<code>KW_PUBLIC</code>,
-       *           <code>KW_PROTECTD</code> or <code>KW_PRIVATE</code>).
-       * @param    isStatic
-       *           <code>true</code> if the variable/property is static.
-       * @param    qname
-       *           Fully qualified class name where <code>type</code> equals
-       *           <code>OO_METH_CLASS</code>. <code>null</code> if this method
-       *           does not return an object instance.
-       * @param    container
-       *           The container class definition.
-       * @param    tempIdx
-       *           Temporary ID.
-       */
-      public MemberData(String          name,
-                        Variable        var,
-                        ParameterKey[]  signature,
-                        int             type,
-                        int             access,
-                        boolean         isStatic,
-                        String          qname,
-                        ClassDefinition container,
-                        int             tempIdx,
-                        Map<String, Object> retType)
-      {
-         this.name      = name;
-         this.var       = var;
-         this.signature = signature;
-         this.type      = type;
-         this.access    = access;
-         this.isStatic  = isStatic;
-         this.qname     = qname;
-         this.retType   = retType;
-         this.container = container;
-         this.tempIdx   = tempIdx;
-         
-         if (signature != null)
-         {
-            this.modes = "";
-            for (ParameterKey pk : signature)
-            {
-               if (pk.mode == null)
-               {
-                  modes = modes + "B";
-                  continue;
-               }
-               
-               switch (pk.mode)
-               {
-                  case KW_INPUT:
-                     modes = modes + "I";
-                     break;
-                  case KW_OUTPUT:
-                     modes = modes + "O";
-                     break;
-                  case KW_IN_OUT:
-                     modes = modes + "U";
-                     break;
-                  case KW_BUFFER:
-                     modes = modes + "B";
-                     break;
-               }
-            }
-         }
-      }
-      
-      /**
-       * The parse for this entire file has finished, and the AST IDs have been computed.  
-       * This is the time the capture the AST IDs or add other annotations, as is executed just
-       * before the AST is persisted.
-       */
-      public void parseFinished()
-      {
-         BiConsumer<Aast, Aast> p = (ast, parent) ->
-         {
-            if (ast.isAnnotation("access-mode"))
-            {
-               parent.putAnnotation("access-mode", (Long) ast.getAnnotation("access-mode"));
-               ast.removeAnnotation("access-mode");
-            }
-            if (ast.isAnnotation("oo-data-store"))
-            {
-               parent.putAnnotation("oo-data-store", (String) ast.getAnnotation("oo-data-store"));
-               ast.removeAnnotation("oo-data-store");
-            }
-            if (ast.isAnnotation("static"))
-            {
-               parent.putAnnotation("static", (Boolean) ast.getAnnotation("static"));
-               ast.removeAnnotation("static");
-            }
-         };
-         if (ast != null)
-         {
-            int type = ast.getType();
-            if (type == KW_TEMP_TAB ||
-                (type == SYMBOL && ast.getParent().getType() == DEFINE_BUFFER))
-            {
-               Aast parent = ast.getParent();
-               p.accept(ast, parent);
-               
-               if (type == KW_TEMP_TAB || type == KW_WORK_TAB)
-               {
-                  ast = parent;
-               }
-               else
-               {
-                  ast = parent;
-                  ast = ast.getImmediateChild(KW_FOR, null);
-                  ast = (Aast) ast.getFirstChild();
-               }
-            }
-            if (type == SYMBOL && ast.upPath("DEFINE_QUERY/KW_QUERY"))
-            {
-               Aast parent = ast.getAncestor(2);
-               p.accept(ast, parent);
-               
-               parent.putAnnotation("name", ast.getText());
-               ast = parent;
-            }
-            
-            astId = ast.getId();
-            ast = null;
-         }
-      }
-      
-      /**
-       * Set the {@link #provisional} state of this member.
-       * 
-       * @param    provisional
-       *           The new state.
-       */
-      public void setProvisional(boolean provisional)
-      {
-         this.provisional = provisional;
-      }
-      
-      /**
-       * Render the state of the instance in a text form.
-       *
-       * @return   The rendered instance.
-       */
-      public String toString()
-      {
-         StringBuilder sb = new StringBuilder();
-         
-         String qual = (qname == null) ? "" : String.format(" - %s", qname);
-         
-         sb.append(String.format("%s %b member %s (%s%s), extent %d in class %s, tempIdx %d\n",
-                                 ProgressParser.lookupTokenName(access),
-                                 isStatic,
-                                 name,
-                                 ProgressParser.lookupTokenName(type),
-                                 qual,
-                                 extent,
-                                 container.getName(),
-                                 tempIdx));
-          
-         if (signature != null)
-         {
-            sb.append(String.format("   PARAMETERS: %d\n", signature.length));
-            
-            for (int i = 0; i < signature.length; i++)
-            {
-               sb.append(String.format("      %s\n", signature[i]));
-            }
-         }
-         
-         return sb.toString();
+      /**
+       * Create an dynamic invocation instance.
+       */
+      MethodSearchResult()
+      {
+         data      = null;
+         overrides = null;
+      }
+      
+      /**
+       * Create an instance using the given result.
+       * 
+       * @param    data
+       *           The result of the search.
+       */
+      MethodSearchResult(MemberData data, String[] overrides)
+      {
+         this.data      = data;
+         this.overrides = overrides;
+      }
+      
+      /**
+       * Reports if this is a dynamic invocation.
+       * 
+       * @return   {@code true} if this is a dynamic invocation.
+       */
+      boolean isDynamic()
+      {
+         return data == null;
+      }
+      
+      /**
+       * Obtain any types overrides which are needed because of fuzzy matching.
+       *  
+       * @return   The type overrides defined for this match or {@code null} if there are no overrides.
+       */
+      String[] getOverrides()
+      {
+         return overrides;
       }
    }
 }

=== removed file 'src/com/goldencode/p2j/uast/ConvertedClassName.java'
--- old/src/com/goldencode/p2j/uast/ConvertedClassName.java	2022-04-28 12:37:22 +0000
+++ new/src/com/goldencode/p2j/uast/ConvertedClassName.java	1970-01-01 00:00:00 +0000
@@ -1,553 +0,0 @@
-/*
-** Module   : ConvertedClassName.java
-** Abstract : container for the package and class name for a converted program
-**
-** Copyright (c) 2018-2022, Golden Code Development Corporation.
-**
-** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
-** 001 GES 20181206 First version implementing a bean-like approach.
-**      OM 20181220 Added overloading support.
-** 002 CA  20190508 Solved converted method names for override and overload cases.
-** 003 CA  20190604 Added missing javadocs.
-** 004 CA  20190927 Added support for direct Java access from 4GL code.
-** 005 CA  20200412 Added incremental conversion support.
-** 006 CA  20200429 Legacy DEFINE ENUM conversion support.
-** 007 CA  20220428 Added getLegacySimpleName.
-*/
-
-/*
-** 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.uast;
-
-import com.goldencode.ast.*;
-import com.goldencode.p2j.pattern.*;
-
-import java.lang.reflect.*;
-import java.util.*;
-
-/**
- * Encapsulates the data necessary to know a converted class name as a simple class, as a
- * package name or as a fully qualified class name.
- */
-public class ConvertedClassName
-{
-   /** Full package name in dotted form. */
-   private final String pkg;
-   
-   /** Simple class name with no package prefix. */
-   private final String cls;
-   
-   /** Map of legacy method names (lowercase) to converted Java names. */
-   private final Map<String, String> methods = new HashMap<>();
-   
-   /** Quick mechanism to check if a converted Java method name is already in use. */
-   private final Set<Method> jMethods = new HashSet<>();
-   
-   /** Map of legacy variable names (lowercase) to converted Java names. */
-   private final Map<String, String> variables = new HashMap<>();
-   
-   /** Quick mechanism to check if a converted Java variable name is already in use. */
-   private final Set<String> jvariables = new HashSet<>();
-   
-   /** Map of legacy variable extents (legacy name to extent). */
-   private final Map<String, Long> extents = new HashMap<>();
-
-   /** The class definition computed during parsing. */
-   private ClassDefinition clsDef;
-   
-   /**
-    * Create a new mapping for a Java class.
-    * 
-    * @param    javaCls
-    *           The qualified Java class name.
-    *           
-    * @throws   ClassNotFoundException
-    *           If the Java class can not be found. 
-    */
-   public ConvertedClassName(String javaCls)
-   throws ClassNotFoundException
-   {
-      Class<?> cls = Class.forName(javaCls);
-      this.pkg = cls.getPackage().getName();
-      this.cls = cls.getSimpleName();
-      
-      java.lang.reflect.Method[] mthds = cls.getMethods();
-      for (java.lang.reflect.Method mthd : mthds)
-      {
-         methods.put(mthd.getName(), mthd.getName());
-      }
-      
-      Field[] fields = cls.getFields();
-      for (Field f : fields)
-      {
-         variables.put(f.getName(), f.getName());
-      }
-   }
-   
-   /**
-    * Constructs a {@code ConvertedClassName} instance using Strings representing the package and
-    * class.
-    *
-    * @param    pkg
-    *           Full package in dotted notation.
-    * @param    cls
-    *           Simple class name, no package.
-    */
-   public ConvertedClassName(String pkg, String cls)
-   {
-      this.pkg = pkg;
-      this.cls = cls;
-   }
-   
-   /**
-    * Constructs a {@code ConvertedClassName} instance using Strings representing the package and
-    * class.
-    *
-    * @param    clsDef
-    *           The associated class definition.
-    * @param    pkg
-    *           Full package in dotted notation.
-    * @param    cls
-    *           Simple class name, no package.
-    */
-   public ConvertedClassName(ClassDefinition clsDef, String pkg, String cls)
-   {
-      this(pkg, cls);
-      
-      this.clsDef = clsDef;
-      
-      if (clsDef.isEnum())
-      {
-         clsDef.initialize(this);
-      }
-   }
-   
-   /**
-    * Get the simple legacy name for this class.
-    */
-   public String getLegacySimpleName()
-   {
-      return clsDef.getSimpleName();
-   }
-   
-   /**   
-    * Returns the simple class name of the target object.
-    *
-    * @return   The class name, no package included.
-    */
-   public String getClassName()
-   {
-      return cls;
-   }
-   
-   /**
-    * Returns the package name.
-    *
-    * @return   The package name in dotted notation.
-    */
-   public String getPackage()
-   {
-      return pkg;
-   }
-   
-   /**
-    * Returns the fully qualified class name including package in dotted notation.
-    *
-    * @return   The fully qualified name.
-    */
-   public String getQualifiedName()
-   {
-      return pkg + "." + cls;
-   }
-   
-   /**
-    * Add a method mapping.
-    * 
-    * @param   legacy
-    *          The legacy name.
-    * @param   name
-    *          The converted java name.
-    * @param   mAst
-    *          Method AST node. It is used only to read the list of parameters in order to allow
-    *          overloaded method keep the same name.
-    */
-   public void addMethod(String legacy, String name, Aast mAst)
-   {
-      methods.put(legacy.toLowerCase(), name);
-      jMethods.add(new Method(name, mAst));
-   }
-   
-   /**
-    * Add a method mapping.
-    * 
-    * @param   legacy
-    *          The legacy name.
-    * @param   name
-    *          The converted java name.
-    * @param   signature
-    *          The method signature.
-    */
-   public void addMethod(String legacy, String name, ParameterKey[] signature)
-   {
-      methods.put(legacy.toLowerCase(), name);
-      jMethods.add(new Method(name, signature));
-   }
-
-   /**
-    * Add a method mapping.
-    * 
-    * @param   legacy
-    *          The legacy name.
-    * @param   name
-    *          The converted java name.
-    * @param   mAstId
-    *          The AST ID for this method definition, from where the signature will be computed.
-    */
-   public void addMethod(String legacy, String name, Long mAstId)
-   {
-      methods.put(legacy.toLowerCase(), name);
-      
-      Aast mAst = AstSymbolResolver.getAst(null, mAstId, true);
-      jMethods.add(new Method(name, mAst));
-   }
-
-   /**
-    * Get the converted java method name for a method - this ensures the same Java name is used 
-    * for methods which are overridden, across the entire converted legacy classes.
-    * <p>
-    * Legacy methods for which there are collisions with the converted Java signatures, the names
-    * are made unique, but still ensuring that any overridden versions will reuse the same name.
-    * 
-    * @param    legacy
-    *           The legacy name.
-    * @param    mAst
-    *           The method definition AST.
-    *           
-    * @return   The converted Java name to use for this legacy method.
-    */
-   public String getConvertedMethodName(String legacy, Aast mAst)
-   {
-      ParameterKey[] signature = SymbolResolver.calculateDefinitionSignature(mAst);
-      
-      if (clsDef != null)
-      {
-         String legacySig = SymbolResolver.calculateLegacySignature(legacy, signature);
-         String javaname = clsDef.getConvertedMethodName(legacySig);
-         if (javaname != null)
-         {
-            return javaname;
-         }
-      }
-      return SymbolResolver.getConvertedMethodName(legacy, signature);
-   }
-   
-   /**
-    * Get the Java method name for a legacy signature.
-    * 
-    * @param    legacySig
-    *           The legacy signature.
-    * @param    javaSig
-    *           The converted Java signature.
-    *           
-    * @return   The converted Java name or <code>null</code> if none was computed yet.
-    */
-   public String getConvertedMethodName(String legacySig, String javaSig)
-   {
-      return SymbolResolver.getConvertedMethodName(legacySig, javaSig);
-   }
-   
-   /**
-    * Get the defined method mappings.
-    * 
-    * @return   The {@link #methods}.
-    */
-   public Map<String, String> getMethods()
-   {
-      return new HashMap<>(methods);
-   }
-   
-   /**
-    * Get the converted java name for the given legacy name.
-    * 
-    * @param    legacy
-    *           The legacy method name.
-    *           
-    * @return   Returns the converted java name or {@code null} if it doesn't exist.
-    */
-   public String getMethod(String legacy)
-   {
-      return methods.get(legacy.toLowerCase());
-   }
-   
-   /**
-    * Check if the proposed converted java name already exists for the methods namespace.
-    * 
-    * @param   jName
-    *          The proposed converted name.
-    * @param   mAst
-    *          Method AST node. It is used only to read the list of parameters in order to allow
-    *          overloaded method keep th same name.
-    *           
-    * @return   {@code true} if the name is already in use.
-    */
-   public boolean isMethodNameConflict(String jName, Aast mAst)
-   {
-      return jMethods.contains(new Method(jName, mAst));
-   }
-   
-   /**
-    * Checks if the given name already exists as a legacy method, if so it calculates a unique
-    * name.  The original name and the resulting name (even if the same) are stored in a map
-    * so that the disambiguation can be remembered.
-    *
-    * @param    prefix
-    *           The getter, setter or extent prefix to use for the property method name.
-    * @param    pname
-    *           The legacy method name for the given property.
-    *
-    * @return   The safe legacy method name to use.
-    */
-   public String disambiguateLegacyPropertyName(String prefix, String pname)
-   {
-      // TODO: rename this to something else
-      // the "%s-%s" is already determined as unique after using the overload suffix for the 
-      // converted Java method name... no need to check for collisions here
-      // DO NOT CHANGE THE CASE for the legacy name!
-      return String.format("%s-%s", prefix, pname);
-   }
-   
-   /**
-    * Get the converted java name for the given legacy property and prefix.
-    * 
-    * @param    prefix
-    *           The getter, setter or extent prefix to use for the property method name.
-    * @param    pname
-    *           The legacy method name for the given property.
-    *           
-    * @return   Returns the converted java name or {@code null} if it doesn't exist.
-    */
-   public String getMethodForProperty(String prefix, String pname)
-   {
-      String legacyMethod = disambiguateLegacyPropertyName(prefix, pname);
-      return methods.get(legacyMethod.toLowerCase());
-   }
-
-   /**
-    * Add a variable mapping.
-    * 
-    * @param    legacy
-    *           The legacy name.
-    * @param    name
-    *           The converted java name.
-    */
-   public void addVariable(String legacy, String name)
-   {
-      variables.put(legacy.toLowerCase(), name);
-      jvariables.add(name);
-   }
-   
-   /**
-    * Add a variable mapping.
-    * 
-    * @param    legacy
-    *           The legacy name.
-    * @param    name
-    *           The converted java name.
-    * @param    extent
-    *           The variable's extent.
-    */
-   public void addVariable(String legacy, String name, Long extent)
-   {
-      variables.put(legacy.toLowerCase(), name);
-      jvariables.add(name);
-      if (extent != null)
-      {
-         extents.put(legacy.toLowerCase(), extent);
-      }
-   }
-   
-   /**
-    * Get the defined variable mappings.
-    * 
-    * @return   The {@link #variables}.
-    */
-   public Map<String, String> getVariables()
-   {
-      return new HashMap<>(variables);
-   }
-   
-   /**
-    * Get the converted java name for the given legacy name.
-    * 
-    * @param    legacy
-    *           The legacy variable name.
-    *           
-    * @return   Returns the converted java name or {@code null} if it doesn't exist.
-    */
-   public String getVariable(String legacy)
-   {
-      return variables.get(legacy.toLowerCase());
-   }
-   
-   /**
-    * Check if the proposed converted java name already exists for the variables namespace.
-    * 
-    * @param    jname
-    *           The proposed converted name.
-    *           
-    * @return   {@code true} if the name is already in use.
-    */
-   public boolean isVariableNameConflict(String jname)
-   {
-      return jvariables.contains(jname);
-   }
-   
-   /**
-    * Get the extent value for the given variable.
-    * 
-    * @param    legacy
-    *           The legacy variable name.
-    *           
-    * @return   The extent variable, or {@code null} if this is a scalar var.
-    */
-   public Long getVariableExtent(String legacy)
-   {
-      return extents.get(legacy.toLowerCase());
-   }
-   
-   /** 
-    * Abstracts a method name in a Java class. It takes into account the parameter types so it
-    * will differentiate between two overloaded method (with same name but different parameter
-    * types).
-    */
-   private static class Method
-   {
-      /** The name of the method. */
-      private final String javaName;
-      
-      /** The list of parameters. May be {@code null} when no parameters are present. */
-      private final ParameterKey[] signature;
-      
-      /**
-       * The constructor for this immutable class.
-       * 
-       * @param   javaName
-       *          The Java name of the method.
-       * @param   mAst
-       *          The {@code Aast} of the method. It is used to extract the signature of
-       *          the method.
-       */
-      Method(String javaName, Aast mAst)
-      {
-         this.signature = SymbolResolver.calculateDefinitionSignature(mAst);
-         this.javaName = javaName;
-      }
-      
-      /**
-       * The constructor for this immutable class.
-       * 
-       * @param   javaName
-       *          The Java name of the method.
-       * @param   signature
-       *          The signature for this method definition.
-       */
-      Method(String javaName, ParameterKey[] signature)
-      {
-         this.signature = signature;
-         this.javaName = javaName;
-      }
-
-      /**
-       * Test {@code other} object for equality.
-       * 
-       * @param   other
-       *          The object to compare to.
-       *
-       * @return  {@code true} only if the {@code other} object is equal to this one. 
-       */
-      @Override
-      public boolean equals(Object other)
-      {
-         if (this == other)
-         {
-            return true;
-         }
-         if (other == null || getClass() != other.getClass())
-         {
-            return false;
-         }
-         
-         Method method = (Method) other;
-         
-         if (!javaName.equals(method.javaName))
-         {
-            return false;
-         }
-         return Arrays.equals(signature, method.signature);
-      }
-      
-      /**
-       * Computes the hash code for this object.
-       * 
-       * @return  The computed hash-code.
-       */
-      @Override
-      public int hashCode()
-      {
-         int result = javaName.hashCode();
-         result = 31 * result + Arrays.hashCode(signature);
-         return result;
-      }
-   }
-}

=== modified file 'src/com/goldencode/p2j/uast/JavaClassDefinition.java'
--- old/src/com/goldencode/p2j/uast/JavaClassDefinition.java	2021-07-08 15:49:35 +0000
+++ new/src/com/goldencode/p2j/uast/JavaClassDefinition.java	2022-05-12 15:03:49 +0000
@@ -2,7 +2,7 @@
 ** Module   : JavaClassDefinition.java
 ** Abstract : defines the 4GL API for a Java class or interface definition
 **
-** Copyright (c) 2019-2021, Golden Code Development Corporation.
+** Copyright (c) 2019-2022, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --------------------------------------Description---------------------------------------
 ** 001 GES 20190903 First version which contains a Java class instance and uses reflection
@@ -10,6 +10,7 @@
 ** 002 CA  20191211 Convert unknowns to Java null, when invoking Java code from 4GL code.
 ** 003 CA  20200412 Added incremental conversion support.
 ** 004 GES 20210708 Removed dead code.
+**         20220428 Set class and package name methods as overrides.
 */
 
 /*
@@ -175,7 +176,8 @@
     * 
     * @return   See above.
     */
-   public String getJavaSimpleName()
+   @Override
+   public String getSimpleJavaName()
    {
       return cls.getSimpleName();
    }
@@ -185,6 +187,7 @@
     * 
     * @return   See above.
     */
+   @Override
    public String getJavaPackage()
    {
       return cls.getPackage().getName();
@@ -198,6 +201,7 @@
     *
     * @return   The converted method name or {@code null} if no match is found.
     */
+   @Override
    public String getConvertedMethodName(String legacySig)
    {
       throw new UnsupportedOperationException("This should not be called!");

=== modified file 'src/com/goldencode/p2j/uast/JavaFieldDefinition.java'
--- old/src/com/goldencode/p2j/uast/JavaFieldDefinition.java	2019-09-27 12:57:02 +0000
+++ new/src/com/goldencode/p2j/uast/JavaFieldDefinition.java	2022-05-12 15:03:49 +0000
@@ -2,10 +2,11 @@
 ** Module   : JavaFieldDefinition.java
 ** Abstract : a wrapper for a Java field
 **
-** Copyright (c) 2019, Golden Code Development Corporation.
+** Copyright (c) 2019-2022, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --------------------------------Description---------------------------------
 ** 001 GES 20190922 First version.
+** 002 GES 20220114 Minor code cleanup.
 */
 
 /*
@@ -155,23 +156,23 @@
       // let the superclass handle the parts that it can handle
       super.annotateOptions(ast, original, inMethod, sym);
       
-      ast.putAnnotation("is-java", new Boolean(true));
+      ast.putAnnotation("is-java", true);
       ast.putAnnotation("javaname", getName());
       
       // this is never the original definition because it references a Java member
       int mods   = JavaClassDefinition.javaAccessLevel(f.getModifiers());
       int access = JavaClassDefinition.javaModToTokenType(mods);
-      ast.putAnnotation("access-mode", new Long(access));
+      ast.putAnnotation("access-mode", (long) access);
       
       if (isStatic())
       {
-         ast.putAnnotation("static", new Boolean(true));
+         ast.putAnnotation("static", true);
       }
       
       if (isExtent())
       {
          // Java arrays are always indeterminate
-         ast.putAnnotation("extent", new Long(-1));
+         ast.putAnnotation("extent", (long) -1);
       }
    }   
 }

=== added file 'src/com/goldencode/p2j/uast/MatchMetrics.java'
--- old/src/com/goldencode/p2j/uast/MatchMetrics.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/uast/MatchMetrics.java	2022-05-12 15:03:49 +0000
@@ -0,0 +1,219 @@
+/*
+** Module   : MemberMetrics.java
+** Abstract : container for metrics state related to the usage of a given class member
+**
+** Copyright (c) 2021, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- --------------------------------------Description---------------------------------------
+** 001 GES 20210408 First version. 
+*/
+
+/*
+** 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.uast;
+
+/**
+ * Stores metrics state related to the usage of a given class member.  This state cannot be stored in the
+ * member data itself, since it is different at every usage location of the given member.  
+ */
+class MatchMetrics
+implements Comparable<MatchMetrics>
+{
+   /** The member to which the metrics refer. */
+   MemberData data;
+   
+   /** Number of exact matches. */
+   int exact = 0;
+   
+   /** Number of matches due to widening or narrowing conversion. */
+   int cvt = 0;
+   
+   /** Number of exact extent matches (none to none, fixed to fixed, indeterminate to indeterminate). */
+   int extExact = 0;
+   
+   /** Number of matches due to fixed extent to indeterminate conversion. */
+   int extCvt = 0;
+   
+   /** Number of wildcard matches. */
+   int wildcard = 0;
+   
+   /** Number of OO inheritance levels the caller type is from the callee type. */
+   int[] ooLvl;
+   
+   /** Type overrides needed due to fuzzy matching. */
+   String[] overrides;
+   
+   /**
+    * Construct an instance which references the given member.
+    * 
+    * @param    data
+    *           The member being referenced.
+    */
+   MatchMetrics(MemberData data)
+   {
+      this.data      = data;
+      this.ooLvl     = new int[data.signature.length];
+      this.overrides = new String[data.signature.length];
+   }
+   
+   /**
+    * Compares this object with the given other object to calculate a relative ordering between the two
+    * instances.
+    * 
+    * @param    other
+    *           The other instance.  Must NOT be {@code null}.
+    *           
+    * @return   -1, 0 or 1 if this instance is less than, equal to, or greater than the other object.
+    * 
+    * @throws   NullPointerException
+    *           If the parameter is {@code null}.
+    */
+   public int compareTo(MatchMetrics other)
+   {
+      int diffExact  = exact - other.exact;
+      int diffCvt    = cvt - other.cvt;
+      int diffExt    = extExact - other.extExact;
+      int diffExtCvt = extCvt - other.extCvt;
+      
+      if (diffExact == 0 && diffCvt == 0 && diffExt == 0 && diffExtCvt == 0)
+      {
+         return compareInheritanceLevels(other);
+      }
+      else if (diffExact > 0 ||
+               (diffExact == 0 && diffCvt > 0) ||
+               (diffExact == 0 && diffCvt == 0 && diffExt > 0) ||
+               (diffExact == 0 && diffCvt == 0 && diffExt == 0 && diffExtCvt > 0))
+      {
+         return 1;
+      }
+      else
+      {
+         return -1;
+      }
+   }
+   
+   /**
+    * Report if the two instances have equivalent state.
+    * 
+    * @param    other
+    *           The other instance to check against.
+    *          
+    * @return   {@code true} if the other instance is non-null, is the same class and the state is equivalent
+    *           as reported by {@link #compareTo}.  {@code false} otherwise.
+    */
+   public boolean equals(Object other)
+   {
+      if (other == null || !(other instanceof MatchMetrics))
+      {
+         return false;
+      }
+      
+      return compareTo((MatchMetrics) other) == 0;
+   }
+
+   /**
+    * Render the state of the instance in a text form.
+    *
+    * @return   The rendered instance.
+    */
+   public String toString()
+   {
+      return String.format("METRICS %d exact; %d cvt; %d extExact; %d extCvt; %d wildcard; " +
+                           "%s ooLvl; %s overrides; \n        %s data",
+                           exact,
+                           cvt,
+                           extExact,
+                           extCvt,
+                           wildcard,
+                           ooLvl,
+                           overrides,
+                           data);       
+   }
+   
+   /**
+    * Compares this object with the given other object to calculate if one of them matched based on OO
+    * inheritance and is "closer" in its relationship.  The first OO level that is different will
+    * determine the outcome unless no OO levels are different in which case the instances are equivalent.
+    * 
+    * @param    other
+    *           The other instance.  Must NOT be {@code null}.
+    *           
+    * @return   -1, 0 or 1 if this instance is less than (farther than), equal to, or greater than (closer
+    *           than) the other object.
+    * 
+    * @throws   NullPointerException
+    *           If the parameter is {@code null}.
+    */
+   private int compareInheritanceLevels(MatchMetrics other)
+   {
+      for (int i = 0; i < ooLvl.length; i++)
+      {
+         int diff = ooLvl[i] - other.ooLvl[i];
+         
+         if (diff > 0)
+         {
+            // farther away than other
+            return -1;
+         }
+         else if (diff < 0)
+         {
+            // closer than other
+            return 1;
+         }
+      }
+      
+      // no difference across the entire array
+      return 0;
+   }
+}

=== added file 'src/com/goldencode/p2j/uast/MemberData.java'
--- old/src/com/goldencode/p2j/uast/MemberData.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/uast/MemberData.java	2022-05-22 17:44:42 +0000
@@ -0,0 +1,322 @@
+/*
+** Module   : MemberData.java
+** Abstract : container for state related to a class member
+**
+** Copyright (c) 2007-2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- --------------------------------------Description---------------------------------------
+** 001 GES 20220522 Moved from an existing inner class to a standalone class. Added some state. 
+*/
+
+/*
+** 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.uast;
+
+import java.util.*;
+import java.util.function.*;
+
+import com.goldencode.ast.*;
+
+/**
+ * Container to track data associated with a member of a class.
+ */
+class MemberData
+implements ProgressParserTokenTypes
+{
+   /** Text name. */
+   String name = null;
+   
+   /** Token type. */
+   int type = -1;
+   
+   /** Extent value (defaults to 0, which means scalar) of the return type. */
+   int extent = 0;
+   
+   /** Access mode. */
+   int access = -1;
+   
+   /** Temporary ID. */
+   int tempIdx = -1;
+   
+   /** Static or instance member. */
+   boolean isStatic = false;
+   
+   /** Class type name for members that return an object instance. */
+   String qname = null;
+   
+   /** Parameter signature for methods. */
+   ParameterKey[] signature = null;
+   
+   /** Associated variable instance for data members. */
+   Variable var = null; // used only during parsing/front phase
+   
+   /** The containing class where this was defined. */
+   ClassDefinition container = null;
+   
+   /** The parameter modes. */
+   String modes = null;
+   
+   /**
+    * Marker for OO vars which are gathered during pre-scan, and they might not be actual class
+    * members (as pre-scan processes DEFINE VAR statements at all levels, including methods).
+    */
+   boolean provisional = false;
+   
+   /** The AST for this member definition. After parsing is finished, this is cleared. */
+   Aast ast = null;
+   
+   /** The AST's ID for this member definition. */
+   Long astId = null;
+
+   /** All annotations set at the method's return type. */
+   // WARNING: this is not set by initialize, in case of METHOD
+   Map<String, Object> retType;
+   
+   /** 
+    * The converted Java name for this member. Used only for previously parsed classes, or
+    * legacy builtin classes.
+    */
+   String javaname = null;
+   
+   /** For methods, this defines if the method overrides a parent class/interface method. */
+   boolean override = false;
+
+   /** For methods, this defines if the method is an abstract method. */
+   boolean abstr = false;   
+   
+   /**
+    * Create an instance, setting the data members.
+    *
+    * @param    name
+    *           The name of the member.
+    * @param    var
+    *           Associated variable instance for data members or <code>null</code>.
+    * @param    signature
+    *           Parameter signature for methods or <code>null</code>.
+    * @param    type
+    *           The token type associated with the member.
+    * @param    access
+    *           Access mode (<code>KW_PUBLIC</code>,
+    *           <code>KW_PROTECTD</code> or <code>KW_PRIVATE</code>).
+    * @param    isStatic
+    *           <code>true</code> if the variable/property is static.
+    * @param    qname
+    *           Fully qualified class name where <code>type</code> equals
+    *           <code>OO_METH_CLASS</code>. <code>null</code> if this method
+    *           does not return an object instance.
+    * @param    container
+    *           The container class definition.
+    * @param    tempIdx
+    *           Temporary ID.
+    */
+   MemberData(String              name,
+              Variable            var,
+              ParameterKey[]      signature,
+              int                 type,
+              int                 access,
+              boolean             isStatic,
+              String              qname,
+              ClassDefinition     container,
+              int                 tempIdx,
+              Map<String, Object> retType)
+   {
+      this.name      = name;
+      this.var       = var;
+      this.signature = signature;
+      this.type      = type;
+      this.access    = access;
+      this.isStatic  = isStatic;
+      this.qname     = qname;
+      this.retType   = retType;
+      this.container = container;
+      this.tempIdx   = tempIdx;
+      
+      if (signature != null)
+      {
+         this.modes = "";
+         for (ParameterKey pk : signature)
+         {
+            if (pk.mode == null)
+            {
+               modes = modes + "B";
+               continue;
+            }
+            
+            switch (pk.mode)
+            {
+               case KW_INPUT:
+                  modes = modes + "I";
+                  break;
+               case KW_OUTPUT:
+                  modes = modes + "O";
+                  break;
+               case KW_IN_OUT:
+                  modes = modes + "U";
+                  break;
+               case KW_BUFFER:
+                  modes = modes + "B";
+                  break;
+            }
+         }
+      }
+   }
+   
+   /**
+    * Render the state of the instance in a text form.
+    *
+    * @return   The rendered instance.
+    */
+   public String toString()
+   {
+      StringBuilder sb = new StringBuilder();
+
+      String qual = (qname == null) ? "" : String.format(" - %s", qname);
+      
+      sb.append(String.format("%s%s member %s (%s%s), extent %d, javaname %s, override %b, " +
+                              "abstract %b in class %s, tempIdx %d\n",
+                              ProgressParser.lookupTokenName(access),
+                              isStatic ? " static" : " instance",
+                              name,
+                              ProgressParser.lookupTokenName(type),
+                              qual,
+                              extent,
+                              javaname == null ? "NONE" : javaname,
+                              override,
+                              abstr,
+                              container.getName(),
+                              tempIdx));
+       
+      if (signature != null)
+      {
+         sb.append(String.format("   PARAMETERS: %d\n", signature.length));
+         
+         for (int i = 0; i < signature.length; i++)
+         {
+            sb.append(String.format("      %s\n", signature[i]));
+         }
+      }
+      
+      return sb.toString();
+   }
+   
+   /**
+    * The parse for this entire file has finished, and the AST IDs have been computed.  
+    * This is the time the capture the AST IDs or add other annotations, as is executed just
+    * before the AST is persisted.
+    */
+   void parseFinished()
+   {
+      BiConsumer<Aast, Aast> p = (ast, parent) ->
+      {
+         if (ast.isAnnotation("access-mode"))
+         {
+            parent.putAnnotation("access-mode", (Long) ast.getAnnotation("access-mode"));
+            ast.removeAnnotation("access-mode");
+         }
+         if (ast.isAnnotation("oo-data-store"))
+         {
+            parent.putAnnotation("oo-data-store", (String) ast.getAnnotation("oo-data-store"));
+            ast.removeAnnotation("oo-data-store");
+         }
+         if (ast.isAnnotation("static"))
+         {
+            parent.putAnnotation("static", (Boolean) ast.getAnnotation("static"));
+            ast.removeAnnotation("static");
+         }
+      };
+      if (ast != null)
+      {
+         int type = ast.getType();
+         if (type == KW_TEMP_TAB ||
+             (type == SYMBOL && ast.getParent().getType() == DEFINE_BUFFER))
+         {
+            Aast parent = ast.getParent();
+            p.accept(ast, parent);
+            
+            if (type == KW_TEMP_TAB || type == KW_WORK_TAB)
+            {
+               ast = parent;
+            }
+            else
+            {
+               ast = parent;
+               ast = ast.getImmediateChild(KW_FOR, null);
+               ast = (Aast) ast.getFirstChild();
+            }
+         }
+         if (type == SYMBOL && ast.upPath("DEFINE_QUERY/KW_QUERY"))
+         {
+            Aast parent = ast.getAncestor(2);
+            p.accept(ast, parent);
+            
+            parent.putAnnotation("name", ast.getText());
+            ast = parent;
+         }
+         
+         astId = ast.getId();
+         ast = null;
+      }
+   }
+   
+   /**
+    * Set the {@link #provisional} state of this member.
+    * 
+    * @param    provisional
+    *           The new state.
+    */
+   void setProvisional(boolean provisional)
+   {
+      this.provisional = provisional;
+   }   
+}

=== added file 'src/com/goldencode/p2j/uast/ParameterConsumer.java'
--- old/src/com/goldencode/p2j/uast/ParameterConsumer.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/uast/ParameterConsumer.java	2022-05-12 15:03:49 +0000
@@ -0,0 +1,85 @@
+/*
+** Module   : ParameterConsumer.java
+** Abstract : functional interface to process a given parameter
+**
+** Copyright (c) 2021, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- --------------------------------------Description---------------------------------------
+** 001 GES 20210507 First version.
+*/
+
+/*
+** 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.uast;
+
+import com.goldencode.ast.*;
+
+/**
+ * Functional interface to process a given parameter.
+ */
+@FunctionalInterface
+public interface ParameterConsumer
+{
+   /**
+    * Process the given parameter key which is associated with the passed AST node.
+    * 
+    * @param    ast
+    *           The parameter's AST node.
+    * @param    key
+    *           The calculated parameter key for this node.
+    * @param    idx
+    *           The 0-based ordinal position of this parameter.
+    */
+   public void accept(Aast ast, ParameterKey key, int idx);
+}

=== modified file 'src/com/goldencode/p2j/uast/ParameterKey.java'
--- old/src/com/goldencode/p2j/uast/ParameterKey.java	2022-05-16 16:24:45 +0000
+++ new/src/com/goldencode/p2j/uast/ParameterKey.java	2022-05-20 12:39:21 +0000
@@ -4,11 +4,13 @@
 **
 ** Copyright (c) 2018-2022, Golden Code Development Corporation.
 **
-** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
+** -#- -I- --Date-- --------------------------------------Description---------------------------------------
 ** 001 CA  20181229 First version.
 ** 002 CA  20190423 WrappedResource must be treated as a handle type.  Normalized DataSet type's case.
-** 003 CA  20190508 Changes to solve collisions in converted Java signature collisions for overloaded methods.
+** 003 CA  20190508 Changes to solve collisions in converted Java signature collisions for overloaded 
+**                  methods.
 ** 004 CA  20220516 Fixed 'toJava' for OO extent parameters.
+**     GES 20220520 Added typeEquivalent(), other helpers and the way Java types are rendered.
 */
 
 /*
@@ -75,8 +77,6 @@
 /**
  * Container for a parameter's key, including its data type and mode.  Used both at caller and
  * at the OO method definition.
- * 
- * TODO: table parameters are matched via the temp-table's field types
  */
 public class ParameterKey
 {
@@ -110,6 +110,91 @@
    }
    
    /**
+    * Create a new parameter key.
+    * 
+    * @param    lparam
+    *           The legacy parameter annotation.
+    */
+   public ParameterKey(LegacyParameter lparam)
+   {
+      String pmode = lparam.mode();
+      
+      if ("INPUT".equalsIgnoreCase(pmode))
+      {
+         this.mode = KW_INPUT;
+      }
+      else if ("OUTPUT".equalsIgnoreCase(pmode))
+      {
+         this.mode = KW_OUTPUT;
+      }
+      else if ("INPUT-OUTPUT".equalsIgnoreCase(pmode))
+      {
+         this.mode = KW_IN_OUT;
+      }
+      else if ("RETURN".equalsIgnoreCase(pmode))
+      {
+         this.mode = KW_RETURN;
+      }
+      else
+      {
+         this.mode = UNKNOWN_TOKEN;
+      }
+
+      String txt = lparam.type();
+      
+      if ("bdt".equalsIgnoreCase(txt))
+      {
+         // OO legacy classes which have POLY parameters have BDT as their type, at the Java implementation.
+         txt = "Object";
+      }
+      
+      if (!lparam.qualified().isEmpty())
+      {
+         txt += "<? extends " + lparam.qualified().toLowerCase() + ">";
+      }
+      else if ("object".equalsIgnoreCase(lparam.type()))
+      {
+         txt += "<? extends progress.lang.object>";
+      }
+      
+      if (lparam.extent() == -1)
+      {
+         txt += "[]";
+      }
+      else if (lparam.extent() != -2)
+      {
+         txt += "[" + lparam.extent() + "]";
+      }
+      
+      this.type = txt;
+   }
+   
+   /**
+    * Report if the type of the two parameters is the same. The mode is not considered.
+    * 
+    * @param    other
+    *           The instance to compare against.
+    *           
+    * @return   {@code true} if the types are the same or are both {@code null}.  If they are different
+    *           (including if only one is {@code null}) then {@code false} will be returned. 
+    */
+   public boolean typeEquivalent(ParameterKey other)
+   {
+      return Objects.equals(this.type, other.type);
+   }
+   
+   /**
+    * Report the type without any encoded extent.
+    * 
+    * @return   The type with any extent portion removed. 
+    */
+   public String scalarType()
+   {
+      int idx = type.indexOf('[');
+      return (idx < 0) ? type : type.substring(0, idx);
+   }
+   
+   /**
     * Test {@code other} object for equality.
     * 
     * @param   other
@@ -124,14 +209,15 @@
       {
          return true;
       }
-      if (other == null || getClass() != other.getClass())
+      
+      if (other == null || !(other instanceof ParameterKey))
       {
          return false;
       }
       
       ParameterKey otherKey = (ParameterKey) other;
       
-      return Objects.equals(this.mode, otherKey.mode) && Objects.equals(this.type, otherKey.type);
+      return Objects.equals(this.mode, otherKey.mode) && typeEquivalent(otherKey);
    }
    
    /**
@@ -157,7 +243,17 @@
    @Override
    public String toString()
    {
-      return (mode == null ? "" : ProgressParser.lookupTokenName(mode)) + " " + type;
+      return renderMode() + " " + type;
+   }
+
+   /**
+    * Describe the instance as a 4GL parameter.
+    * 
+    * @return   See above.
+    */
+   public String render()
+   {
+      return renderMode() + " " + renderType();
    }
 
    /**
@@ -176,39 +272,117 @@
     * @return    See above.
     */
    public String toJava()
-   {
-      // TODO: some of these calls must be marked as 'dynamic'??
-
+   {      
+      String t = type.toLowerCase();
+      
+      // "dataset <signature>"
+      if (t.startsWith("dataset "))
+      {
+         switch (mode)
+         {
+            case KW_INPUT:
+               return "InputDataSetParameter";
+            case KW_OUTPUT:
+               return "OutputDataSetParameter";
+            case KW_IN_OUT:
+               return "InputOutputDataSetParameter";
+         }
+      }
+
+      if (t.equals("dataset-handle"))
+      {
+         switch (mode)
+         {
+            case KW_INPUT:
+               return "InputDataSetHandle";
+            case KW_OUTPUT:
+               return "OutputDataSetHandle";
+            case KW_IN_OUT:
+               return "InputOutputDataSetHandle";
+         }
+      }
+      
+      // "temp-table <signature>"
+      if (t.startsWith("temp-table "))
+      {
+         switch (mode)
+         {
+            case KW_INPUT:
+               return "InputTableParameter";
+            case KW_OUTPUT:
+               return "OutputTableParameter";
+            case KW_IN_OUT:
+               return "InputOutputTableParameter";
+         }
+      }
+
+      if (t.equals("table-handle"))
+      {
+         switch (mode)
+         {
+            case KW_INPUT:
+               return "InputTableHandle";
+            case KW_OUTPUT:
+               return "OutputTableHandle";
+            case KW_IN_OUT:
+               return "InputOutputTableHandle";
+         }
+      }
+      
+      // we must process the "dataset <...|int[]>" and "temp-table <...|int[]>" cases above first; by
+      // the time we get here, we are dealing with BDT cases only
       if (type.indexOf('[') > 0)
       {
-         switch (mode.intValue())
+         switch (mode)
          {
             case KW_INPUT:
                return type.startsWith("object") 
                         ? "object[]" 
                         : type.substring(0, type.indexOf('[')) + "[]"; // extent is lost
             case KW_OUTPUT:
-               return OutputExtentParameter.class.getSimpleName();
+               return "OutputExtentParameter";
             case KW_IN_OUT:
-               return InputOutputExtentParameter.class.getSimpleName();
+               return "InputOutputExtentParameter";
          }
       }
       
-      if (type.startsWith("object"))
-      {
-         return object.class.getSimpleName();
-      }
-      
-      if (type.equalsIgnoreCase("dataset") || type.equalsIgnoreCase("dataset-handle"))
-      {
-         return DataSetParameter.class.getSimpleName();
-      }
-
-      if (type.equalsIgnoreCase("temp-table") || type.equalsIgnoreCase("table-handle"))
-      {
-         return TableParameter.class.getSimpleName();
-      }
-      
+      if (t.startsWith("object"))
+      {
+         return "object";
+      }
+            
       return type;
    }
+   
+   /**
+    * Get a string representation of the mode.
+    * 
+    * @return   The mode.
+    */
+   private String renderType()
+   {
+      String text = type;
+      
+      // note the trailing space, it is important to avoid matching "DATASET-HANDLE"
+      if (type.startsWith("DATASET "))
+      {
+         text = "DATASET";
+      }
+      else if (type.startsWith("TEMP-TABLE"))
+      {
+         text = "TEMP-TABLE";
+      }
+      
+      return text;
+   }
+   
+   /**
+    * Get a string representation of the mode.
+    * 
+    * @return   The mode.
+    */
+   private String renderMode()
+   {
+      return mode == null ? "" : ProgressParser.lookupTokenName(mode);
+   }   
 }

=== modified file 'src/com/goldencode/p2j/uast/ScanDriver.java'
--- old/src/com/goldencode/p2j/uast/ScanDriver.java	2022-05-01 17:28:59 +0000
+++ new/src/com/goldencode/p2j/uast/ScanDriver.java	2022-05-16 20:54:31 +0000
@@ -66,6 +66,7 @@
 **                           profiles to be ran at once, with the conversion switching the state between 
 **                           profiles, when a resource (like a file or namespace) is being processed.  Only
 **                           front phase is supported at this time.
+**     GES 20220516          Avoid duplicate parsing caused by recursive parsing of classes.
 */
 
 /*
@@ -368,19 +369,25 @@
                
                try
                {
+                  boolean exists = AstManager.get().isExistingAst(AstGenerator.getAstName(fileName));
+                  
                   if (!silent && singleThreaded)
                   {
-                     System.out.println(fileName);
-                  }
-                  
-                  // process the file, note that for HTML files the file list
-                  // may be edited to remove the output file name that the E4GL
-                  // preprocessor generates, if it is separately listed in
-                  // the list (this avoids duplicate processing of that file
-                  // since it was already processed fully to an AST when it was
-                  // listed as an HTML file, it doesn't need to be processed
-                  // again)
-                  gen.processFile(fileName, safeList, index);
+                     System.out.printf("%s%s\n", fileName, exists ? " (already parsed)" : "");
+                  }
+
+                  // only parse if the file has not been recursively parsed already
+                  if (!exists)
+                  {
+                     // process the file, note that for HTML files the file list
+                     // may be edited to remove the output file name that the E4GL
+                     // preprocessor generates, if it is separately listed in
+                     // the list (this avoids duplicate processing of that file
+                     // since it was already processed fully to an AST when it was
+                     // listed as an HTML file, it doesn't need to be processed
+                     // again)
+                     gen.processFile(fileName, safeList, index);
+                  }
                }
                catch(Exception exc)
                {

=== modified file 'src/com/goldencode/p2j/uast/SymbolResolver.java'
--- old/src/com/goldencode/p2j/uast/SymbolResolver.java	2022-05-16 16:24:45 +0000
+++ new/src/com/goldencode/p2j/uast/SymbolResolver.java	2022-05-23 20:07:33 +0000
@@ -1,5 +1,5 @@
 /*
-** Module   : SymbolResolver.java
+** Module   : SymbolRame, false);esolver.java
 ** Abstract : manages the symbol dictionaries and lookup process
 **
 ** Copyright (c) 2004-2022, Golden Code Development Corporation.
@@ -474,6 +474,12 @@
 **     CA  20220516          Fixed tracking of extent data members (variables or properties).
 **                           Indexed extent property setters have the index as int64 (and this is forced for
 **                           explicit indexed setters, too).
+**     GES 20220502          Added lookupTableInfo() to reduce the number of name node lookups. Reworked
+**                           translateType() to properly handle buffers, tables, datasets, table-handles,
+**                           dataset-handles and objects. Reworked method name processing and how we track
+**                           methods so that converted method name conflicts are resolved within the class
+**                           hierarchy. This minimizes the number of conflicts since previously the 
+**                           disambiguation was done at a project level.
 */
 
 /*
@@ -742,7 +748,7 @@
       };
       
       @Override
-      public void addVariable(String name, Variable var, Aast node, int access, boolean isStatic, 
+      public void addVariable(int type, String name, Variable var, Aast node, int access, boolean isStatic, 
                               String qname, boolean provisional)
       {
          // no-op
@@ -1295,8 +1301,23 @@
     */
    public static ParameterKey[] calculateDefinitionSignature(Aast def)
    {
-      ParameterKey[] signature = new ParameterKey[0];
-      
+      List<ParameterKey> parms = new LinkedList<ParameterKey>();
+      
+      processDefinitionSignature(def, (Aast ast, ParameterKey key, int idx) -> parms.add(key));
+      
+      return parms.toArray(new ParameterKey[0]);
+   }
+
+   /**
+    * Process a method definition signature (left to right) executing the given consumer for each parameter.
+    * 
+    * @param    def
+    *           The METHOD_DEF node whose signature is processed.
+    * @param    handler
+    *           The consumer of each parameter.
+    */
+   public static void processDefinitionSignature(Aast def, ParameterConsumer handler)
+   {
       if (def != null)
       {
          Aast methkw  = null;
@@ -1317,7 +1338,7 @@
             if (def.getType() != KW_METHOD && def.getType() != KW_CONSTRUC)
             {
                System.out.printf("BAD METHOD NODE %s\n", def.dumpTree());
-               return null;
+               return;
             }
          }
          
@@ -1327,8 +1348,6 @@
          
          if (lparens != null && lparens.getFirstChild() != null)
          {
-            signature = new ParameterKey[lparens.getNumImmediateChildren()];
-            
             // iterate all parameters and extract their types
             Aast parm = (Aast) lparens.getFirstChild();
             
@@ -1336,14 +1355,7 @@
             {
                if (parm.getType() == PARAMETER)
                {
-                  String name = translateType((int) ((long) parm.getAnnotation("type")), parm);
-                  
-                  if ("object".equals(name) || "jobject".equals(name))
-                  {
-                     String qual = (String) parm.getAnnotation("qualified");
-                     
-                     name = String.format("%s<? extends %s>", name, qual);
-                  }
+                  String name = translateType((int) ((long) parm.getAnnotation("type")), parm, null);
                   
                   name = formatExtentName(name, readExtent(parm));
                   
@@ -1359,15 +1371,15 @@
                   }
                   
                   // at definition time, the default mode (if missing) is INPUT
-                  signature[k++] = new ParameterKey(ptype == null ? KW_INPUT : ptype.intValue(), name);
+                  handler.accept(parm,
+                                 new ParameterKey(ptype == null ? KW_INPUT : ptype.intValue(), name),
+                                 k);
                }
                parm = (Aast) parm.getNextSibling();
             }
          }
       }
-      
-      return signature;
-   }
+   }   
    
    /**
     * Read the extent value for the given node if it is specified as an annotation or if there
@@ -1422,7 +1434,7 @@
       else
       {
          Aast child = node.getImmediateChild(KW_EXTENT, null);
-         
+
          if (child != null)
          {
             // there is an extent, default to indeterminate
@@ -1474,54 +1486,7 @@
    }
    
    /**
-    * Get the Java method name for a legacy signature.
-    * 
-    * @param    name
-    *           The legacy name.
-    * @param    sig
-    *           The definition signature.
-    *           
-    * @return   The converted Java name or <code>null</code> if none was computed yet.
-    */
-   public static String getConvertedMethodName(String name, ParameterKey[] sig)
-   {
-      String javaSig = calculateJavaSignature(name, sig);
-      
-      WorkArea wa = locate();
-      Map<String, String> methods = wa.javaMethodCollisions.get(javaSig);
-      if (methods == null)
-      {
-         return null;
-      }
-      
-      String legacySig = calculateLegacySignature(name, sig);
-      return methods.get(legacySig);
-   }
-   
-   /**
-    * Get the Java method name for a legacy signature.
-    * 
-    * @param    legacySig
-    *           The legacy signature.
-    * @param    javaSig
-    *           The converted Java signature.
-    *           
-    * @return   The converted Java name or <code>null</code> if none was computed yet.
-    */
-   public static String getConvertedMethodName(String legacySig, String javaSig)
-   {
-      WorkArea wa = locate();
-      Map<String, String> methods = wa.javaMethodCollisions.get(javaSig);
-      if (methods == null)
-      {
-         return null;
-      }
-      
-      return methods.get(legacySig);
-   }
-   
-   /**
-    * Compute the Java signature for a legacy method - this will include the legacy name and the
+    * Compute the Java signature for a legacy method - this will include the converted method name and the
     * parameter representation in the converted Java code, but not the return type.
     * 
     * @param    name
@@ -1536,25 +1501,63 @@
       WorkArea wa = local.get();
       
       String jname = wa.nconvert.convert(name, MatchPhraseConstants.TYPE_METHOD);
+      
       // in 4GL, the method names are case-insensitive - even if we want a Java signature here,
-      // we still need to use a lowercased name, as otherwise overriden methods may not be resolved
+      // we still need to use a lowercased name, as otherwise overridden methods may not be resolved
       // properly (as 4GL has no restriction for an overridden method to have the same case as
       // the parent method).
       jname = jname.toLowerCase();
-      String s = jname + "(";
+      
+      return jname.toLowerCase() + "(" + calculateJavaParameters(sig) + ")";
+   }
+   
+   /**
+    * Compute the Java signature for the legacy method parameters (does not include the parenthesis).
+    * 
+    * @param    sig
+    *           The parameter definition.
+    *           
+    * @return   The Java representation of the parameter list or "" if there are no parameters.
+    */
+   public static String calculateJavaParameters(ParameterKey[] sig)
+   {
+      String core = "";
+      
       if (sig != null)
       {
-         for (ParameterKey pk : sig)
+         for (int i = 0; i < sig.length; i++)
          {
-            s = s + (s.endsWith("(") ? "" : ", ") + pk.toJava();
+            core = core + (i == 0 ? "" : ", ") + sig[i].toJava();
          }
       }
-      return s + ")";
-   }
-   
-   /**
-    * Compute the legacy signature for a legacy method - this will include the legacy name and the
-    * parameter representation, but not the return type.
+      
+      return core;
+   }
+   
+   /**
+    * Given a Java method with a {@link LegacySignature}, reverse-compute its 4GL signature.
+    * 
+    * @param    mname
+    *           The legacy method name.
+    * @param    lsig
+    *           The legacy signature annotation.
+    */
+   public static String calculateLegacySignature(String mname, LegacySignature lsig)
+   {
+      LegacyParameter[] lparms = lsig.parameters();
+      ParameterKey[]    sig    = new ParameterKey[(lparms == null) ? 0 : lparms.length];
+      
+      for (int i = 0; i < lparms.length; i++)
+      {
+         sig[i] = new ParameterKey(lparms[i]);
+      }
+      
+      return calculateLegacySignature(mname, sig);
+   }
+   
+   /**
+    * Compute the legacy signature for a legacy method - this will include the legacy name (lowercased) and
+    * the parameter list, but not the return type.
     * 
     * @param    name
     *           The legacy name.
@@ -1565,8 +1568,8 @@
     */
    public static String calculateLegacySignature(String name, ParameterKey[] sig)
    {
-      String jname = name.toLowerCase();
-      String s = jname + "(";
+      String s = name.toLowerCase() + "(";
+      
       if (sig != null)
       {
          for (ParameterKey pk : sig)
@@ -1574,6 +1577,7 @@
             s = s + (s.endsWith("(") ? "" : ", ") + pk.toString();
          }
       }
+      
       return s + ")";
    }
 
@@ -1585,161 +1589,149 @@
     * when they are defined in an already converted class.
     * 
     * @param    stype
-    *           The member type.
+    *           Token type of the defining statement.
+    * @param    cls
+    *           The class in which the property or event exists.
+    * @param    node
+    *           The AST node being processed.
     * @param    var
     *           The associated variable for this member.
     * @param    name
     *           The legacy name.
     */
-   public static void trackMethodSignatures(int stype, Variable var, String name)
+   public static void trackPropertyOrEventMethodSignatures(int             stype,
+                                                           ClassDefinition cls,
+                                                           Aast            node,
+                                                           Variable        var,
+                                                           String          name)
    {
       if (isPreScan())
       {
          return;
       }
       
+      boolean over = var.isOverride();
+      boolean abst = var.isAbstract();
+      
       if (stype == DEFINE_PROPERTY)
       {
-         String ptype = translateType(var.getTokenType(), null);
-         if ("object".equals(ptype) && var.getClassName() != null)
-         {
-            ptype = "object<? extends " + var.getClassName() + ">";
-         }
+         String ptype = translateType(var.getTokenType(), null, var.getClassName());
+         
+         
          if (var.isExtent())
          {
-            String ext = var.isExtent() 
-                           ? "[" + (var.getExtent() > 0 ? Integer.toString(var.getExtent()) : "") +"]" 
-                           : "";
-
-            trackMethodSignature(null, "get-" + name, new ParameterKey[0]);
-            trackMethodSignature(null,
-                                 "get-" + name, 
-                                 new ParameterKey[]
-                                 {
-                                    new ParameterKey(KW_INPUT, "int64")
-                                 });
-            trackMethodSignature(null,
-                                 "set-" + name, 
-                                 new ParameterKey[]
-                                 {
-                                    new ParameterKey(KW_INPUT, ptype + ext)
-                                 });
-            trackMethodSignature(null,
-                                 "set-" + name, 
-                                 new ParameterKey[]
-                                 {
-                                    new ParameterKey(KW_INPUT, ptype),
-                                    new ParameterKey(KW_INPUT, "int64")
-                                 });
-            trackMethodSignature(null, "length-of-" + name, new ParameterKey[0]);
-            trackMethodSignature(null,
-                                 "resize-" + name, 
-                                 new ParameterKey[]
-                                 {
-                                    new ParameterKey(KW_INPUT, "int64")
-                                 });
+            String ext = "[" + (var.getExtent() > 0 ? Integer.toString(var.getExtent()) : "") +"]";
+
+            cls.registerPropertyMethod(node, "bulk-get", name, over, abst, new ParameterKey[0]);
+            cls.registerPropertyMethod(node,
+                                       "get",
+                                       name,
+                                       over,
+                                       abst,
+                                       new ParameterKey[]
+                                       {
+                                          new ParameterKey(KW_INPUT, "int64")
+                                       });
+            
+            cls.registerPropertyMethod(node,
+                                       "bulk-set",
+                                       name, 
+                                       over,
+                                       abst,
+                                       new ParameterKey[]
+                                       {
+                                          new ParameterKey(KW_INPUT, ptype + ext)
+                                       });
+            cls.registerPropertyMethod(node,
+                                       "set",
+                                       name,
+                                       over,
+                                       abst,
+                                       new ParameterKey[]
+                                       {
+                                          new ParameterKey(KW_INPUT, ptype),
+                                          new ParameterKey(KW_INPUT, "int64")
+                                       });
+
+            cls.registerPropertyMethod(node,
+                                       "set-all",
+                                       name,
+                                       over,
+                                       abst,
+                                       new ParameterKey[]
+                                       {
+                                          new ParameterKey(KW_INPUT, ptype)
+                                       });
+            
+            cls.registerPropertyMethod(node, "length-of", name, over, abst, new ParameterKey[0]);
+            cls.registerPropertyMethod(node,
+                                       "resize",
+                                       name,
+                                       over,
+                                       abst,
+                                       new ParameterKey[]
+                                       {
+                                          new ParameterKey(KW_INPUT, "int64")
+                                       });
          }
          else
          {
-            trackMethodSignature(null, "get-" + name, new ParameterKey[0]);
-            trackMethodSignature(null,
-                                 "set-" + name, 
-                                 new ParameterKey[]
-                                 {
-                                    new ParameterKey(KW_INPUT, ptype)
-                                 });
+            cls.registerPropertyMethod(node, "get", name, over, abst, new ParameterKey[0]);
+            cls.registerPropertyMethod(node,
+                                       "set",
+                                       name,
+                                       over,
+                                       abst,
+                                       new ParameterKey[]
+                                       {
+                                          new ParameterKey(KW_INPUT, ptype)
+                                       });
          }
       }
       else if (stype == DEFINE_EVENT)
       {
-         trackMethodSignature(null, "subscribe-" + name, 
-                              new ParameterKey[]
-                              {
-                                 new ParameterKey(KW_INPUT, "handle"),
-                                 new ParameterKey(KW_INPUT, "character"),
-                              });
-         trackMethodSignature(null, "unsubscribe-" + name, 
-                              new ParameterKey[]
-                              {
-                                 new ParameterKey(KW_INPUT, "handle"),
-                                 new ParameterKey(KW_INPUT, "character"),
-                              });
-         trackMethodSignature(null, "subscribe-" + name, 
-                              new ParameterKey[]
-                              {
-                                 new ParameterKey(KW_INPUT, "object<? extends progress.lang.object>"),
-                                 new ParameterKey(KW_INPUT, "character"),
-                              });
-         trackMethodSignature(null, "unsubscribe-" + name, 
-                              new ParameterKey[]
-                              {
-                                 new ParameterKey(KW_INPUT, "object<? extends progress.lang.object>"),
-                                 new ParameterKey(KW_INPUT, "character"),
-                              });
+         cls.registerPropertyMethod(node, "subscribe", name, over, abst, 
+                                    new ParameterKey[]
+                                    {
+                                       new ParameterKey(KW_INPUT, "handle"),
+                                       new ParameterKey(KW_INPUT, "character"),
+                                    });
+         cls.registerPropertyMethod(node, "unsubscribe", name, over, abst,
+                                    new ParameterKey[]
+                                    {
+                                       new ParameterKey(KW_INPUT, "handle"),
+                                       new ParameterKey(KW_INPUT, "character"),
+                                    });
+         cls.registerPropertyMethod(node, "subscribe", name, over, abst,
+                                    new ParameterKey[]
+                                    {
+                                       new ParameterKey(KW_INPUT, "object<? extends progress.lang.object>"),
+                                       new ParameterKey(KW_INPUT, "character"),
+                                    });
+         cls.registerPropertyMethod(node, "unsubscribe", name, over, abst,
+                                    new ParameterKey[]
+                                    {
+                                       new ParameterKey(KW_INPUT, "object<? extends progress.lang.object>"),
+                                       new ParameterKey(KW_INPUT, "character"),
+                                    });
          
          // TODO: publish-[evtName] isn't tracked - not sure if even is required to do so
-         // trackMethodSignature(null, "publish-" + name, pkey);
+         // cls.registerPropertyMethod(node, "publish-", name, over, abst, pkey);
       }
    }
    
    /**
-    * Track this method signature so that an overloaded suffix can be computed, to avoid collisions
-    * in the generated Java code.
+    * Convert the given legacy method name to Java and return it.
     * 
-    * @param    javaname
-    *           The javaname to forcibly assign to this legacy signature, or <code>null</code> to
-    *           compute an unique one.
     * @param    name
-    *           The legacy name.
-    * @param    sig
-    *           The parameter definition.
+    *           The the 4GL method name.
+    *           
+    * @return   The converted Java method name.
     */
-   public static void trackMethodSignature(String javaname, String name, ParameterKey[] sig)
+   public static String convertMethodName(String name)
    {
       WorkArea wa = locate();
-      // can't track the static mode at the signature, as at the time we resolve the name, we don't
-      // know if this is for a static or for a instance call.
-      String javaSig = calculateJavaSignature(name, sig);
-      String legacySig = calculateLegacySignature(name, sig);
-      
-      Map<String, String> methods = wa.javaMethodCollisions.get(javaSig);
-      if (methods == null)
-      {
-         wa.javaMethodCollisions.put(javaSig, methods = new LinkedHashMap<>());
-      }
-      if (javaname != null)
-      {
-         // force this method name...
-         methods.put(legacySig, javaname);
-      }
-      else
-      {
-         methods.putIfAbsent(legacySig, javaname);
-      }
-
-      if (methods.get(legacySig) == null)
-      {
-         String jname = wa.nconvert.convert(name, MatchPhraseConstants.TYPE_METHOD);
-         if (methods.size() == 1)
-         {
-            methods.put(legacySig, jname);
-            return;
-         }
-
-         // TODO: the suffix can be replaced with something more verbose, to indicate i.e. the
-         // collision reason
-         int suffix = 1;
-         while (true)
-         {
-            String candidate = jname + "_" + suffix;
-            if (!methods.values().contains(candidate))
-            {
-               methods.put(legacySig, candidate);
-               break;
-            }
-            suffix = suffix + 1;
-         }
-      }
+      return wa.nconvert.convert(name, MatchPhraseConstants.TYPE_METHOD);
    }
    
    /**
@@ -1749,10 +1741,12 @@
     *           The token type representing a variable/parameter type. 
     * @param    parm
     *           The PARAMETER node which is used to extract the type of this parameter.
+    * @param    qualified
+    *           Fully qualified object classname or {@code null} if no qualifier is needed. 
     *
     * @return   The wrapper class name.
     */
-   public static String translateType(int type, Aast parm)
+   public static String translateType(int type, Aast parm, String qualified)
    {
       if (parm != null)
       {
@@ -1764,10 +1758,6 @@
          {
             return "TABLE-HANDLE";
          }
-         else if (parm.downPath("KW_TABLE") || parm.getType() == KW_TABLE)
-         {
-            return "TEMP-TABLE";
-         }
       }
 
       String cls = null;
@@ -1825,22 +1815,49 @@
             break;
          case VAR_CLASS:
             cls = "object";
+            
+            String qual = qualified;
+            
             if (parm != null)
             {
                if (parm.isAnnotation("is-java") && (Boolean) parm.getAnnotation("is-java"))
                {
                   cls = "jobject";
                }
+               
+               if (qualified == null)
+               {
+                  qual = (String) parm.getAnnotation("qualified");
+               }
             }
+            
+            cls = String.format("%s<? extends %s>", cls, qual);
             break;
             
-         // TABLE and BUFFER parameters - these will have the TABLE or BUFFER prefix,
-         // followed by the schemaname.
+         // TABLE and BUFFER parameters - these will have the TEMP-TABLE or BUFFER prefix,
+         // followed by the schemaname or other details that make the table unambigous
          case KW_TABLE:
             ref = parm.getType() == KW_TABLE ? parm : parm.getImmediateChild(KW_TABLE, null);
             ref = ref.getImmediateChild(TEMP_TABLE, null);
+            
+            if (ref == null)
+            {
+               // this is unexpected since permanent tables and work-tables should not be possible here
+               // and buffers are handled below
+               throw new RuntimeException(String.format("TABLE parameters should be temp-tables: %s",
+                                                        parm.dumpTree()));
+            }
+            
+            String signature = (String) ref.getAnnotation("db_signature");
+            
+            if (signature == null)
+            {
+               throw new RuntimeException(String.format("Missing 'db_signature' annotation in: %s",
+                                                        ref.dumpTree()));
+            }
+            
             // for temp-tables, the compile time mapping is done via the table's field signature
-            cls = "TEMP-TABLE";
+            cls = String.format("TEMP-TABLE <%s>", signature);
             break;
          case KW_BUFFER:
             ref = parm.getType() == KW_BUFFER ? parm : parm.getImmediateChild(KW_BUFFER, null);
@@ -1852,18 +1869,51 @@
             if (ref.getType() == TEMP_TABLE || 
                 (ref.getType() == BUFFER && "".equals(ref.getAnnotation("dbname"))))
             {
-               cls = "BUFFER <temp-table>";
+               String tabname = (String) ref.getAnnotation("schemaname");
+               String tabsig  = (String) ref.getAnnotation("db_signature");
+               
+               if (tabsig == null)
+               {
+                  throw new RuntimeException(String.format("Missing 'db_signature' annotation in: %s",
+                                                           ref.dumpTree()));
+               }
+               
+               cls = String.format("BUFFER <temp-table %s <%s>>", tabname.toLowerCase(), tabsig);
             }
             else
             {
-               cls = String.format("BUFFER %s", ref.getAnnotation("schemaname"));
+               cls = String.format("BUFFER <%s>", ref.getAnnotation("schemaname"));
             }
             break;
          case KW_DATASET:
-            cls = "DATASET";
+            ref = parm.getType() == KW_DATASET ? parm : parm.getImmediateChild(KW_DATASET, null);
+            
+            if (ref == null)
+            {
+               throw new RuntimeException(String.format("Missing KW_DATASET in: %s", parm.dumpTree()));
+            }
+            
+            ref = (Aast) ref.getFirstChild();
+            
+            if (ref == null || ref.getType() != DATA_SET)
+            {
+               throw new RuntimeException(String.format("Missing DATA_SET in: %s", parm.dumpTree()));
+            }
+            
+            String sig = (String) ref.getAnnotation("db_signature");
+            
+            if (sig == null)
+            {
+               throw new RuntimeException(String.format("Missing 'db_signature' annotation in: %s",
+                                                        ref.dumpTree()));
+            }
+            
+            cls = String.format("DATASET %s", sig);
             break;
          case KW_DATA_SRC:
-            // TODO: example with this as parameter and argument?
+            // TODO: as far as I know, this is not possible; dump some data so we can generate an example 
+            //       with this as parameter and argument
+            System.err.printf("WARNING: UNEXPECTED DATA-SOURCE PARAMETER %s\n", parm.dumpTree());
             cls = "DATA-SOURCE";
             break;
       }
@@ -1919,10 +1969,11 @@
                 type != KW_BY_VALUE)
             {
                String cls = ExpressionConversionWorker.expressionType(parm, false, false);
-               
-               if ("Buffer".equalsIgnoreCase(cls))
+
+               // this is not just buffers, it is also any form of table reference except for table-handle
+               if ("Buffer".equalsIgnoreCase(cls) || "DATASET".equalsIgnoreCase(cls))
                {
-                  cls = translateType(parm.getType(), parm);
+                  cls = translateType(parm.getType(), parm, null);
                }
                else
                {
@@ -2012,7 +2063,7 @@
    }
 
    /**
-    * Load the specified converted class.  This lookup an instance from an already-converted class.
+    * Load the specified class definition.
     * 
     * @param    qname
     *           The legacy OO class qualified name.
@@ -2022,7 +2073,7 @@
     * @throws   ClassNotFoundException
     *           If the class can't be resolved or loaded.
     */
-   public static ConvertedClassName loadConvertedClassByQname(String qname)
+   public static ClassDefinition loadConvertedClassByQname(String qname)
    throws ClassNotFoundException
    {
       String fname = ConversionData.findClassFilename(qname);
@@ -2036,7 +2087,7 @@
    }
    
    /**
-    * Load the specified converted class.  This will either create a {@link ClassDefinition}
+    * Load the specified class definition.  This will either create a {@link ClassDefinition}
     * structure for a new class (to be later populated with members by the parser), or will restore
     * an instance from an already-converted class.
     * 
@@ -2046,12 +2097,12 @@
     *           Flag indicating that this is a new class (now being parsed), or a previously
     *           converted class.
     *           
-    * @return   See above.
+    * @return   The class definition.
     * 
     * @throws   ClassNotFoundException
     *           If the class can't be resolved or loaded.
     */
-   public static ConvertedClassName loadConvertedClass(String filename, boolean isNew)
+   public static ClassDefinition loadConvertedClass(String filename, boolean isNew)
    throws ClassNotFoundException
    {
       WorkArea wa = local.get();
@@ -2068,38 +2119,23 @@
          wa.fname2qname.put(filename, qname);
       }
       
-      ConvertedClassName cclass = wa.cvtCache.get(qname);
-      if (cclass != null)
-      {
-         return cclass;
-      }
-      
+      ClassDefinition clsdef = null;
+            
       if (isNew)
       {
-         ClassDefinition clsDef = getClassDefinition(qname);
-         if (clsDef != null)
-         {
-            Object[] clsDetails = ConversionData.getConvertedClassName(filename);
-            cclass = new ConvertedClassName(clsDef, 
-                                            (String) clsDetails[0], 
-                                            (String) clsDetails[1]);
-            wa.cvtCache.put(qname, cclass);
-            
-            return cclass;
-         }
-         else
+         clsdef = getClassDefinition(qname);
+         
+         if (clsdef == null)
          {
             throw new ClassNotFoundException("Can not find class " + qname + " from " + filename);
          }
       }
-      
-      ClassDefinition clsDef = loadClassDefinition(qname);
-      cclass = new ConvertedClassName(clsDef, clsDef.getJavaPackage(), clsDef.getJavaSimpleName());
-      clsDef.initialize(cclass);
-      
-      wa.cvtCache.put(qname, cclass);
-      
-      return cclass;
+      else
+      {
+         clsdef = loadClassDefinition(qname);
+      }
+      
+      return clsdef;
    }
    
    /**
@@ -3909,29 +3945,21 @@
     */
    public void addClass(Aast symbol, Aast inherits, Aast imple, String file)
    {
-      Set<String>     ifaces = null;
-      String          name   = symbol.getText();
+      String name = symbol.getText();
       
       WorkArea wa = locate();
       if (wa.preScanPass == 0)
       {
-         loadClass(name);
+         loadClass(name, true);
       }
       
       SearchResult found = resolveClassName(name);
       ClassDefinition[] parents = calculateParents(name, inherits, found.dotnet);
+      ClassDefinition[] ifaces  = null; 
       
       if (imple != null)
       {
-         ifaces = new HashSet<String>();
-         
-         Aast child = imple.getChildAt(0);
-         
-         while (child != null)
-         {
-            ifaces.add((String) child.getAnnotation("qualified"));
-            child = (Aast) child.getNextSibling();
-         }
+         ifaces = calculateParents(name, imple, found.dotnet);         
       }
 
       addClass(name, parents, ifaces, found.builtin, found.dotnet, file);
@@ -3959,7 +3987,7 @@
     */
    public void addClass(String            name,
                         ClassDefinition[] parents,
-                        Set<String>       ifaces,
+                        ClassDefinition[] ifaces,
                         boolean           builtin,
                         boolean           dotnet,
                         String            file)
@@ -4046,7 +4074,7 @@
       WorkArea wa = locate();
       if (wa.preScanPass == 0)
       {
-         loadClass(name);
+         loadClass(name, true);
       }
 
       ClassDefinition iface = lookupClass(name);
@@ -4096,7 +4124,7 @@
       WorkArea wa = locate();
       if (wa.preScanPass == 0)
       {
-         loadClass(name);
+         loadClass(name, true);
       }
 
       ClassDefinition edef = lookupClass(name);
@@ -4132,7 +4160,7 @@
          {
             Variable value__ = new Variable("value__", VAR_INT, null);
             addVariable(true, value__);
-            edef.addVariable("value__", value__, null, KW_PUBLIC, false, null, false);
+            edef.addVariable(DEFINE_ENUM, "value__", value__, null, KW_PUBLIC, false, null, false);
             value__.setAnnotationHelper((Variable var, Aast ast, boolean ori, boolean meth, SymbolResolver sym) ->
             {
                ast.putAnnotation("dotnet-enum-implicit-value", true);
@@ -4176,7 +4204,7 @@
          var.annotateOptions(symbol, true, false, this);
       
          // we register it as static because it is implicitly static in its lookup
-         cls.addVariable(name, var, symbol, KW_PUBLIC, true, qname, false);
+         cls.addVariable(DEFINE_ENUM, name, var, symbol, KW_PUBLIC, true, qname, false);
       }
    }
    
@@ -4315,6 +4343,42 @@
     */
    public String loadClass(String name)
    {
+      return loadClass(name, false);
+   }
+   
+   /**
+    * Load (using recursion if needed) the hierarchy of class definitions (or
+    * a single interface definition) referenced by the given name. This should
+    * be called when a reference to a class or interface name occurs (except
+    * in the case of the <code>CLASS</code> or <code>INTERFACE</code> language
+    * statements). The name can be unqualified (requiring a call to
+    * {@link #resolveClassName} to determine the fully qualified name) or it
+    * can be either partially or fully qualified (partially qualified names were
+    * added in v11). Once resolved, if the class or interface definition
+    * is already loaded, then this will return immediately.  Otherwise the
+    * class or interface file will be found via the <code>PROPATH</code> and
+    * that file's AST will be loaded.  If the AST does not exist, then the
+    * file will first be parsed. During parsing, the <code>CLASS</code> or 
+    * <code>INTERFACE</code> language statements will call {@link #addClass}
+    * or {@link #addInterface} respectively. This will instantiate the proper
+    * {@link ClassDefinition} objects and the contained definitions for
+    * methods, member variables and properties will be added by the rules
+    * that match those language features. Once parsing is complete, the
+    * definition will be fully formed and loaded. Any references to other
+    * classes or interfaces in that file will cause recursive parsing/loading
+    * using this method, until the entire object hierarchy is loaded. At that
+    * point, this chain of methods will return.
+    *
+    * @param    name
+    *           Unqualified or partially/fully qualified class or interface name to
+    *           load.
+    * @param    topLevel
+    *           {@code true} if the named class is already being parsed in the caller.
+    *
+    * @return   The fully qualified class or interface name that was loaded.
+    */
+   public String loadClass(String name, boolean topLevel)
+   {   
       SearchResult found = resolveClassName(name);
 
       ClassDefinition cls = lookupClass(found.clsname);
@@ -4383,7 +4447,7 @@
             generator.setCache(true);
             generator.setDumpLexer(true);
             generator.setDumpParser(true);
-            generator.setAstPersist(false);
+            generator.setAstPersist(true);
          }
 
          String relative = Configuration.normalizeFilename(found.filename);
@@ -4410,6 +4474,12 @@
 
             // the PROPATH is inherited from the caller
             generator.preScanClass(relative, found.builtin, found.dotnet, this.propath);
+            
+            // save all referenced classes/interfaces except for the genesis object
+            if (wa.preScanPass > 1)
+            {
+               wa.processed.add(found.clsname);
+            }
          }
          catch (AstException   |
                 ANTLRException |
@@ -4451,23 +4521,30 @@
             String err  = String.format(spec, found.clsname, relative);
             throw new RuntimeException(err);
          }
-      }         
-
-      // the class has been loaded (now or previously) - ensure all super-classes are loaded, too
-      if (!isPreScan())
-      {
-         if (generator == null)
+         
+         if (!isPreScan())
          {
-            generator = new AstGenerator();
-            generator.setSilent(silent);
-            generator.setPreprocess(true);
-            generator.setCache(true);
-            generator.setDumpLexer(true);
-            generator.setDumpParser(true);
-            generator.setAstPersist(false);
+            // we cannot just process the genesis object that started the graph; we must process all
+            // referenced classes/interfaces including those not in our parent hierarchy AND we need
+            // to process them in the same order they were processed previously
+            if (!wa.processed.isEmpty())
+            {
+               for (String fname : wa.processed)
+               {
+                  // ensure all super-classes are loaded for this object
+                  parseHierarchy(fname, wa);
+               }
+               
+               wa.processed.clear();
+            }
+            
+            // do NOT handle the 2nd level parsing of the genesis object here if that parsing is already
+            // happening in the caller
+            if (!topLevel)
+            {
+               parseHierarchy(cls.getName(), wa);
+            }
          }
-      
-         parseHierarchy(cls.getName());
       }      
 
       return found.clsname;
@@ -4573,7 +4650,8 @@
       // annotateClassRef, but left here in case there are other situations added.
       if (qname == null)
       {
-         System.out.println("FAILURE: Returning from annotateClassRef due null qname " + node.dumpTree(true));
+         System.out.println("FAILURE: Returning from annotateClassRef because of null qname " +
+                            node.dumpTree(true));
          return;
       }
       ClassDefinition cls = lookupClass(qname);
@@ -5038,8 +5116,6 @@
     *
     * @param    stype
     *           Defining language statement type.
-    * @param    extent
-    *           The extent.  Use 0 for no-extent (scalar member).
     * @param    name
     *           Member name.
     * @param    node
@@ -5050,17 +5126,27 @@
     * @param    stic
     *           Node of type <code>KW_STATIC</code> if the variable/property is
     *           static.
+    * @param    ov
+    *           Override node if specified.
+    * @param    ab
+    *           Abstract node if specified.
     * @param    qname
     *           Fully qualified class name for members that are references to a given class
     *           or <code>null</code> if this member is not an object instance.
+    * @param    extent
+    *           The extent value or 0 if there is no extent.  The AST cannot be inspected at this point
+    *           because all the child notes are not linked into the root node yet.  For this reason, the
+    *           explicit value must be passed.
     */
-   public void addDataMember(int    stype, 
-                             int    extent, 
-                             String name, 
-                             Aast   node, 
-                             Aast   access, 
-                             Aast   stic, 
-                             String qname)
+   public void addDataMember(int    stype,
+                             String name,
+                             Aast   node,
+                             Aast   access,
+                             Aast   stic,
+                             Aast   ov,
+                             Aast   ab,
+                             String qname,
+                             int    extent)
    {
       // this is called both in pre-scan and normal parse phases:
       // 1. in pre-scan, it will pickup and collect the DEF VAR scoped with methods/c'tor/etc
@@ -5078,7 +5164,7 @@
          
          boolean isStatic = (stic != null);
          boolean isProp = (stype == DEFINE_PROPERTY);
-                  
+         
          // if the previous definition is a property, and we are in pre-scan mode, then ignore it; this is
          // just a dirty fix for pre-scan mode, it's not complete
          if (var.isProp() && !isProp && isPreScan())
@@ -5089,13 +5175,15 @@
          var.setProperty(isProp);
          var.setStatic(isStatic);
          var.setExtent(extent);
-
-         trackMethodSignatures(stype, var, name);
+         var.setOverride(ov != null);
+         var.setAbstract(ab != null);
+            
+         trackPropertyOrEventMethodSignatures(stype, cls, node, var, name);
          
          // a variable is provisional during prescan, if and only if we have no explicit access modifier set
          boolean provisional = isPreScan() && stype == DEFINE_VARIABLE && access == null;
          
-         cls.addVariable(name, var, node, amode, isStatic, qname, provisional);
+         cls.addVariable(stype, name, var, node, amode, isStatic, qname, provisional);
       }
    }
    
@@ -6991,7 +7079,7 @@
     * given the field's name and an optional table name-node.
     * 
     * @param   table
-    *          Name node asssociated with the field reference. If not {@code null}, only the
+    *          Name node associated with the field reference. If not {@code null}, only the
     *          private namespace of the given {@code table} is searched (for an exact match)
     *          of {@code fieldName}. If {@code null}, a lenient search (i.e., abbreviations
     *          permitted) is performed across all scopes in the schema dictionary.
@@ -7050,6 +7138,45 @@
    }
    
    /**
+    * Look up and return the table info instance which describes a database table in a schema.
+    *
+    * @param   name
+    *          Text string to match, exactly as found in the original source code
+    *          (abbreviated, qualified or unqualified). 
+    * @param   forceTemp
+    *          If <code>true</code>, force a temp-table lookup.
+    * @param   forcePersistent
+    *          If <code>true</code>, force a persistent table lookup.
+    *
+    * @return  The table info instance or <code>null</code> if no match was found.
+    */
+   public TableInfo lookupTableInfo(String name, boolean forceTemp, boolean forcePersistent)
+   {
+      TableInfo result = null;
+      
+      try
+      {
+         if (schemaDict != null)
+         {
+            SchemaHelper<TableInfo> info = (SchemaDictionary dict) -> 
+            {
+               return dict.findTableInfo(name, forceTemp, forcePersistent);
+            };
+               
+            Predicate<TableInfo> notNull = (TableInfo val) -> { return (val == null); };
+               
+            result = processHierarchy(info, notNull);
+         }
+      }
+      catch (SchemaException exc)
+      {
+         exc.printStackTrace();
+      }
+      
+      return result;
+   }
+   
+   /**
     * Look up and return the AST which represents a database field in a schema, given the field's
     * name.
     * 
@@ -8347,11 +8474,13 @@
     * 
     * @param    clsName
     *           The qualified legacy name.
+    * @param    wa
+    *           The work area.
     *           
     * @throws   NullPointerException
     *           If a parent class can not be found. 
     */
-   private void parseHierarchy(String clsName)
+   private void parseHierarchy(String clsName, WorkArea wa)
    throws NullPointerException
    {
       ClassDefinition cdef = getClassDefinition(clsName);
@@ -8364,13 +8493,13 @@
       if (cdef.isProcessed())
       {
          return;
-      }
-      
+      }      
+
       if (cdef.getInterfaces() != null)
       {
          for (String iface : cdef.getInterfaces())
          {
-            parseHierarchy(iface);
+            parseHierarchy(iface, wa);
          }
       }
       
@@ -8380,10 +8509,10 @@
       {
          for (ClassDefinition parent : parents)
          {
-            parseHierarchy(parent.getName());
+            parseHierarchy(parent.getName(), wa);
          }
       }
-
+      
       cdef.setProcessed(true);
       
       String file = cdef.getFilename();
@@ -9487,7 +9616,7 @@
                return value;
             };
             
-            result = def.calcFromParentGraph(func, continu, backup);
+            result = def.calcFromParentGraph(func, continu, backup, true, false);
             
             if (exc[0] != null)
             {
@@ -9726,12 +9855,6 @@
        */
       private Map<String, ClassDefinition> classCache = new HashMap<>();
       
-      /** 
-       * A cache of converted class information (not by the current run). The key is the qualified 
-       * class name. 
-       */
-      private Map<String, ConvertedClassName> cvtCache = new HashMap<>();
-      
       /** Mapping of class files to their qualified name. */
       private Map<String, String> fname2qname = new HashMap<>();
       
@@ -9763,18 +9886,10 @@
       /** The set of class/interface references processed during 1st level parse of a class. */
       private Set<String> preScanRefs = new HashSet<>();
       
-      /**
-       * A mapping of (legacy-name, Java signature) to a map of legacy signatures, for which a 
-       * certain Java name must be enforced.  This is required to solve two cases:
-       * <ul>
-       *   <li>For override methods, the converted Java name must match.</li>
-       *   <li>For overloaded methods which convert to identical Java signaturesm, the converted
-       *       Java method name must be different.</li>
-       * </ul>
-       */
-      private Map<String, Map<String, String>> javaMethodCollisions = new HashMap<>();
+      /** An ordered set of class/interface references processed. */
+      private Set<String> processed = new LinkedHashSet<>();
       
-      /** The converted to generated Java names. */
+      /** The converter to generated Java names. */
       private NameConverter nconvert = new NameConverter();
   }
 }

=== modified file 'src/com/goldencode/p2j/uast/Variable.java'
--- old/src/com/goldencode/p2j/uast/Variable.java	2021-11-12 20:01:50 +0000
+++ new/src/com/goldencode/p2j/uast/Variable.java	2022-05-22 16:49:53 +0000
@@ -2,9 +2,9 @@
 ** Module   : Variable.java
 ** Abstract : contains the data needed to define a Progress variable
 **
-** Copyright (c) 2005-2021, Golden Code Development Corporation.
+** Copyright (c) 2005-2022, Golden Code Development Corporation.
 **
-** -#- -I- --Date-- --JPRM-- ----------------Description-----------------
+** -#- -I- --Date-- --JPRM-- ----------------------------------Description----------------------------------
 ** 001 GES 20050210   @19752 First version, simple bean-like approach.
 ** 002 GES 20050330   @20563 Added support for a large number of options
 **                           and helper methods to copy options and write
@@ -84,6 +84,8 @@
 **                           Added a custom mechanism for caller-defined annotations using the
 **                           AnnotationHelper interface.
 **     CA  20211112          XML-NODE-NAME must be annotated at the table field.
+**     GES 20220521          Added dataset signature annotation processing.  Cleaned up some warnings.
+**                           Removed unnecessary property handling. Added override flag.
 */
 
 /*
@@ -254,6 +256,12 @@
    
    /** Flag to differentiate instance and static members. */
    private boolean isStatic = false;
+
+   /** Flag for override option (properties and events only). */
+   private boolean isOverride = false;   
+   
+   /** Flag for abstract option (properties and events only). */
+   private boolean isAbstract = false;   
    
    /** Access mode if not defined in a method. */
    private int access = -1;
@@ -336,8 +344,6 @@
          Aast kwInit = copy.getImmediateChild(KW_INIT, null);
          if (kwInit != null && kwInit.getNumImmediateChildren() > 1)
          {
-            int size = kwInit.getNumImmediateChildren();
-            
             // Due to a bug in Progress 9.1c (and possibly other versions),
             // a temp-table field or var like some specific element of any
             // extent field/var always picks up the initial value of the
@@ -351,6 +357,8 @@
             Aast initTgt = kwInit.getChildAt(extent);
             if (initTgt == null)
             {
+               int size = kwInit.getNumImmediateChildren();
+               
                // We have more than one initializer value, but not enough
                // to match the index we were given, so use the last
                // available.
@@ -539,6 +547,26 @@
    }   
    
    /**
+    * Returns the override flag.
+    *
+    * @return   <code>true</code> if this is an overridden property or event.
+    */
+   public boolean isOverride()
+   {
+      return isOverride;
+   }   
+   
+   /**
+    * Returns the abstract flag.
+    *
+    * @return   <code>true</code> if this is an abstract property or event.
+    */
+   public boolean isAbstract()
+   {
+      return isAbstract;
+   }   
+   
+   /**
     * Returns the static flag.
     *
     * @return   <code>true</code> if this is a static member.
@@ -549,6 +577,28 @@
    }
    
    /**
+    * Sets the state of the override flag.
+    *
+    * @param    isOverride
+    *           <code>true</code> if this is an overridden property or event.
+    */
+   public void setOverride(boolean isOverride)
+   {
+      this.isOverride = isOverride;
+   }   
+   
+   /**
+    * Sets the state of the abstract flag.
+    *
+    * @param    isAbstract
+    *           <code>true</code> if this is an abstract property or event.
+    */
+   public void setAbstract(boolean isAbstract)
+   {
+      this.isAbstract = isAbstract;
+   }   
+   
+   /**
     * Sets the state of the property flag.
     *
     * @param    isProp
@@ -835,7 +885,7 @@
          boolean isEnum = ast.getType() == ENUM_VALUE;
          if (tempIdx != -1 && !builtin)
          {
-            ast.putAnnotation("tempidx", new Long(tempIdx));
+            ast.putAnnotation("tempidx", (long) tempIdx);
             if (clsDef != null)
             {
                ast.putAnnotation("tempidx-file", clsDef.getFilename());
@@ -874,7 +924,7 @@
          
          if (builtin)
          {
-            ast.putAnnotation("builtin", new Boolean(true));
+            ast.putAnnotation("builtin", true);
          }
          
          if (!original && clsDef != null)
@@ -885,7 +935,7 @@
          
          if (isProp)
          {
-            ast.putAnnotation("cls-property", new Boolean(true));
+            ast.putAnnotation("cls-property", true);
             ast.putAnnotation("cls-container", clsDef.getFilename());
          }
 
@@ -898,30 +948,61 @@
             // set default access mode (it will be overridden below if there is an
             // explicit mode specified
             access = isProp || isEnum || ast.getType() == DEFINE_EVENT ? KW_PUBLIC : KW_PRIVATE;
-            ast.putAnnotation("access-mode", new Long(access));
+            ast.putAnnotation("access-mode", (long) access);
+         }
+         
+         if (!original && ast.getType() == DATA_SET)
+         {
+            Aast ds = def.getImmediateChild(SYMBOL, null);
+            
+            if (ds == null)
+            {
+               throw new RuntimeException(String.format("Malformed dataset definition %s.", def.dumpTree()));
+            }
+            
+            String sig = (String) ds.getAnnotation("db_signature");
+            
+            if (sig == null)
+            {
+               throw new RuntimeException(String.format("Missing 'db_signature' annotation in dataset " +
+                                                        "definition %s.",
+                                                        def.dumpTree()));
+            }
+            
+            ast.putAnnotation("db_signature", sig);
          }
          
          if (!original && access != -1)
          {
             // access mode matters on the original definition, propagate it here
-            ast.putAnnotation("access-mode", new Long(access));
+            ast.putAnnotation("access-mode", (long) access);
          }
          
          if (isStatic || isEnum)
          {
-            ast.putAnnotation("static", new Boolean(true));
+            ast.putAnnotation("static", true);
+         }
+         
+         if (isOverride)
+         {
+            ast.putAnnotation("override", true);
+         }
+         
+         if (isAbstract)
+         {
+            ast.putAnnotation("abstract", true);
          }
          
          if (extent != 0)
          {
-            ast.putAnnotation("extent", new Long(extent));
+            ast.putAnnotation("extent", (long) extent);
          }
          
          if (isEnum)
          {
             def = ast;
             ast.putAnnotation("name", name);
-            ast.putAnnotation("type", new Long(tokenType));
+            ast.putAnnotation("type", (long) tokenType);
             return;
          }
          
@@ -942,7 +1023,7 @@
          
          if (tokenType != -1)
          {
-            ast.putAnnotation("type", new Long(tokenType));
+            ast.putAnnotation("type", (long) tokenType);
          }
          
          Aast child        = (Aast) def.getFirstChild();
@@ -978,23 +1059,23 @@
                case KW_INPUT:
                case KW_IN_OUT:
                case KW_OUTPUT:
-                  ast.putAnnotation("parmtype", new Long(ctype));
+                  ast.putAnnotation("parmtype", (long) ctype);
                   break;
                case KW_PUBLIC:
                case KW_PROTECTD:
                case KW_PRIVATE:
                   // remember the explicit type
                   access = ctype;
-                  ast.putAnnotation("access-mode", new Long(ctype));
+                  ast.putAnnotation("access-mode", (long) ctype);
                   break;
                case KW_STATIC:
-                  ast.putAnnotation("static", new Boolean(true));
+                  ast.putAnnotation("static", true);
                   break;
                case KW_ABSTRACT:
-                  ast.putAnnotation("abstract", new Boolean(true));
+                  ast.putAnnotation("abstract", true);
                   break;
                case KW_OVERRIDE:
-                  ast.putAnnotation("override", new Boolean(true));
+                  ast.putAnnotation("override", true);
                   break;
                case KW_FORMAT:
                   grandchild = (Aast) child.getFirstChild();
@@ -1048,7 +1129,7 @@
                                  // save our changes
                                  grandchild.setText(gtxt);
                                  
-                                 d = new Double(gtxt);
+                                 d = Double.parseDouble(gtxt);
                               }
                               else if (gtype == HEX_LITERAL)
                               {
@@ -1060,11 +1141,11 @@
                                  
                                  if (gtxt.length() == 0)
                                  {
-                                    d = new Double(0);
+                                    d = (double) 0;
                                  }
                                  else
                                  {
-                                    d = new Double(gtxt);
+                                    d = Double.parseDouble(gtxt);
                                  }
                               }
                               else if (gtype == BOOL_TRUE || gtype == BOOL_FALSE)
@@ -1072,7 +1153,7 @@
                                  // Progress silently accepts this initializer, we
                                  // assume it is the same as 0 (no init), but this
                                  // is untested at this time.
-                                 d = new Double(0);
+                                 d = (double) 0;
                               }
                            }
                            
@@ -1081,7 +1162,7 @@
                         case VAR_INT:
                         case VAR_INT64:
                            Long l = null;
-                        
+                           
                            if (!unknown)
                            {
                               if (gtype == DEC_LITERAL)                     
@@ -1089,12 +1170,12 @@
                                  // Progress allows decimal initializers for
                                  // integers, in this case we must round to the
                                  // nearest integer...
-                                 Double dd = new Double(gtxt);
-                                 l = new Long(Math.round(dd.doubleValue()));
+                                 Double dd = Double.parseDouble(gtxt);
+                                 l = (long) Math.round(dd.doubleValue());
                               }
                               else if (gtype == NUM_LITERAL)
                               {
-                                 l = new Long(gtxt);
+                                 l = Long.parseLong(gtxt);
                               }
                               else if (gtype == HEX_LITERAL)
                               {
@@ -1106,11 +1187,11 @@
                                  
                                  if (gtxt.length() == 0)
                                  {
-                                    l = new Long(0);
+                                    l = (long) 0;
                                  }
                                  else
                                  {
-                                    l = new Long(gtxt);
+                                    l = Long.parseLong(gtxt);
                                  }
                               }
                               else if (gtype == BOOL_TRUE ||
@@ -1119,7 +1200,7 @@
                                  // Progress silently accepts this initializer,
                                  // we assume it is the same as 0 (no init), but
                                  // this is untested at this time.
-                                 l = new Long(0);
+                                 l = (long) 0;
                               }
                            }
                            
@@ -1212,7 +1293,7 @@
                   int prec = ExpressionConversionWorker.literalToInt(child.getChildAt(0));
                   if (tokenType == VAR_DEC && prec != 10)
                   {
-                     ast.putAnnotation("decimals", new Long(prec));
+                     ast.putAnnotation("decimals", (long) prec);
                   }
                   break;
                case KW_EXTENT:
@@ -1264,48 +1345,7 @@
             if (ext != 0)
             {
                this.extent = ext;
-               ast.putAnnotation("extent", new Long(ext));
-            }
-         }
-            
-         if (isProp && clsDef != null && original && 
-             (!SymbolResolver.isPreScan() || (clsDef.isBuiltIn() && !clsDef.isDotNet())))
-         {
-            String[] mnames = 
-            {
-               "get",
-               "set",
-               "resize",
-               "length-of"
-            };
-            Aast ptype = def.getImmediateChild(KW_AS, null);
-            ptype = (Aast) ptype.getFirstChild();
-            String propType = ExpressionConversionWorker.expressionType(ptype, false);
-            ParameterKey[][] pks = 
-            {
-               null,
-               new ParameterKey[] { new ParameterKey(KW_INPUT, propType) },
-               new ParameterKey[] { new ParameterKey(KW_INPUT, "int64") },
-               null,
-            };
-            for (int i = 0; i < mnames.length; i++)
-            {
-               if (!isExtent() && i > 1)
-               {
-                  break;
-               }
-               String mname = mnames[i] + "-" + name;
-               String javaname = null;
-               String legacySignature = SymbolResolver.calculateLegacySignature(mname, pks[i]); 
-               if (clsDef.isBuiltIn())
-               {
-                  javaname = clsDef.getConvertedMethodName(legacySignature);
-               }
-               SymbolResolver.trackMethodSignature(javaname, mname, pks[i]);
-               
-               String javaSignature = SymbolResolver.calculateJavaSignature(mname, pks[i]);
-               def.putAnnotation(mnames[i] + "-legacysignature", legacySignature);
-               def.putAnnotation(mnames[i] + "-javasignature", javaSignature);
+               ast.putAnnotation("extent", (long) ext);
             }
          }
       }
@@ -1374,6 +1414,21 @@
                                  ", format string = " + fmt + ") ");
       }
    }
+
+   /**
+    * Render the instance as text.
+    * 
+    * @return   The details of this instance as text.
+    */
+   public String toString()
+   {
+      return String.format("VARIABLE name %s; token type %d; extent %d; classname %s; AST %s",
+                           name,
+                           tokenType,
+                           extent,
+                           cls,
+                           def == null ? "null" : def.dumpTree());
+   }
    
    /**
     * Helper to copy a child subtree of a specific type from the source to

=== modified file 'src/com/goldencode/p2j/uast/progress.g'
--- old/src/com/goldencode/p2j/uast/progress.g	2022-05-16 16:24:45 +0000
+++ new/src/com/goldencode/p2j/uast/progress.g	2022-05-22 18:41:48 +0000
@@ -2299,6 +2299,9 @@
 **                           otherwise it can collide with a var with the name as a simple class name.
 **     CA  20220514          AVERAGE accumulator can be aliased via AVG.
 **     CA  20220516          Fixed tracking of extent data members (variables or properties).
+**     GES 20220520          Added signature calc/annotation to def_dataset_stmt. Reworked the record
+**                           annotations to be more efficient (to use lookupTableInfo()) and to add the
+**                           table signature annotation.
 */
 
 /*
@@ -8312,7 +8315,7 @@
       sym.addBuiltinFunction("transaction"          , FUNC_LOGICAL    , true ); // used for annotations only
       sym.addBuiltinFunction("trim"                 , FUNC_CHAR       , true );
       sym.addBuiltinFunction("truncate"             , FUNC_DEC        , true );
-      sym.addBuiltinFunction("type-of"              , FUNC_LOGICAL    , false);
+      sym.addBuiltinFunction("type-of"              , FUNC_LOGICAL    , false); // used for annotations only
       sym.addBuiltinFunction("unbox"                , FUNC_POLY       , true );
       sym.addBuiltinFunction("upper"                , FUNC_CHAR       , true );
       sym.addBuiltinFunction("user"                 , FUNC_CHAR       , false);
@@ -10947,8 +10950,6 @@
     *           Resource name being defined.
     * @param    ttype
     *           The stmt type being processed.
-    * @param    extent
-    *           The extent.  Use 0 for no-extent members.
     * @param    def
     *           The definition node for this member.
     * @param    typeDef
@@ -10957,8 +10958,22 @@
     *           Access mode node if specified.
     * @param    st
     *           Static node if specified.
+    * @param    ov
+    *           Override node if specified.
+    * @param    ab
+    *           Abstract node if specified.
+    * @param    extent
+    *           The extent value or 0 if there is no extent.
     */
-   private void processMember(String varname, int ttype, int extent, Aast def, Aast typeDef, Aast am, Aast st)
+   private void processMember(String varname,
+                              int    ttype,
+                              Aast   def,
+                              Aast   typeDef,
+                              Aast   am,
+                              Aast   st,
+                              Aast   ov,
+                              Aast   ab,
+                              int    extent)
    {
       // only define a new data member if we are not inside an OO code block
       if (!inMethodDef)
@@ -10990,7 +11005,7 @@
                }
             }
          
-            sym.addDataMember(ttype, extent, varname, def, am, st, qname);
+            sym.addDataMember(ttype, varname, def, am, st, ov, ab, qname, extent);
          }
       }
    }
@@ -13538,8 +13553,8 @@
          | KW_SERIALAB     // should only apply to dataset, temp-table, var and prop
          | KW_NON_SER      // should only apply to dataset, temp-table, var and prop
          | st:KW_STATIC  { inStaticCtxt = true; }
-         | ab:KW_ABSTRACT  // ignored for now
-         | ov:KW_OVERRIDE  // ignored for now
+         | ab:KW_ABSTRACT  // only applies to prop and event
+         | ov:KW_OVERRIDE  // only applies to prop and event
       )*
       (
            (
@@ -13619,11 +13634,11 @@
                  force = true;
               }
            }
-         | varname=def_prop_stmt[#d, #am, #st]   { #d.setType(DEFINE_PROPERTY); }
-         | varname=def_event_stmt[#d, #am, #st]      { #d.setType(DEFINE_EVENT); }
-         | varname=def_button_stmt[allowColon]   { #d.setType(DEFINE_BUTTON); }
-         | varname=def_rect_stmt[allowColon]     { #d.setType(DEFINE_RECTANGLE); }
-         | varname=def_image_stmt                { #d.setType(DEFINE_IMAGE); }
+         | varname=def_prop_stmt[#d, #am, #st, #ov, #ab]   { #d.setType(DEFINE_PROPERTY); }
+         | varname=def_event_stmt[#d, #am, #st, #ov, #ab]  { #d.setType(DEFINE_EVENT); }
+         | varname=def_button_stmt[allowColon]             { #d.setType(DEFINE_BUTTON); }
+         | varname=def_rect_stmt[allowColon]               { #d.setType(DEFINE_RECTANGLE); }
+         | varname=def_image_stmt                          { #d.setType(DEFINE_IMAGE); }
       )
       (
            // the trigger phrase that can appear in some of these statements can have
@@ -13793,12 +13808,12 @@
               }
            }
            // pre-scan version is used here
-         | varname=def_prop_stmt_pre_scan[#d, #am, #st]
+         | varname=def_prop_stmt_pre_scan[#d, #am, #st, #ov, #ab]
            { #d.setType(DEFINE_PROPERTY); always = true; }
-         | varname=def_event_stmt[#d, #am, #st]  { #d.setType(DEFINE_EVENT); always = true; }
-         | varname=def_button_stmt[allowColon]   { #d.setType(DEFINE_BUTTON); }
-         | varname=def_rect_stmt[allowColon]     { #d.setType(DEFINE_RECTANGLE); }
-         | varname=def_image_stmt                { #d.setType(DEFINE_IMAGE); }
+         | varname=def_event_stmt[#d, #am, #st, #ov, #ab]  { #d.setType(DEFINE_EVENT); always = true; }
+         | varname=def_button_stmt[allowColon]             { #d.setType(DEFINE_BUTTON); }
+         | varname=def_rect_stmt[allowColon]               { #d.setType(DEFINE_RECTANGLE); }
+         | varname=def_image_stmt                          { #d.setType(DEFINE_IMAGE); }
       )
       // the colon and stmt_term is deliberately removed in pre scan mode
       {
@@ -14207,13 +14222,46 @@
          | KW_SERIALZH
          | KW_REF_ONLY
       )*
-      for_record_spec[false, false, false]
+      f:for_record_spec[false, false, false]
       ( 
          options { generateAmbigWarnings = false; }
          :
            data_relation[idx]       { idx++; }
          | parent_id_relation[pidx] { pidx++; }
       )*
+      {
+         Aast   child     = (Aast) #f.getFirstChild();
+         String signature = "";
+         
+         while (child != null)
+         {
+            int type = child.getType();
+         
+            if (type == BUFFER || type == TEMP_TABLE)
+            {
+               String s = (String) child.getAnnotation("db_signature");
+               
+               if (s == null)
+               {
+                  String err = String.format("Missing 'db_signature' for record: %s", child.dumpTree());
+                  throw new RuntimeException(err);
+               }
+               
+               signature += "<" + s + ">";
+            }
+            
+            child = (Aast) child.getNextSibling();
+         }
+         
+         if (signature.length() == 0)
+         {
+            String err2 = String.format("Empty 'db_signature' for dataset: %s", #ds.dumpTree());
+            throw new RuntimeException(err2);
+         }
+         
+         // annotate the SYMBOL node in the definition (will be the first child of DEFINE_DATASET) 
+         #d.putAnnotation("db_signature", signature);
+      }
    ;
    
 /**
@@ -23216,7 +23264,7 @@
             sym.addWidget(varname, WID_FILL_IN);
          }
          
-         processMember(varname, DEFINE_VARIABLE, size, ds, (#a != null) ? #a : #l, am, st);
+         processMember(varname, DEFINE_VARIABLE, ds, (#a != null) ? #a : #l, am, st, null, null, size);
       }
    ;
 
@@ -23251,10 +23299,14 @@
  *           Access mode node if specified.
  * @param    st
  *           Static node if specified.
+ * @param    ov
+ *           Override node if specified.
+ * @param    ab
+ *           Abstract node if specified.
  *
  * @return   The property name being defined.
  */
-def_prop_stmt [Aast ds, Aast am, Aast st] returns [String varname = null]
+def_prop_stmt [Aast ds, Aast am, Aast st, Aast ov, Aast ab] returns [String varname = null]
    :
       {
          int ftype = -1;
@@ -23288,7 +23340,7 @@
 
          // this MUST be done before the getter_setter because references to the property
          // inside those blocks will otherwise be missing some property-specific annotations
-         processMember(varname, DEFINE_PROPERTY, size, ds, #a, am, st);
+         processMember(varname, DEFINE_PROPERTY, ds, #a, am, st, ov, ab, size);
       }
       getter_setter
       (
@@ -23336,10 +23388,14 @@
  *           Access mode node if specified.
  * @param    st
  *           Static node if specified.
+ * @param    ov
+ *           Override node if specified.
+ * @param    ab
+ *           Abstract node if specified.
  *
  * @return   The property name being defined.
  */
-def_prop_stmt_pre_scan [Aast ds, Aast am, Aast st] returns [String varname = null]
+def_prop_stmt_pre_scan [Aast ds, Aast am, Aast st, Aast ov, Aast ab] returns [String varname = null]
    :
       {
          int ftype = -1;
@@ -23373,7 +23429,7 @@
             size = (size == -1 && initcount != -1) ? initcount : size;
          }
 
-         processMember(varname, DEFINE_PROPERTY, size, ds, #a, am, st);
+         processMember(varname, DEFINE_PROPERTY, ds, #a, am, st, ov, ab, size);
       }
       // getter/setter support is deliberately removed in pre scan mode
    ;
@@ -23473,10 +23529,14 @@
  *           Access mode node if specified.
  * @param    st
  *           Static node if specified.
+ * @param    ov
+ *           Override node if specified.
+ * @param    ab
+ *           Abstract node if specified.
  *
  * @return   The event name being defined.
  */
-def_event_stmt [Aast ds, Aast am, Aast st] returns [String varname = null]
+def_event_stmt [Aast ds, Aast am, Aast st, Aast ov, Aast ab] returns [String varname = null]
    :
       ev:KW_EVENTS! { hide(ev); }
       s:any_symbol_at_all
@@ -23501,7 +23561,7 @@
       )
       {
          sym.deleteScope();
-         processMember(varname, DEFINE_EVENT, 0, ds, null, am, st);
+         processMember(varname, DEFINE_EVENT, ds, null, am, st, ov, ab, 0);
       }
    ;
    
@@ -27562,27 +27622,33 @@
       {
          // now store an annotation with the fully qualified name and the
          // shorted unabbreviated unique buffer name
-         String fullname = sym.lookupFullTableName(tablename, forceTemp, forcePersistent);
-         String bufname  = sym.lookupBufferName(tablename, false, forceTemp, forcePersistent);
-         String dbname   = sym.lookupDatabaseName(tablename, forceTemp, forcePersistent);
+         TableInfo info = sym.lookupTableInfo(tablename, forceTemp, forcePersistent);
                   
-         if (fullname != null)         
-            ##.putAnnotation("schemaname", fullname);
-            
-         if (bufname != null)
-            ##.putAnnotation("bufname", bufname);
-            
-         if (dbname != null)
-            ##.putAnnotation("dbname", dbname);
-            
-         // store the backing construct (this will be the same as the token
-         // type for every record type except buffers, in which case it will
-         // be TABLE, TEMP_TABLE or WORK_TABLE)
-         int backing = sym.lookupTableRecordType(tablename, forceTemp, forcePersistent);
-         
-         if (backing != -1)
+         if (info != null)
          {
-            ##.putAnnotation("recordtype", new Long(backing));
+            String sig      = info.getSignature();
+            String fullname = info.getQualified();
+            String bufname  = info.getBuffer();
+            String dbname   = info.getDatabase();
+            int    backing  = info.getRecordType();
+            
+            if (sig != null)
+               ##.putAnnotation("db_signature", sig);
+            
+            if (fullname != null)
+               ##.putAnnotation("schemaname", fullname);
+            
+            if (bufname != null)
+               ##.putAnnotation("bufname", bufname);
+               
+            if (dbname != null)
+               ##.putAnnotation("dbname", dbname);
+               
+            // store the backing construct (this will be the same as the token
+            // type for every record type except buffers, in which case it will
+            // be TABLE, TEMP_TABLE or WORK_TABLE)
+            if (backing != -1)
+               ##.putAnnotation("recordtype", new Long(backing));
          }
       }
    ;
@@ -29157,6 +29223,9 @@
             if (typ != null)
                ##.putAnnotation("type", new Long(typ.getType()));
          }
+         
+         // remember if this is an OO parameter def or a user-defined function parameter def
+         ##.putAnnotation("isOO", oo);
       }
    ;
    

=== modified file 'src/com/goldencode/p2j/util/Agent.java'
--- old/src/com/goldencode/p2j/util/Agent.java	2022-05-05 17:43:41 +0000
+++ new/src/com/goldencode/p2j/util/Agent.java	2022-05-12 15:39:57 +0000
@@ -82,6 +82,7 @@
 **     CA  20220405 Added authentication and authorization for web requests.  When this is enabled, the target
 **                  API call will be executed under the authenticated FWD context, and not the agent's context.
 **     CA  20220428 Fixed memptr and extent output arguments. 
+**     GES 20210501 Upgraded table parameter processing to match API changes.
 */
 
 /*
@@ -2608,7 +2609,6 @@
 
          boolean inputOutput = modes.charAt(i) == 'U';
          boolean output = modes.charAt(i) == 'O';
-         @SuppressWarnings("unused")
          boolean input = modes.charAt(i) == 'I';
          
          if (arg instanceof TableWrapper)
@@ -2616,7 +2616,10 @@
             // a result set will be sent only if the requester and the appserver are not in the  
             // same JVM
             TableWrapper rs = (TableWrapper) arg;
-            arg = new TableParameter(rs, rs.isAppend());
+            arg = new TableParameter(rs,
+                                     ParameterOption.APPEND,
+                                     input || inputOutput, 
+                                     output || inputOutput);
             ((TableParameter) arg).setParameterIndex(i + 1);
          }
          else if (arg instanceof DatasetWrapper)

=== modified file 'src/com/goldencode/p2j/util/CallMode.java'
--- old/src/com/goldencode/p2j/util/CallMode.java	2019-07-22 20:20:22 +0000
+++ new/src/com/goldencode/p2j/util/CallMode.java	2022-05-12 15:03:49 +0000
@@ -3,10 +3,11 @@
 ** Abstract : Specification for a parameter mode, when adding it via a CALL:SET-PARAMETER or
 **            legacy ParameterList class.
 **
-** Copyright (c) 2019, Golden Code Development Corporation.
+** Copyright (c) 2019-2021, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- -------------------------------Description--------------------------------
 ** 001 CA  20190710 Extracted from Call.java.
+** 002 GES 20210430 Moved APPEND into the ParameterOption.
 */
 
 /*
@@ -78,9 +79,6 @@
    /** Flag indicating INPUT-OUTPUT mode. */
    public boolean inputOutput;
    
-   /** Flag indicating APPEND option. */
-   public boolean append;
-   
    /** Parameter passing mode BY-VALUE, BIND or BY-REFERENCE option. */
    public ParameterOption mode = ParameterOption.NONE;
    
@@ -104,23 +102,38 @@
       cmode.mode = ParameterOption.NONE;
       
       String smode = mode.getValue().toUpperCase();
-      String byRef = "-BY-REFERENCE"; // TODO: in OO, you can have 'input-output by-reference'
-      String byVal = "-BY-VALUE";
-      String bind  = "-BIND";
-      if (smode.endsWith(byRef))
+      
+      String append = "-APPEND";
+      String byRef  = "-BY-REFERENCE"; // TODO: in OO, you can have 'input-output by-reference'
+      String byVal  = "-BY-VALUE";
+      String bind   = "-BIND";
+      
+      int optionLen = 0;
+      
+      if (smode.endsWith(append))
+      {
+         cmode.mode = ParameterOption.APPEND;
+         optionLen = append.length();
+      }
+      else if (smode.endsWith(byRef))
       {
          cmode.mode = ParameterOption.BY_REFERENCE;
-         smode = smode.substring(0, smode.length() - byRef.length());
+         optionLen = byRef.length();
       }
       else if (smode.endsWith(byVal))
       {
          cmode.mode = ParameterOption.BY_VALUE;
-         smode = smode.substring(0, smode.length() - byVal.length());
+         optionLen = byVal.length();
       }
       else if (smode.endsWith(bind))
       {
          cmode.mode = ParameterOption.BIND;
-         smode = smode.substring(0, smode.length() - bind.length());
+         optionLen = bind.length();
+      }
+      
+      if (optionLen != 0)
+      {
+         smode = smode.substring(0, smode.length() - optionLen);
       }
       
       switch (smode)
@@ -134,10 +147,6 @@
          case "INPUT-OUTPUT":
             cmode.inputOutput = true;
             break;
-         case "OUTPUT-APPEND":
-            cmode.output = true;
-            cmode.append = true;
-            break;
          default:
             // invalid mode
             return null;

=== modified file 'src/com/goldencode/p2j/util/CallParameter.java'
--- old/src/com/goldencode/p2j/util/CallParameter.java	2021-06-14 07:25:20 +0000
+++ new/src/com/goldencode/p2j/util/CallParameter.java	2022-05-12 15:03:49 +0000
@@ -12,6 +12,7 @@
 **                  handle until Call.invoke is executed.
 **     OM  20210309 Removed double display of error message number.
 **     CA  20210609 Fixed OO property and field references used as arguments (extent or not).
+**     GES 20210430 Reworked table and dataset parameter processing.
 */
 
 /*
@@ -139,6 +140,7 @@
     */
    public boolean copyOutput()
    {
+      // TODO: should DataSetParameter be excluded here?
       if (mode.input || argument instanceof TableParameter)
       {
          return true;
@@ -319,15 +321,19 @@
             return argument;
          }
          
+         boolean input = mode.input || mode.inputOutput;
+         boolean output = mode.output || mode.inputOutput;
+         
          // for table/dataset handle input/input-output validation is delayed till invoke
          if ("TABLE-HANDLE".equalsIgnoreCase(dataType))
          {
-            argument = new TableParameter((handle) value, mode.append, mode.mode);
+            
+            argument = new TableParameter((handle) value, mode.mode, input, output);
             return argument;
          }
          else if ("DATASET-HANDLE".equalsIgnoreCase(dataType))
          {
-            argument = new DataSetParameter((handle) value, mode.append, mode.mode);
+            argument = new DataSetParameter((handle) value, mode.mode, input, output);
             return argument;
          } else if (initialValue instanceof BaseDataType) {
             argument = TypeFactory.initInput((BaseDataType) initialValue);
@@ -401,7 +407,7 @@
          return value;
       }
       
-      if (mode.input) 
+      if (mode.input || mode.inputOutput) 
       {
          Class<? extends BaseDataType> cls = isClass ? object.class : BaseDataType.fromTypeName(dataType);
          

=== added file 'src/com/goldencode/p2j/util/InputOutputTableHandle.java'
--- old/src/com/goldencode/p2j/util/InputOutputTableHandle.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/util/InputOutputTableHandle.java	2022-05-12 15:03:49 +0000
@@ -0,0 +1,168 @@
+/*
+** Module   : InputOutputTableHandle.java
+** Abstract : INPUT-OUTPUT TABLE-HANDLE parameter support.
+**
+** Copyright (c) 2013-2021, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
+** 001 GES 20210430 Created initial version, by moving code from the parent class.
+*/
+
+/*
+** 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.persist.*;
+
+/**
+ * Implements the INPUT-OUTPUT TABLE-HANDLE parameter type. This hard codes the input/output flags and
+ * delegates all other processing to the parent class.
+ */
+public class InputOutputTableHandle
+extends TableParameter
+{
+   /**
+    * Create an instance using a buffer.
+    *
+    * @param    table
+    *           Buffer of the wrapped table.
+    */
+   public InputOutputTableHandle(Temporary table)
+   {
+      super(table, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a buffer.
+    *
+    * @param    table
+    *           Buffer of the wrapped table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputOutputTableHandle(Temporary table, ParameterOption option)
+   {
+      super(table, option);
+   }
+   
+   /**
+    * Create an instance.
+    *
+    * @param    tableHandle
+    *           Handle to the wrapped table.
+    */
+   public InputOutputTableHandle(handle tableHandle)
+   {
+      super(tableHandle, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance.
+    *
+    * @param    tableHandle
+    *           Handle to the wrapped table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputOutputTableHandle(handle tableHandle, ParameterOption option)
+   {
+      super(tableHandle, option);
+   }
+   
+   /**
+    * Create an instance using a result set (for remote parameters).
+    *
+    * @param    resultSet
+    *           Result set containing input data.
+    */
+   public InputOutputTableHandle(TableWrapper resultSet)
+   {
+      super(resultSet, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a result set (for remote parameters).
+    *
+    * @param    resultSet
+    *           Result set containing input data.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputOutputTableHandle(TableWrapper resultSet, ParameterOption option)
+   {
+      super(resultSet, option);
+   }
+   
+   /**
+    * Determines if this parameter is INPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code true}.
+    */
+   @Override
+   public boolean isInputMode()
+   {
+      return true;
+   }
+
+   /**
+    * Determines if this parameter is OUTPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code true}.
+    */
+   @Override
+   public boolean isOutputMode()
+   {
+      return true;
+   }
+}

=== added file 'src/com/goldencode/p2j/util/InputOutputTableParameter.java'
--- old/src/com/goldencode/p2j/util/InputOutputTableParameter.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/util/InputOutputTableParameter.java	2022-05-12 15:03:49 +0000
@@ -0,0 +1,168 @@
+/*
+** Module   : InputOutputTableParameter.java
+** Abstract : INPUT-OUTPUT TABLE parameter support.
+**
+** Copyright (c) 2013-2021, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
+** 001 GES 20210430 Created initial version, by moving code from the parent class.
+*/
+
+/*
+** 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.persist.*;
+
+/**
+ * Implements the INPUT-OUTPUT TABLE parameter type. This hard codes the input/output flags and delegates
+ * all other processing to the parent class.
+ */
+public class InputOutputTableParameter
+extends TableParameter
+{
+   /**
+    * Create an instance using a buffer.
+    *
+    * @param    table
+    *           Buffer of the wrapped table.
+    */
+   public InputOutputTableParameter(Temporary table)
+   {
+      super(table, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a buffer.
+    *
+    * @param    table
+    *           Buffer of the wrapped table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputOutputTableParameter(Temporary table, ParameterOption option)
+   {
+      super(table, option);
+   }
+   
+   /**
+    * Create an instance.
+    *
+    * @param    tableHandle
+    *           Handle to the wrapped table.
+    */
+   public InputOutputTableParameter(handle tableHandle)
+   {
+      super(tableHandle, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance.
+    *
+    * @param    tableHandle
+    *           Handle to the wrapped table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputOutputTableParameter(handle tableHandle, ParameterOption option)
+   {
+      super(tableHandle, option);
+   }
+   
+   /**
+    * Create an instance using a result set (for remote parameters).
+    *
+    * @param    resultSet
+    *           Result set containing input data.
+    */
+   public InputOutputTableParameter(TableWrapper resultSet)
+   {
+      super(resultSet, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a result set (for remote parameters).
+    *
+    * @param    resultSet
+    *           Result set containing input data.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputOutputTableParameter(TableWrapper resultSet, ParameterOption option)
+   {
+      super(resultSet, option);
+   }
+   
+   /**
+    * Determines if this parameter is INPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code true}.
+    */
+   @Override
+   public boolean isInputMode()
+   {
+      return true;
+   }
+
+   /**
+    * Determines if this parameter is OUTPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code true}.
+    */
+   @Override
+   public boolean isOutputMode()
+   {
+      return true;
+   }
+}

=== added file 'src/com/goldencode/p2j/util/InputTableHandle.java'
--- old/src/com/goldencode/p2j/util/InputTableHandle.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/util/InputTableHandle.java	2022-05-12 15:03:49 +0000
@@ -0,0 +1,168 @@
+/*
+** Module   : InputTableHandle.java
+** Abstract : INPUT TABLE-HANDLE parameter support.
+**
+** Copyright (c) 2013-2021, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
+** 001 GES 20210429 Created initial version, by moving code from the parent class.
+*/
+
+/*
+** 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.persist.*;
+
+/**
+ * Implements the INPUT TABLE-HANDLE parameter type. This hard codes the input/output flags and delegates
+ * all other processing to the parent class.
+ */
+public class InputTableHandle
+extends TableParameter
+{
+   /**
+    * Create an instance using a buffer.
+    *
+    * @param    table
+    *           Buffer of the wrapped table.
+    */
+   public InputTableHandle(Temporary table)
+   {
+      super(table, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a buffer.
+    *
+    * @param    table
+    *           Buffer of the wrapped table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputTableHandle(Temporary table, ParameterOption option)
+   {
+      super(table, option);
+   }
+   
+   /**
+    * Create an instance.
+    *
+    * @param    tableHandle
+    *           Handle to the wrapped table.
+    */
+   public InputTableHandle(handle tableHandle)
+   {
+      super(tableHandle, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance.
+    *
+    * @param    tableHandle
+    *           Handle to the wrapped table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputTableHandle(handle tableHandle, ParameterOption option)
+   {
+      super(tableHandle, option);
+   }
+   
+   /**
+    * Create an instance using a result set (for remote parameters).
+    *
+    * @param    resultSet
+    *           Result set containing input data.
+    */
+   public InputTableHandle(TableWrapper resultSet)
+   {
+      super(resultSet, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a result set (for remote parameters).
+    *
+    * @param    resultSet
+    *           Result set containing input data.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputTableHandle(TableWrapper resultSet, ParameterOption option)
+   {
+      super(resultSet, option);
+   }
+   
+   /**
+    * Determines if this parameter is INPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code true}.
+    */
+   @Override
+   public boolean isInputMode()
+   {
+      return true;
+   }
+
+   /**
+    * Determines if this parameter is OUTPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code false}.
+    */
+   @Override
+   public boolean isOutputMode()
+   {
+      return false;
+   }
+}

=== added file 'src/com/goldencode/p2j/util/InputTableParameter.java'
--- old/src/com/goldencode/p2j/util/InputTableParameter.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/util/InputTableParameter.java	2022-05-12 15:03:49 +0000
@@ -0,0 +1,168 @@
+/*
+** Module   : InputTableParameter.java
+** Abstract : INPUT TABLE parameter support.
+**
+** Copyright (c) 2013-2021, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
+** 001 GES 20210429 Created initial version, by moving code from the parent class.
+*/
+
+/*
+** 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.persist.*;
+
+/**
+ * Implements the INPUT TABLE parameter type. This hard codes the input/output flags and delegates
+ * all other processing to the parent class.
+ */
+public class InputTableParameter
+extends TableParameter
+{
+   /**
+    * Create an instance using a buffer.
+    *
+    * @param    table
+    *           Buffer of the wrapped table.
+    */
+   public InputTableParameter(Temporary table)
+   {
+      super(table, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a buffer.
+    *
+    * @param    table
+    *           Buffer of the wrapped table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputTableParameter(Temporary table, ParameterOption option)
+   {
+      super(table, option);
+   }
+   
+   /**
+    * Create an instance.
+    *
+    * @param    tableHandle
+    *           Handle to the wrapped table.
+    */
+   public InputTableParameter(handle tableHandle)
+   {
+      super(tableHandle, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance.
+    *
+    * @param    tableHandle
+    *           Handle to the wrapped table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputTableParameter(handle tableHandle, ParameterOption option)
+   {
+      super(tableHandle, option);
+   }
+   
+   /**
+    * Create an instance using a result set (for remote parameters).
+    *
+    * @param    resultSet
+    *           Result set containing input data.
+    */
+   public InputTableParameter(TableWrapper resultSet)
+   {
+      super(resultSet, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a result set (for remote parameters).
+    *
+    * @param    resultSet
+    *           Result set containing input data.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public InputTableParameter(TableWrapper resultSet, ParameterOption option)
+   {
+      super(resultSet, option);
+   }
+   
+   /**
+    * Determines if this parameter is INPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code true}.
+    */
+   @Override
+   public boolean isInputMode()
+   {
+      return true;
+   }
+
+   /**
+    * Determines if this parameter is OUTPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code false}.
+    */
+   @Override
+   public boolean isOutputMode()
+   {
+      return false;
+   }
+}

=== modified file 'src/com/goldencode/p2j/util/OutputExtentParameter.java'
--- old/src/com/goldencode/p2j/util/OutputExtentParameter.java	2021-08-18 10:18:01 +0000
+++ new/src/com/goldencode/p2j/util/OutputExtentParameter.java	2022-05-19 22:49:46 +0000
@@ -111,6 +111,342 @@
    }
    
    /**
+    * Copy the input array into an output array, using the output type constructor for each non-null
+    * element.
+    * 
+    * @param    input
+    *           The input array.
+    *           
+    * @return   The copy of the input array, with the elements converted to the output data type.
+    */
+   public static integer[] toInteger(int64[] input)
+   {
+      integer[] output = new integer[input.length];
+      
+      for (int i = 0; i < output.length; i++)
+      {
+         if (input[i] != null)
+         {
+            output[i] = new integer(input[i]);
+         }
+      }
+      
+      return output;
+   }
+   
+   /**
+    * Copy the input array into an output array, using the output type constructor for each non-null
+    * element.
+    * 
+    * @param    input
+    *           The input array.
+    *           
+    * @return   The copy of the input array, with the elements converted to the output data type.
+    */
+   public static integer[] toInteger(decimal[] input)
+   {
+      integer[] output = new integer[input.length];
+      
+      for (int i = 0; i < output.length; i++)
+      {
+         if (input[i] != null)
+         {
+            output[i] = new integer(input[i]);
+         }
+      }
+      
+      return output;
+   }
+   
+   /**
+    * Copy the input array into an output array, using the output type constructor for each non-null
+    * element.
+    * 
+    * @param    input
+    *           The input array.
+    *           
+    * @return   The copy of the input array, with the elements converted to the output data type.
+    */
+   public static decimal[] toDecimal(integer[] input)
+   {
+      decimal[] output = new decimal[input.length];
+      
+      for (int i = 0; i < output.length; i++)
+      {
+         if (input[i] != null)
+         {
+            output[i] = new decimal(input[i]);
+         }
+      }
+      
+      return output;
+   }
+
+   /**
+    * Copy the input array into an output array, using the output type constructor for each non-null
+    * element.
+    * 
+    * @param    input
+    *           The input array.
+    *           
+    * @return   The copy of the input array, with the elements converted to the output data type.
+    */
+   public static decimal[] toDecimal(int64[] input)
+   {
+      decimal[] output = new decimal[input.length];
+      
+      for (int i = 0; i < output.length; i++)
+      {
+         if (input[i] != null)
+         {
+            output[i] = new decimal(input[i]);
+         }
+      }
+      
+      return output;
+   }      
+   
+   /**
+    * Copy the input array into an output array, using the output type constructor for each non-null
+    * element.
+    * 
+    * @param    input
+    *           The input array.
+    *           
+    * @return   The copy of the input array, with the elements converted to the output data type.
+    */
+   public static int64[] toInt64(integer[] input)
+   {
+      int64[] output = new int64[input.length];
+      
+      for (int i = 0; i < output.length; i++)
+      {
+         if (input[i] != null)
+         {
+            output[i] = new int64(input[i]);
+         }
+      }
+      
+      return output;
+   }
+   
+   /**
+    * Copy the input array into an output array, using the output type constructor for each non-null
+    * element.
+    * 
+    * @param    input
+    *           The input array.
+    *           
+    * @return   The copy of the input array, with the elements converted to the output data type.
+    */
+   public static int64[] toInt64(decimal[] input)
+   {
+      int64[] output = new int64[input.length];
+      
+      for (int i = 0; i < output.length; i++)
+      {
+         if (input[i] != null)
+         {
+            output[i] = new int64(input[i]);
+         }
+      }
+      
+      return output;
+   }
+   
+   /**
+    * Copy the input array into an output array, using the output type constructor for each non-null
+    * element.
+    * 
+    * @param    input
+    *           The input array.
+    *           
+    * @return   The copy of the input array, with the elements converted to the output data type.
+    */
+   public static character[] toCharacter(longchar[] input)
+   {
+      character[] output = new character[input.length];
+      
+      for (int i = 0; i < output.length; i++)
+      {
+         if (input[i] != null)
+         {
+            output[i] = new character(input[i]);
+         }
+      }
+      
+      return output;
+   }
+
+   /**
+    * Copy the input array into an output array, using the output type constructor for each non-null
+    * element.
+    * 
+    * @param    input
+    *           The input array.
+    *           
+    * @return   The copy of the input array, with the elements converted to the output data type.
+    */
+   public static longchar[] toLongchar(character[] input)
+   {
+      longchar[] output = new longchar[input.length];
+      
+      for (int i = 0; i < output.length; i++)
+      {
+         if (input[i] != null)
+         {
+            output[i] = new longchar(input[i]);
+         }
+      }
+      
+      return output;
+   }
+   
+   /**
+    * Copy the input array into an output array, using the output type constructor for each non-null
+    * element.
+    * 
+    * @param    input
+    *           The input array.
+    *           
+    * @return   The copy of the input array, with the elements converted to the output data type.
+    */
+   public static date[] toDate(datetime[] input)
+   {
+      date[] output = new date[input.length];
+      
+      for (int i = 0; i < output.length; i++)
+      {
+         if (input[i] != null)
+         {
+            output[i] = new date(input[i]);
+         }
+      }
+      
+      return output;
+   }
+   
+   /**
+    * Copy the input array into an output array, using the output type constructor for each non-null
+    * element.
+    * 
+    * @param    input
+    *           The input array.
+    *           
+    * @return   The copy of the input array, with the elements converted to the output data type.
+    */
+   public static date[] toDate(datetimetz[] input)
+   {
+      date[] output = new date[input.length];
+      
+      for (int i = 0; i < output.length; i++)
+      {
+         if (input[i] != null)
+         {
+            output[i] = new date(input[i]);
+         }
+      }
+      
+      return output;
+   }
+
+   /**
+    * Copy the input array into an output array, using the output type constructor for each non-null
+    * element.
+    * 
+    * @param    input
+    *           The input array.
+    *           
+    * @return   The copy of the input array, with the elements converted to the output data type.
+    */
+   public static datetime[] toDatetime(datetimetz[] input)
+   {
+      datetime[] output = new datetime[input.length];
+      
+      for (int i = 0; i < output.length; i++)
+      {
+         if (input[i] != null)
+         {
+            output[i] = new datetime(input[i]);
+         }
+      }
+      
+      return output;
+   }
+   
+   /**
+    * Copy the input array into an output array, using the output type constructor for each non-null
+    * element.
+    * 
+    * @param    input
+    *           The input array.
+    *           
+    * @return   The copy of the input array, with the elements converted to the output data type.
+    */
+   public static datetime[] toDatetime(date[] input)
+   {
+      datetime[] output = new datetime[input.length];
+      
+      for (int i = 0; i < output.length; i++)
+      {
+         if (input[i] != null)
+         {
+            output[i] = new datetime(input[i]);
+         }
+      }
+      
+      return output;
+   }
+
+   /**
+    * Copy the input array into an output array, using the output type constructor for each non-null
+    * element.
+    * 
+    * @param    input
+    *           The input array.
+    *           
+    * @return   The copy of the input array, with the elements converted to the output data type.
+    */
+   public static datetimetz[] toDatetimeTz(date[] input)
+   {
+      datetimetz[] output = new datetimetz[input.length];
+      
+      for (int i = 0; i < output.length; i++)
+      {
+         if (input[i] != null)
+         {
+            output[i] = new datetimetz(input[i]);
+         }
+      }
+      
+      return output;
+   }
+   
+   /**
+    * Copy the input array into an output array, using the output type constructor for each non-null
+    * element.
+    * 
+    * @param    input
+    *           The input array.
+    *           
+    * @return   The copy of the input array, with the elements converted to the output data type.
+    */
+   public static datetimetz[] toDatetimeTz(datetime[] input)
+   {
+      datetimetz[] output = new datetimetz[input.length];
+      
+      for (int i = 0; i < output.length; i++)
+      {
+         if (input[i] != null)
+         {
+            output[i] = new datetimetz(input[i]);
+         }
+      }
+      
+      return output;
+   }
+   
+   /**
     * Initializes the indeterminate extent parameter reference.
     *
     * @return  The initialized indeterminate extent parameter reference.

=== added file 'src/com/goldencode/p2j/util/OutputTableHandle.java'
--- old/src/com/goldencode/p2j/util/OutputTableHandle.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/util/OutputTableHandle.java	2022-05-12 15:03:49 +0000
@@ -0,0 +1,168 @@
+/* 
+** Module   : OutputTableHandle.java
+** Abstract : OUTPUT TABLE-HANDLE parameter support.
+**
+** Copyright (c) 2013-2021, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
+** 001 GES 20210430 Created initial version, by moving code from the parent class.
+*/
+
+/*
+** 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.persist.*;
+
+/**
+ * Implements the OUTPUT TABLE-HANDLE parameter type. This hard codes the input/output flags and delegates
+ * all other processing to the parent class.
+ */
+public class OutputTableHandle
+extends TableParameter
+{
+   /**
+    * Create an instance using a buffer.
+    *
+    * @param    table
+    *           Buffer of the wrapped table.
+    */
+   public OutputTableHandle(Temporary table)
+   {
+      super(table, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a buffer.
+    *
+    * @param    table
+    *           Buffer of the wrapped table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public OutputTableHandle(Temporary table, ParameterOption option)
+   {
+      super(table, option);
+   }
+   
+   /**
+    * Create an instance.
+    *
+    * @param    tableHandle
+    *           Handle to the wrapped table.
+    */
+   public OutputTableHandle(handle tableHandle)
+   {
+      super(tableHandle, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance.
+    *
+    * @param    tableHandle
+    *           Handle to the wrapped table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public OutputTableHandle(handle tableHandle, ParameterOption option)
+   {
+      super(tableHandle, option);
+   }
+   
+   /**
+    * Create an instance using a result set (for remote parameters).
+    *
+    * @param    resultSet
+    *           Result set containing input data.
+    */
+   public OutputTableHandle(TableWrapper resultSet)
+   {
+      super(resultSet, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a result set (for remote parameters).
+    *
+    * @param    resultSet
+    *           Result set containing input data.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public OutputTableHandle(TableWrapper resultSet, ParameterOption option)
+   {
+      super(resultSet, option);
+   }
+   
+   /**
+    * Determines if this parameter is INPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code false}.
+    */
+   @Override
+   public boolean isInputMode()
+   {
+      return false;
+   }
+
+   /**
+    * Determines if this parameter is OUTPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code true}.
+    */
+   @Override
+   public boolean isOutputMode()
+   {
+      return true;
+   }
+}

=== added file 'src/com/goldencode/p2j/util/OutputTableParameter.java'
--- old/src/com/goldencode/p2j/util/OutputTableParameter.java	1970-01-01 00:00:00 +0000
+++ new/src/com/goldencode/p2j/util/OutputTableParameter.java	2022-05-12 15:03:49 +0000
@@ -0,0 +1,168 @@
+/*
+** Module   : OutputTableParameter.java
+** Abstract : OUTPUT TABLE parameter support.
+**
+** Copyright (c) 2013-2021, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
+** 001 GES 20210430 Created initial version, by moving code from the parent class.
+*/
+
+/*
+** 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.persist.*;
+
+/**
+ * Implements the OUTPUT TABLE parameter type. This hard codes the input/output flags and delegates
+ * all other processing to the parent class.
+ */
+public class OutputTableParameter
+extends TableParameter
+{
+   /**
+    * Create an instance using a buffer.
+    *
+    * @param    table
+    *           Buffer of the wrapped table.
+    */
+   public OutputTableParameter(Temporary table)
+   {
+      super(table, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a buffer.
+    *
+    * @param    table
+    *           Buffer of the wrapped table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public OutputTableParameter(Temporary table, ParameterOption option)
+   {
+      super(table, option);
+   }
+   
+   /**
+    * Create an instance.
+    *
+    * @param    tableHandle
+    *           Handle to the wrapped table.
+    */
+   public OutputTableParameter(handle tableHandle)
+   {
+      super(tableHandle, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance.
+    *
+    * @param    tableHandle
+    *           Handle to the wrapped table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public OutputTableParameter(handle tableHandle, ParameterOption option)
+   {
+      super(tableHandle, option);
+   }
+   
+   /**
+    * Create an instance using a result set (for remote parameters).
+    *
+    * @param    resultSet
+    *           Result set containing input data.
+    */
+   public OutputTableParameter(TableWrapper resultSet)
+   {
+      super(resultSet, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a result set (for remote parameters).
+    *
+    * @param    resultSet
+    *           Result set containing input data.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public OutputTableParameter(TableWrapper resultSet, ParameterOption option)
+   {
+      super(resultSet, option);
+   }
+   
+   /**
+    * Determines if this parameter is INPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code false}.
+    */
+   @Override
+   public boolean isInputMode()
+   {
+      return false;
+   }
+
+   /**
+    * Determines if this parameter is OUTPUT or INPUT-OUTPUT.
+    *
+    * @return   Always {@code true}.
+    */
+   @Override
+   public boolean isOutputMode()
+   {
+      return true;
+   }
+}

=== modified file 'src/com/goldencode/p2j/util/ParameterOption.java'
--- old/src/com/goldencode/p2j/util/ParameterOption.java	2019-06-30 21:10:34 +0000
+++ new/src/com/goldencode/p2j/util/ParameterOption.java	2022-05-12 15:03:49 +0000
@@ -3,10 +3,11 @@
 ** Abstract : Declares the parameter options for passing complex data structures (TABLEs and
 **            DATASETs and their handles).
 **
-** Copyright (c) 2019, Golden Code Development Corporation.
+** Copyright (c) 2019-2021, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 OM  20190522 Created initial version.
+** 002 GES 20210430 Added APPEND member.
 */
 
 /*
@@ -66,8 +67,8 @@
 /**
  * This enum declares the parameter passing options for complex data structures (TABLE, DATASET,
  * TABLE-HANDLE and DATASET-HANDLE for both parameter declaration and parameter passing.
- * The enum is generated in both {@link com.goldencode.p2j.persist.DataSetParameter} and
- * {@link TableParameter} as optional parameter in constructor.
+ * The enum is used in both {@link com.goldencode.p2j.persist.DataSetParameter} and
+ * {@link TableParameter} as an optional parameter in the constructor.
  */
 public enum ParameterOption
 {
@@ -77,12 +78,15 @@
     */
    NONE,
    
+   /** Data is appended instead of being referenced or replaced. */
+   APPEND,
+   
    /** The structure is passed by reference (see {@code BY-REFERENCE} mode). */
    BY_REFERENCE,
    
-   /** The structure is binded (see {@code BIND} mode). */
+   /** The structure is bound (see {@code BIND} mode). */
    BIND,
    
-   /** The structure is passed by reference (see {@code BY-VALUE} mode). */
+   /** The structure is passed by value (see {@code BY-VALUE} mode). */
    BY_VALUE
 }

=== modified file 'src/com/goldencode/p2j/util/TableParameter.java'
--- old/src/com/goldencode/p2j/util/TableParameter.java	2021-11-25 13:43:19 +0000
+++ new/src/com/goldencode/p2j/util/TableParameter.java	2022-05-12 15:03:49 +0000
@@ -1,6 +1,6 @@
 /*
 ** Module   : TableParameter.java
-** Abstract : Container for table handle and calling-side APPEND option.
+** Abstract : Parameter support for TABLE and TABLE-HANDLE.
 **
 ** Copyright (c) 2013-2021, Golden Code Development Corporation.
 **
@@ -26,6 +26,7 @@
 **     SVL 20210517 Fixed isValid() for the case of a result set.
 **     CA  20211125 Fixed an issue when the TABLE-HANDLE handle is not unknown, but invalid - getTable() must
 **                  return null.
+**     GES 20210430 Reworked to enable subclasses which reduce parameter processing from common use cases.
 */
 
 /*
@@ -86,10 +87,16 @@
 import com.goldencode.p2j.persist.*;
 
 /**
- * Container for table handle and calling-side APPEND option. Serves two purposes: 1. passes
- * calling-side APPEND option to the called side; 2. implements interchangeability between TABLE
- * and TABLE-HANDLE parameters (In 4GL you can pass TABLE parameter to a procedure that takes
- * TABLE-HANDLE and vice versa).
+ * Represents a table or table-handle parameter, the mode (INPUT, INPUT-OUTPUT or OUTPUT) and any table
+ * parameter options (APPEND, BY-VALUE, BIND or BY-REFERENCE).  Allows subclasses to specialize this
+ * behavior.  A core feature is to implement interchangeability between TABLE and TABLE-HANDLE parameters
+ * (in the 4GL you can pass a TABLE parameter to a procedure that takes TABLE-HANDLE and vice versa).
+ * <p>
+ * The parameter mode can be implemented in this class or it can be overridden in a subclass.  If it is
+ * implemented here, then the input and output flags must be passed to the constructor.  If the subclass
+ * overrides the {@link #isInputMode} and {@link #isOutputMode} methods then the input and output boolean
+ * members will be ignored.  This is sub-optimal but it was done deliberately to enable the base class to
+ * be used as a full replacement for any of the subclasses.
  */
 public class TableParameter
 {
@@ -99,11 +106,8 @@
    /** Table handle. */
    private final handle tableHandle;
    
-   /** Corresponds calling-side APPEND option. */
-   private final boolean append;
-   
-   /** Parameter passing mode BY-VALUE, BIND or BY-REFERENCE option. */
-   private final ParameterOption mode;
+   /** Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE). */
+   private final ParameterOption option;
    
    /** Calling- or called-side result set (for remote parameters). */
    private TableWrapper resultSet = null;
@@ -111,6 +115,12 @@
    /** Determines if this is a remote (passed to appserver) parameter. */
    private boolean remoteParameter = false;
    
+   /** {@code true} if the parameter mode is INPUT or INPUT-OUTPUT. */
+   private boolean input = false;
+   
+   /** {@code true} if the parameter mode is OUTPUT or INPUT-OUTPUT. */
+   private boolean output = false;
+   
    /**
     * 1-based index of the table parameter (in declaration of the function). <code>0</code>
     * if it is not defined.
@@ -120,82 +130,88 @@
    /**
     * Create an instance using a buffer.
     *
-    * @param table
-    *        Buffer of the wrapped table.
+    * @param    table
+    *           Buffer of the wrapped table.
     */
    public TableParameter(Temporary table)
    {
-      this(table, false, ParameterOption.NONE);
-   }
-   
-   /**
-    * Create an instance using a buffer.
-    *
-    * @param table
-    *        Buffer of the wrapped table.
-    * @param append
-    *        Determines if APPEND option was specified on the calling side.
-    */
-   public TableParameter(Temporary table, boolean append)
-   {
-      this(table, append, ParameterOption.NONE);
-   }
-   
-   /**
-    * Create an instance using a buffer.
-    *
-    * @param   table
-    *          Buffer of the wrapped table.
-    * @param   append
-    *          Determines if APPEND option was specified on the calling side.
-    * @param   mode
-    *          Parameter passing mode BY-VALUE, BIND or BY-REFERENCE option.
-    */
-   public TableParameter(Temporary table, boolean append, ParameterOption mode)
-   {
-      this.table = (Temporary) ((BufferImpl) table).ref();
+      this(table, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a buffer.
+    *
+    * @param    table
+    *           Buffer of the wrapped table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public TableParameter(Temporary table, ParameterOption option)
+   {
+      this(table, option, false, false);
+   }
+   
+   /**
+    * Create an instance using a buffer.
+    *
+    * @param    table
+    *           Buffer of the wrapped table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    * @param    input
+    *           {@code true} if the parameter mode is INPUT or INPUT-OUTPUT.
+    * @param    output
+    *           {@code true} if the parameter mode is OUTPUT or INPUT-OUTPUT.
+    */
+   public TableParameter(Temporary table, ParameterOption option, boolean input, boolean output)
+   {
+      this.table       = (Temporary) ((BufferImpl) table).ref();
       this.tableHandle = ((BufferReference) table).buffer().tableHandle();
-      this.append = append;
-      this.mode = mode;
+      this.option      = option;
+      this.input       = input;
+      this.output      = output;
    }
    
    /**
     * Create an instance using a table handle.
     *
-    * @param tableHandle
-    *        Handle to the wrapped table.
+    * @param    tableHandle
+    *           Handle to the wrapped table.
     */
    public TableParameter(handle tableHandle)
    {
-      this(tableHandle, false, ParameterOption.NONE);
+      this(tableHandle, ParameterOption.NONE);
    }
    
    /**
     * Create an instance using a table handle.
     *
-    * @param tableHandle
-    *        Handle to the wrapped table.
-    * @param append
-    *        Determines if APPEND option was specified on the calling side.
+    * @param    tableHandle
+    *           Handle to the wrapped table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
     */
-   public TableParameter(handle tableHandle, boolean append)
+   public TableParameter(handle tableHandle, ParameterOption option)
    {
-      this(tableHandle, append, ParameterOption.NONE);
+      this(tableHandle, option, false, false);
    }
    
    /**
     * Create an instance using a table handle.
     *
-    * @param   tableHandle
-    *          Handle to the wrapped table.
-    * @param   append
-    *          Determines if APPEND option was specified on the calling side.
-    * @param   mode
-    *          Parameter passing mode BY-VALUE, BIND or BY-REFERENCE option.
+    * @param    tableHandle
+    *           Handle to the wrapped table.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    * @param    input
+    *           {@code true} if the parameter mode is INPUT or INPUT-OUTPUT.
+    * @param    output
+    *           {@code true} if the parameter mode is OUTPUT or INPUT-OUTPUT.
     */
-   public TableParameter(handle tableHandle, boolean append, ParameterOption mode)
+   public TableParameter(handle tableHandle, ParameterOption option, boolean input, boolean output)
    {
       WrappedResource res = tableHandle.getResource();
+      
       if (tableHandle._isValid())
       {
          if (res instanceof TempTable)
@@ -220,41 +236,56 @@
       }
       
       this.tableHandle = tableHandle;
-      this.append = append;
-      this.mode = mode;
-   }
-   
-   /**
-    * Create an instance using a result set (for remote parameters).
-    *
-    * @param resultSet
-    *        Result set containing input data.
-    * @param append
-    *        Determines if APPEND option was specified on the calling side.
-    */
-   public TableParameter(TableWrapper resultSet, boolean append)
-   {
-      this(resultSet, append, ParameterOption.NONE);
-   }
-   
-   /**
-    * Create an instance using a result set (for remote parameters).
-    *
-    * @param   resultSet
-    *          Result set containing input data.
-    * @param   append
-    *          Determines if APPEND option was specified on the calling side.
-    * @param   mode
-    *          Parameter passing mode BY-VALUE, BIND or BY-REFERENCE option.
-    */
-   public TableParameter(TableWrapper resultSet, boolean append, ParameterOption mode)
-   {
-      this.table = null;
-      this.tableHandle = new handle();
-      this.resultSet = resultSet;
-      this.append = append;
-      this.mode = mode;
+      this.option      = option;
+      this.input       = false;
+      this.output      = false;
+   }
+   
+   /**
+    * Create an instance using a result set (for remote parameters).
+    *
+    * @param    resultSet
+    *           Result set containing input data.
+    */
+   public TableParameter(TableWrapper resultSet)
+   {
+      this(resultSet, ParameterOption.NONE);
+   }
+   
+   /**
+    * Create an instance using a result set (for remote parameters).
+    *
+    * @param    resultSet
+    *           Result set containing input data.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    */
+   public TableParameter(TableWrapper resultSet, ParameterOption option)
+   {
+      this(resultSet, option, false, false);
+   }
+   
+   /**
+    * Create an instance using a result set (for remote parameters).
+    *
+    * @param    resultSet
+    *           Result set containing input data.
+    * @param    option
+    *           Table parameter option (APPEND, BY-VALUE, BIND or BY-REFERENCE).
+    * @param    input
+    *           {@code true} if the parameter mode is INPUT or INPUT-OUTPUT.
+    * @param    output
+    *           {@code true} if the parameter mode is OUTPUT or INPUT-OUTPUT.
+    */
+   public TableParameter(TableWrapper resultSet, ParameterOption option, boolean input, boolean output)
+   {
+      this.table           = null;
+      this.tableHandle     = new handle();
+      this.resultSet       = resultSet;
+      this.option          = option;
       this.remoteParameter = true;
+      this.input           = input;
+      this.output          = output;
       
       // if INPUT, create a dynamic table
       if (resultSet.isInput() && !resultSet.isFromJson() && resultSet.isAfterTable())
@@ -276,10 +307,52 @@
    }
    
    /**
+    * Determines if this parameter is INPUT or INPUT-OUTPUT.
+    *
+    * @return   {@code true} if this is INPUT or INPUT-OUTPUT.
+    */
+   public boolean isInputMode()
+   {
+      return input;
+   }
+
+   /**
+    * Set the flag for whether this parameter is INPUT or INPUT-OUTPUT.
+    *
+    * @param    input
+    *           {@code true} if this is INPUT or INPUT-OUTPUT.
+    */
+   public void setInputMode(boolean input)
+   {
+      this.input = input;
+   }
+
+   /**
+    * Determines if this parameter is OUTPUT or INPUT-OUTPUT.
+    *
+    * @return   {@code true} if this is OUTPUT or INPUT-OUTPUT.
+    */
+   public boolean isOutputMode()
+   {
+      return output;
+   }
+   
+   /**
+    * Set the flag for whether this parameter is OUTPUT or INPUT-OUTPUT.
+    *
+    * @param    output
+    *           {@code true} if this is OUTPUT or INPUT-OUTPUT.
+    */
+   public void setOutputMode(boolean output)
+   {
+      this.output = output;
+   }
+
+   /**
     * Determines if this is a remote parameter.
     *
-    * @return <code>true</code> if this is a remote parameter, <code>false</code> if this is a
-    *         local parameter.
+    * @return   {@code true} if this is a remote parameter, {@code false} if this is a
+    *           local parameter.
     */
    public boolean isRemoteParameter()
    {
@@ -287,9 +360,9 @@
    }
 
    /**
-    * Get result set (for remote parameters).
+    * Get the result set (for remote parameters).
     *
-    * @return Result set.
+    * @return   Result set.
     */
    public TableWrapper getResultSet()
    {
@@ -297,10 +370,10 @@
    }
 
    /**
-    * Set result set (for remote parameters).
+    * Set the result set (for remote parameters).
     *
-    * @param resultSet
-    *        Result set, normally containing output data.
+    * @param    resultSet
+    *           Result set, normally containing output data.
     */
    public void setResultSet(TableWrapper resultSet)
    {
@@ -308,9 +381,9 @@
    }
 
    /**
-    * Get the handle which references the wrapped table.
+    * Get the table-handle which references the wrapped table.
     *
-    * @return  handle to the wrapped table.
+    * @return   The table-handle (contains the wrapped table).
     */
    public handle getTableHandle()
    {
@@ -331,7 +404,7 @@
    /**
     * Get the buffer of the wrapped table.
     *
-    * @return  buffer of the wrapped table.
+    * @return   buffer of the wrapped table.
     */
    public Temporary getTable()
    {
@@ -356,57 +429,57 @@
    /**
     * Determines if the APPEND option was specified on the calling side.
     *
-    * @return <code>true</code> if the APPEND option was specified on the calling side.
+    * @return    {@code true} if the APPEND option was specified on the calling side.
     */
    public boolean isAppend()
    {
-      return append;
+      return option == ParameterOption.APPEND;
    }
    
    /**
     * Determines if the {@code BY-VALUE} option was specified on the calling side.
     *
-    * @return  {@code true} if the {@code BY-VALUE} option was specified on the calling side.
+    * @return   {@code true} if the {@code BY-VALUE} option was specified on the calling side.
     */
    public boolean isByValue()
    {
-      return mode == ParameterOption.BY_VALUE;
+      return option == ParameterOption.BY_VALUE;
    }
    
    /**
     * Determines if the {@code BIND} option was specified on the calling side.
     *
-    * @return  {@code true} if the {@code BIND} option was specified on the calling side.
+    * @return   {@code true} if the {@code BIND} option was specified on the calling side.
     */
    public boolean isBind()
    {
-      return mode == ParameterOption.BIND;
+      return option == ParameterOption.BIND;
    }
    
    /**
     * Determines if the {@code BY-REFERENCE} option was specified on the calling side.
     *
-    * @return  {@code true} if the {@code BY-REFERENCE} option was specified on the calling side.
+    * @return   {@code true} if the {@code BY-REFERENCE} option was specified on the calling side.
     */
    public boolean isByReference()
    {
-      return mode == ParameterOption.BY_REFERENCE;
+      return option == ParameterOption.BY_REFERENCE;
    }
    
    /**
-    * Get the parameter passing mode for this argument.
+    * Get the table parameter option for this argument.
     * 
-    * @return   The parameter passing {@link #mode}.
+    * @return   The table parameter option.
     */
-   public ParameterOption getParameterMode()
+   public ParameterOption getParameterOption()
    {
-      return mode;
+      return option;
    }
    
    /**
     * Get 1-based index of the table parameter (in declaration of the function).
     *
-    * @return 1-based index of the table parameter or <code>0</code> if it is not defined.
+    * @return    1-based index of the table parameter or <code>0</code> if it is not defined.
     */
    public int getParameterIndex()
    {
@@ -416,8 +489,8 @@
    /**
     * Set 1-based index of the table parameter (in declaration of the function).
     *
-    * @param parameterIndex
-    *        1-based index of the table parameter.
+    * @param    parameterIndex
+    *           1-based index of the table parameter.
     */
    public void setParameterIndex(int parameterIndex)
    {

