Project

General

Profile

4350_fuzzy_matching_changes_20210415.patch

Greg Shah, 04/15/2021 10:32 AM

Download (124 KB)

View differences:

new/src/com/goldencode/p2j/schema/SchemaDictionary.java 2021-04-13 20:30:32 +0000
499 499
**    CA   20201011          Scope.promoted changed to an identity hash set.
500 500
**    CA   20201015          Replaced java.util.Stack with a non-synchronized custom implementation.
501 501
**     AIL 20210309          Clean local schema dictionary instances. 
502
**    GES  20210208          Added table-level signature calculation (saved as an annotation). Added
503
**                           findTableInfo() to provide a set of related data about a matched record. This
504
**                           saves multiple calls each having to resolve the same name node lookup.
502 505
*/
503 506

  
504 507
/*
......
1765 1768
      {
1766 1769
         copyTableProperties(ast, table);
1767 1770
      }
1771
      
1772
      // calculate and save the table signature
1773
      calculateSignature(table);
1768 1774

  
1769 1775
      if (modelTable == null)
1770 1776
      {
......
1916 1922
   }
1917 1923
   
1918 1924
   /**
1925
    * Get the Progress parser token type of a table, temp-table, work-table,
1926
    * or buffer. If <code>name</code> represents a buffer, the type of the
1927
    * backing record is returned. That is, a type of <code>BUFFER</code> is
1928
    * never returned, only the type of the record which backs the buffer.
1929
    * <p>
1930
    * If both forceTemp and forcePersistent are <code>false</code>, then the lookup is performed
1931
    * with no preference for temp-table or persistent table.
1932
    *
1933
    * @param   name
1934
    *          Case insensitive record name; may be qualified or not. If
1935
    *          qualified, only the table portion of the name may be
1936
    *          abbreviated.
1937
    * @param   forceTemp
1938
    *          If <code>true</code>, force a temp-table lookup.
1939
    * @param   forcePersistent
1940
    *          If <code>true</code>, force a persistent table lookup.
1941
    *
1942
    * @return  One of the Progress parser token types <code>TABLE</code>,
1943
    *          <code>TEMP_TABLE</code>, or <code>WORK_TABLE</code>, or
1944
    *          <code>UNKNOWN</code> if <code>name</code> cannot be found.
1945
    *
1946
    * @throws  AmbiguousSchemaNameException
1947
    *          if <code>name</code> is an ambiguous identifier for the
1948
    *          target record.
1949
    */
1950
   public TableInfo findTableInfo(String name, boolean forceTemp, boolean forcePersistent)
1951
   throws AmbiguousSchemaNameException
1952
   {
1953
      // TODO: The recordType lookup used scopes.size() - 1 instead of nearestEnclosingInternal(), is this
1954
      //       an issue?  It seems like it should have been consistent with the other lookups, so this is
1955
      //       being made consistent here.
1956
      int l1 = computeLevel(forceTemp, forcePersistent, true, nearestEnclosingInternal());
1957
      int l2 = computeLevel(forceTemp, forcePersistent, false, SCHEMA_GLOBAL_SCOPE);
1958

  
1959
      // Look up record name node.
1960
      NameNode node = findEntry(name, EntityName.TABLE, l1, l2, false);
1961
      
1962
      TableInfo info = null;
1963
      
1964
      if (node != null)
1965
      {
1966
         NameNode dbNode  = node.getParent();
1967
         
1968
         // don't just use getAst() here, we need the AST for the backing table if this is a buffer
1969
         // (this will work for both the simple case and the buffer case)
1970
         Aast   ast   = getTable(node); 
1971
         String sig   = ast != null ? (String) ast.getAnnotation("signature") : null;
1972
         String sname = getQualifiedName(node);
1973
         String bname = getRecordName(node, EntityName.TABLE, name);
1974
         String dname = (dbNode != null) ? dbNode.getName() : "";
1975
         int    ttype = getTable(node).getType();
1976
         
1977
         info = new TableInfo(sig, sname, bname, dname, ttype);
1978
      }
1979
      
1980
      return info;
1981
   }
1982
   
1983
   /**
1919 1984
    * Get an object which exposes schema-related information about a field which is referenced in
1920 1985
    * source code.
1921 1986
    * 
......
4908 4973
   }
4909 4974
   
4910 4975
   /**
4976
    * Calculate and save the signature for the table.  Each field is represented by "datatype" or
4977
    * "datatype[extent]" format.  The table level signature is all of the field signatures separated
4978
    * by "|" delimiter with the number and order of the fields matching the order in the AST.
4979
    *  
4980
    * @param    table
4981
    *           The table AST substree.
4982
    */
4983
   private void calculateSignature(Aast table)
4984
   {
4985
      String signature = "";
4986
      
4987
      Aast next = (Aast) table.getFirstChild();
4988

  
4989
      // iterate through all fields
4990
      while (next != null)
4991
      {
4992
         int type = next.getType();
4993
         
4994
         // if this is a field, calculate the field's portion of the signature
4995
         if (type >= BEGIN_FIELDTYPES && type <= END_FIELDTYPES ||
4996
             type >= BEGIN_METATYPES  && type <= END_METATYPES)
4997
         {
4998
            Aast ext = next.getImmediateChild(KW_EXTENT, null);
4999
            
5000
            String extent = null;
5001
            
5002
            // read the extent value if any
5003
            if (ext != null)
5004
            {
5005
               if (ext.getNumImmediateChildren() == 0)
5006
               {
5007
                  String err = String.format("Indeterminate EXTENT in a temp-table definition for %s",
5008
                                             next.dumpTree());
5009
                  
5010
                  // indeterminate extent does not make sense for a temp-table, should not happen
5011
                  throw new RuntimeException(err);
5012
               }
5013
               else
5014
               {
5015
                  // the only child is a NUM_LITERAL
5016
                  Aast num = (Aast) ext.getFirstChild();
5017
                  extent = num.getText();
5018
               }
5019
            }
5020
            
5021
            // create the field portion of the signature
5022
            String fsig = SchemaParser.fieldSignature(type, extent);
5023
            
5024
            // add it in to the table signature
5025
            signature += (signature.length() == 0) ? fsig : "|" + fsig;
5026
         }
5027

  
5028
         next = (Aast) next.getNextSibling();
5029
      }
5030

  
5031
      // save the result
5032
      if (signature.isEmpty())
5033
      {
5034
         // there should always be at least 1 field
5035
         throw new RuntimeException(String.format("Invalid temp-table, no fields!\n%s", table.dumpTree()));
5036
      }
5037
      else
5038
      {
5039
         table.putAnnotation("signature", signature);
5040
      }
5041
   }
5042
   
5043
   /**
4911 5044
    * Copy TABLE properties from a source AST to a destination AST. The set
4912 5045
    * of properties (essentially child AST nodes) copied will be limited to
4913 5046
    * those that are valid in a table (filtered by token type). It is assumed
new/src/com/goldencode/p2j/schema/TableInfo.java 2021-04-09 17:19:41 +0000
1
/*
2
** Module   : TableInfo.java
3
** Abstract : Schema information about a database field.
4
**
5
** Copyright (c) 2021, Golden Code Development Corporation.
6
**
7
** -#- -I- --Date-- --------------------------------Description----------------------------------
8
** 001 GES 20210224 Created initial version.
9
*/
10

  
11
/*
12
** This program is free software: you can redistribute it and/or modify
13
** it under the terms of the GNU Affero General Public License as
14
** published by the Free Software Foundation, either version 3 of the
15
** License, or (at your option) any later version.
16
**
17
** This program is distributed in the hope that it will be useful,
18
** but WITHOUT ANY WARRANTY; without even the implied warranty of
19
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
** GNU Affero General Public License for more details.
21
**
22
** You may find a copy of the GNU Affero GPL version 3 at the following
23
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
24
** 
25
** Additional terms under GNU Affero GPL version 3 section 7:
26
** 
27
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
28
**   terms apply to the works covered under the License.  These additional terms
29
**   are non-permissive additional terms allowed under Section 7 of the GNU
30
**   Affero GPL version 3 and may not be removed by you.
31
** 
32
**   0. Attribution Requirement.
33
** 
34
**     You must preserve all legal notices or author attributions in the covered
35
**     work or Appropriate Legal Notices displayed by works containing the covered
36
**     work.  You may not remove from the covered work any author or developer
37
**     credit already included within the covered work.
38
** 
39
**   1. No License To Use Trademarks.
40
** 
41
**     This license does not grant any license or rights to use the trademarks
42
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
43
**     of Golden Code Development Corporation. You are not authorized to use the
44
**     name Golden Code, FWD, or the names of any author or contributor, for
45
**     publicity purposes without written authorization.
46
** 
47
**   2. No Misrepresentation of Affiliation.
48
** 
49
**     You may not represent yourself as Golden Code Development Corporation or FWD.
50
** 
51
**     You may not represent yourself for publicity purposes as associated with
52
**     Golden Code Development Corporation, FWD, or any author or contributor to
53
**     the covered work, without written authorization.
54
** 
55
**   3. No Misrepresentation of Source or Origin.
56
** 
57
**     You may not represent the covered work as solely your work.  All modified
58
**     versions of the covered work must be marked in a reasonable way to make it
59
**     clear that the modified work is not originating from Golden Code Development
60
**     Corporation or FWD.  All modified versions must contain the notices of
61
**     attribution required in this license.
62
*/
63

  
64
package com.goldencode.p2j.schema;
65

  
66
/**
67
 * A read-only bean which exposes schema-related information associated with a database table
68
 * which is referenced in source code.
69
 */
70
public class TableInfo
71
{
72
   /** Table signature associated with the table. */
73
   private final String signature;
74
   
75
   /** Fully qualified schema name of the table. */
76
   private final String qualified;
77
   
78
   /** Table's buffer name as indicated in source. */
79
   private final String buffer;
80
   
81
   /** Table's database logical name or alias. */
82
   private final String database;
83
   
84
   /** Table's token type. */
85
   private final int recordType;
86
   
87
   /**
88
    * Constructor.
89
    * 
90
    * @param   signature
91
    *          Structural signature for the table.
92
    * @param   qualified
93
    *          Fully qualified schema name of the table.
94
    * @param   buffer
95
    *          Name of the buffer indicated by the table reference in source code.
96
    * @param   database
97
    *          Name of the database which contains the table, as specified by the table reference
98
    *          in source code, or looked up if the source reference was unqualified.
99
    * @param   recordType
100
    *          Token type of the table backing the buffer indicated by the table reference in
101
    *          source code.
102
    */
103
   TableInfo(String signature, String qualified, String buffer, String database, int recordType)
104
   {
105
      this.signature  = signature;
106
      this.qualified  = qualified;
107
      this.buffer     = buffer;
108
      this.database   = database;
109
      this.recordType = recordType;
110
   }
111
   
112
   /**
113
    * Get the signature of the table in the schema.
114
    * 
115
    * @return   Table signature.
116
    */
117
   public String getSignature()
118
   {
119
      return signature;
120
   }
121
   
122
   /**
123
    * Get the fully qualified schema name of the field.
124
    * 
125
    * @return  Qualified field name.
126
    */
127
   public String getQualified()
128
   {
129
      return qualified;
130
   }
131
   
132
   /**
133
    * Get the buffer name associated with the field.
134
    * 
135
    * @return  Buffer name.
136
    */
137
   public String getBuffer()
138
   {
139
      return buffer;
140
   }
141
   
142
   /**
143
    * Get the logical database name or alias, if any, associated with the field.
144
    * 
145
    * @return  The logical name of the database associated with the buffer associated with the
146
    *          field. An empty string is returned if the containing table is a temp- or
147
    *          work-table.
148
    */
149
   public String getDatabase()
150
   {
151
      return database;
152
   }
153
   
154
   /**
155
    * Get the Progress parser token type of a table, temp-table, work-table, or buffer which
156
    * contains the field. If the record containing the field is a buffer, the type of the backing
157
    * record is returned. That is, a type of {@code BUFFER} is never returned, only the type of
158
    * the record which backs the buffer.
159
    * 
160
    * @return  Record token type.
161
    */
162
   public int getRecordType()
163
   {
164
      return recordType;
165
   }
166
}
new/src/com/goldencode/p2j/schema/schema.g 2021-02-08 21:46:29 +0000
2 2
** Module   : schema.g
3 3
** Abstract : Progress database schema dump file (.df) parser
4 4
**
5
** Copyright (c) 2004-2019, Golden Code Development Corporation.
5
** Copyright (c) 2004-2021, Golden Code Development Corporation.
6 6
**
7 7
** -#- -I- --Date-- --JPRM-- ----------------------------Description-----------------------------
8 8
** 001 ECF 20041130   @18769 First version implementing a complete lexer,
......
99 99
** 037 OM  20200530          Fix for datetime[tz].
100 100
** 038 CA  20200930          Use factory.setASTNodeClass(Class) instead of setASTNodeClass(String), to avoid
101 101
**                           loadClass call.
102
**     GES 20210208          Added table-level signature calculation and it is saved as an annotation.
102 103
*/
103 104

  
104 105
/*
......
161 162
**            SchemaParserTokenTypes.java
162 163
** Abstract : Progress database schema dump file (.df) parser
163 164
**
164
** Copyright (c) 2004-2016, Golden Code Development Corporation.
165
** Copyright (c) 2004-2021, Golden Code Development Corporation.
165 166
**
166 167
** -#- -I- --Date-- --JPRM-- ----------------------------Description-----------------------------
167 168
** 001 ECF 20041130   @18769 WARNING, THIS IS A GENERATED FILE!!! DO NOT EDIT THIS FILE. The
......
241 242
   }
242 243
   
243 244
   /**
245
    * Calculate the signature string for a field.
246
    *
247
    * @param    type
248
    *           The field's data type.
249
    * @param    extent
250
    *           The extent size (as a num literal) or {@code null} if the field is scalar.
251
    *
252
    * @return   The signature in "datatype" or "datatype[extent]" format.
253
    */
254
   public static String fieldSignature(int type, String extent)
255
   {
256
      String code = null;
257
      
258
      switch (type)
259
      {
260
         case FIELD_BLOB:
261
            code = "blob";
262
            break;
263
         case FIELD_CHAR:
264
            code = "char";
265
            break;
266
         case FIELD_CLOB:
267
            code = "clob";
268
            break;
269
         case FIELD_COM_HANDLE:
270
            code = "comh";
271
            break;
272
         case FIELD_DATE:
273
            code = "date";
274
            break;
275
         case FIELD_DATETIME:
276
            code = "dt";
277
            break;
278
         case FIELD_DATETIME_TZ:
279
            code = "dttz";
280
            break;
281
         case FIELD_DEC:
282
            code = "dec";
283
            break;
284
         case FIELD_INT:
285
            code = "int";
286
            break;
287
         case FIELD_INT64:
288
            code = "i64";
289
            break;
290
         case FIELD_LOGICAL:
291
            code = "log";
292
            break;
293
         case FIELD_RECID:
294
            code = "rec";
295
            break;
296
         case FIELD_ROWID:
297
            code = "row";
298
            break;
299
         case FIELD_RAW:
300
            code = "raw";
301
            break;
302
         case FIELD_HANDLE:
303
            code = "hndl";
304
            break;
305
         case FIELD_BIGINT:
306
            code = "bigi";
307
            break;
308
         case FIELD_BYTE:
309
            code = "byte";
310
            break;
311
         case FIELD_DOUBLE:
312
            code = "dbl";
313
            break;
314
         case FIELD_FIXCHAR:
315
            code = "fixc";
316
            break;
317
         case FIELD_FLOAT:
318
            code = "flt";
319
            break;
320
         case FIELD_SHORT:
321
            code = "shrt";
322
            break;
323
         case FIELD_TIMESTAMP:
324
            code = "tmst";
325
            break;
326
         case FIELD_TIME:
327
            code = "time";
328
            break;
329
         default:
330
            code = "unkw";
331
            break;
332
      }
333
      
334
      return (extent == null) ? code : code + "[" + extent + "]";
335
   }
336
   
337
   /**
244 338
    * Configure the hidden filter to ensure that token types that are not
245 339
    * normally recognized by the parser, are hidden (but still accessible)
246 340
    * using the filter.
......
700 794
 */
701 795
table
702 796
   :
703
      { astFactory.makeASTRoot(currentAST, #[TABLE]); }
797
      {
798
         astFactory.makeASTRoot(currentAST, #[TABLE]);
799
         String signature = "";
800
         String fsig = null;
801
      }
704 802
      (
705 803
         KW_ADD! tab:KW_TABLE! s:STRING! (y:KW_TYPE! d:SYMBOL!)?
706 804
         {
......
723 821
         (
724 822
            options { generateAmbigWarnings = false; }
725 823
            :
726
              field
824
              fsig=field
825
              {
826
                 signature += (signature.length() == 0) ? fsig : "|" + fsig;
827
              }
727 828
            | index
728 829
         )*
729 830
      )
730 831
      {
731 832
         currentTable = null;
732 833
         forceLineColumnNums(#tab, ##);
834
         ##.putAnnotation("signature", signature);
733 835
      }
734 836
   ;
735 837
   
......
804 906
 * definition would always be a child of the most recently parsed table
805 907
 * definition.
806 908
 *
807
 * @throws  SemanticException
808
 *          if this field definition reports a different parent table name
809
 *          from the table definition most recently parsed.
909
 * @return   The signature for this field in "datatype" or "datatype[extent]" format.
910
 *
911
 * @throws   SemanticException
912
 *           if this field definition reports a different parent table name
913
 *           from the table definition most recently parsed.
810 914
 */
811
field
915
field returns [String sig = null]
812 916
   :
813 917
      {
814 918
         int    ftype;
815 919
         String fmt = null;
920
         String ext = null;
816 921
      }
817 922
      (
818 923
         KW_ADD! f:KW_FIELD^ s:STRING!
......
850 955
            | field_trigger
851 956
            | help
852 957
            | help_sa
853
            | extent
958
            | ext=extent
854 959
            | decimals
855 960
            | length
856 961
            | viewAs
......
876 981
            | unknownOption!
877 982
         )*
878 983
      )
984
      {
985
         sig = fieldSignature(ftype, ext);
986
      }
879 987
   ;
880 988
   
881 989
/**
......
1736 1844
 * <p>
1737 1845
 * The resulting sub-tree is the <code>KW_EXTENT</code> with a child of
1738 1846
 * <code>NUM_LITERAL</code>.
1847
 *
1848
 * @return   The size of the extent.
1739 1849
 */
1740
extent
1850
extent returns [String num = null]
1741 1851
   :
1742
      KW_EXTENT^ NUM_LITERAL
1852
      KW_EXTENT^ n:NUM_LITERAL { num = #n.getText(); }
1743 1853
   ;
1744 1854
   
1745 1855
/**
new/src/com/goldencode/p2j/uast/ClassDefinition.java 2021-04-15 14:18:30 +0000
1 1
/*
2
** Module   : ClassDefinition.java
3 2
** Abstract : defines the 4GL API for a class or interface definition
4 3
**
5 4
** Copyright (c) 2007-2021, Golden Code Development Corporation.
......
100 99
**     CA  20210305          For builtin OO method calls, save at the call the typelist for the target method
101 100
**                           parameters.
102 101
**     CA  20210318          Fixed conversion of extent() function/statement with builtin OO properties.
102
**     GES 20210311          Rewrote exact and fuzzy method matching to support all data types and
103
**                           overloading rules.  Fixed many latent problems.
103 104
*/
104 105

  
105 106
/*
......
160 161
import java.lang.reflect.*;
161 162
import java.util.*;
162 163
import java.util.function.*;
164
import java.util.stream.*;
163 165
import com.goldencode.ast.*;
164 166
import com.goldencode.p2j.convert.*;
165 167
import com.goldencode.p2j.convert.db.*;
......
189 191
public class ClassDefinition
190 192
implements ProgressParserTokenTypes
191 193
{
192
   /**
193
    * Flag to indicate whether tracing is ON.
194
    */
195
   public static final boolean nowrapObjects = System.getProperty("nowrap.objects") != null;
196

  
197 194
   /** Data store identifier. */
198 195
   protected static enum DataStoreType { VAR, METHOD, TABLE, QRY, DATASET, DATASRC };
199 196

  
......
650 647
      this.interfaces = interfaces.isEmpty() ? null : Collections.unmodifiableSet(interfaces);
651 648
      this.parents = parents.isEmpty() 
652 649
                        ? null 
653
                        : (ClassDefinition[]) parents.toArray(new ClassDefinition[0]);
650
                        : parents.toArray(new ClassDefinition[0]);
654 651
   }
655 652
   
656 653
   /**
......
1413 1410
            {
1414 1411
               // this can happen if we are referencing this class via a USING from an USING defined
1415 1412
               // in this file... in this case, inherit the tempidx, but show an warning.
1416
               MemberData dat = defs.get(new SignatureKey(sig));
1417 1413
               
1418 1414
               // GES_TODO: re-enable this
1419 1415
               // System.out.printf("\nERROR: duplicate method definition!\n");
1420 1416
               // System.out.printf("EXISTING %s\n", dat.toString());
1421
               // System.out.printf("DUPLICATE %s\n at %s", mdat.toString(), node.dumpTree());
1417
               // System.out.printf("DUPLICATE %s\n at %s", prevMdat.toString(), node.dumpTree());
1422 1418
               
1423 1419
               // return;
1424 1420
            }
......
1461 1457
      if (node != null)
1462 1458
      {
1463 1459
         node.putAnnotation("name", name);
1464
         node.putAnnotation("return_type", new Long(type));
1460
         node.putAnnotation("return_type", (long) type);
1465 1461
         node.putAnnotation("signature", renderSignature(name, sig)); 
1466
         node.putAnnotation("access-mode", new Long(access));
1467
         node.putAnnotation("static", new Boolean(isStatic));
1462
         node.putAnnotation("access-mode", (long) access);
1463
         node.putAnnotation("static", isStatic);
1468 1464
         node.putAnnotation("oo-data-store", DataStoreType.METHOD.toString());
1469
         node.putAnnotation("tempidx", new Long(mTempIdx));
1465
         node.putAnnotation("tempidx", (long) mTempIdx);
1470 1466
         node.putAnnotation("tempidx-file", this.filename);
1471 1467
         
1472 1468
         if (extent != 0)
1473 1469
         {
1474
            node.putAnnotation("extent", new Long(extent));
1470
            node.putAnnotation("extent", (long) extent);
1475 1471
         }
1476 1472
         
1477 1473
         if (ret != null && ret.isAnnotation("qualified"))
......
1570 1566
      if (node != null)
1571 1567
      {
1572 1568
         // go ahead and annotate the chp_wrapper right now too
1573
         MemberData mdat = lookupMethodWorker(mname, signature, access, isStatic, internal, node);
1569
         MethodSearchResult found = lookupMethodWorker(mname, signature, access, isStatic, internal, node);
1570
         MemberData         mdat    = (found == null) ?  null : found.data;
1574 1571
         
1575 1572
         if (mdat != null)
1576 1573
         {
......
1593 1590
            }
1594 1591
            
1595 1592
            // the access mode won't necessarily be the same as that passed in
1596
            node.putAnnotation("access-mode", new Long(mdat.access));
1597
            node.putAnnotation("static", new Boolean(mdat.isStatic));
1593
            node.putAnnotation("access-mode", (long) mdat.access);
1594
            node.putAnnotation("static", mdat.isStatic);
1598 1595
            if (mdat.modes != null && !mdat.modes.isEmpty())
1599 1596
            {
1600 1597
               node.putAnnotation("param_modes", mdat.modes);
1601 1598
            }
1602 1599
            
1603
            node.putAnnotation("tempidx", new Long(mdat.tempIdx));
1600
            node.putAnnotation("tempidx", (long) mdat.tempIdx);
1604 1601
            node.putAnnotation("tempidx-file", mdat.container.filename);
1605 1602
            
1606 1603
            node.putAnnotation("found-in-cls", mdat.container.name);
......
1612 1609
            
1613 1610
            if (mdat.extent != 0)
1614 1611
            {
1615
               node.putAnnotation("extent", new Long(mdat.extent));
1612
               node.putAnnotation("extent", (long) mdat.extent);
1616 1613
            }
1617 1614
            
1618 1615
            if (mdat.container.builtin && !mdat.container.dotnet)
......
1625 1622
               }
1626 1623
            }
1627 1624
            
1628
            annotateCallSignature(node, signature, mdat.signature);
1625
            annotateCallSignature(node, signature, mdat.signature, found.getOverrides());
1629 1626
            node.putAnnotation("signature", renderSignature(mdat.name, mdat.signature));
1630 1627
            if (isBuiltIn())
1631 1628
            {
......
1658 1655
         }
1659 1656
         else
1660 1657
         {
1661
            // check if there are POLY arguments (BaseDataType signature); if so, mark this call
1662
            // with 'dynamic', so conversion rules can emit a dynamic call for it
1663
            boolean dynamic = false;
1664
            for (ParameterKey key : signature)
1665
            {
1666
               if ("BaseDataType".equals(key.type))
1667
               {
1668
                  dynamic = true;
1669
                  break;
1670
               }
1671
            }
1672
            
1673
            if (dynamic)
1658
            // dynamic cases will have a non-null result; this may be for table-handle parms, dataset-handle
1659
            // parms or POLY parms
1660
            if (found != null && found.isDynamic())
1674 1661
            {
1675 1662
               node.putAnnotation("dynamic", true);
1676 1663
               if (isStatic)
......
2426 2413
            {
2427 2414
               if (vdata.var.isProp())
2428 2415
               {
2429
                  String datatype = SymbolResolver.translateType(vdata.type, null);
2430
                  if ("object".equals(datatype))
2431
                  {
2432
                     datatype = String.format("object <? extends %s>", vdata.qname);
2433
                  }
2434
                  int extent = vdata.var.getExtent();
2435
                  String ext = extent == 0 
2436
                                 ? "" 
2437
                                 : "[" + (extent > 0 ? Integer.toString(extent) : "") +"]";
2416
                  String datatype = SymbolResolver.translateType(vdata.type, null, vdata.qname);
2417
                  
2418
                  
2419
                  int    extent = vdata.var.getExtent();
2420
                  String ext = (extent == 0) ? "" : "[" + (extent > 0 ? Integer.toString(extent) : "") +"]";
2438 2421

  
2439 2422
                  // add property methods
2440 2423
                  String[] prefixes = extent != 0 ? extPrefixes : propPrefixes;
......
2684 2667
    *           The OO_METH_* node whose parameters will be annotated.
2685 2668
    * @param    callSig
2686 2669
    *           The array of parameter types that is the method definition signature.
2670
    * @param    defSig
2671
    *           The array of parameter types that is the method definition signature.
2672
    * @param    overrides
2673
    *           Type overrides found by any fuzzy matching for this method.
2687 2674
    */
2688
   private void annotateCallSignature(Aast call, ParameterKey[] callSig, ParameterKey[] defSig)
2675
   private void annotateCallSignature(Aast           call,
2676
                                      ParameterKey[] callSig,
2677
                                      ParameterKey[] defSig,
2678
                                      String[]       overrides)
2689 2679
   {
2690 2680
      int idx = 0;
2691 2681
      
......
2884 2874
                     {
2885 2875
                        if (parm.getType() == PARAMETER)
2886 2876
                        {
2887
                           parm.putAnnotation("wrap_parameter", new Boolean(true));
2877
                           parm.putAnnotation("wrap_parameter", true);
2888 2878
                        }
2889 2879
                     }
2890
                     else if ((!nowrapObjects || !classname.startsWith("object<")) && 
2880
                     else if (!classname.startsWith("object<") && 
2891 2881
                              !classname.startsWith("jobject<"))
2892 2882
                     {
2893
                        // TODO: CA: what's the reas for nowrapObjects?
2894
                        parm.putAnnotation("wrap_parameter", Boolean.TRUE);
2883
                        parm.putAnnotation("wrap_parameter", true);
2895 2884
                     }
2896 2885
                     
2897 2886
                     parm.putAnnotation("classname", classname);
......
2977 2966
            {
2978 2967
               Iterator<MemberData> iter = defs.values().iterator();
2979 2968
               
2980
               outer:
2981 2969
               while (iter.hasNext())
2982 2970
               {
2983 2971
                  MemberData dat = checkAccessRights(iter.next(), access);
......
3047 3035
    * @return   Member data or <code>null</code> if no such variable or data
3048 3036
    *           member exists. 
3049 3037
    */
3050
   private MemberData lookupMethodWorker(String         name,
3051
                                         ParameterKey[] sig,
3052
                                         int            access,
3053
                                         boolean        isStatic,
3054
                                         boolean        internal,
3055
                                         Aast           node)
3038
   private MethodSearchResult lookupMethodWorker(String         name,
3039
                                                 ParameterKey[] sig,
3040
                                                 int            access,
3041
                                                 boolean        isStatic,
3042
                                                 boolean        internal,
3043
                                                 Aast           node)
3056 3044
   {
3057
      MemberData mdat = null;
3045
      MethodSearchResult result = null;
3058 3046
      
3059 3047
      synchronized (lock)
3060 3048
      {
3061
         mdat = exactMethodLookup(name, sig, access, isStatic, internal);
3049
         result = exactMethodLookup(name, sig, access, isStatic, internal);
3062 3050

  
3063
         if (mdat == null && !isStatic && internal)
3051
         if (result == null && !isStatic && internal)
3064 3052
         {
3065 3053
            // can't find an instance member, check for static
3066
            mdat = exactMethodLookup(name, sig, access, true, internal);
3054
            result = exactMethodLookup(name, sig, access, true, internal);
3067 3055
         }
3068 3056
         
3069
         if (mdat == null)
3057
         if (result == null)
3070 3058
         {
3071
            mdat = fuzzyMethodLookup(name, sig, access, isStatic, internal, node);
3059
            result = fuzzyMethodLookup(name, sig, access, isStatic, internal, node);
3072 3060
   
3073
            if (mdat == null && !isStatic && internal)
3061
            if (result == null && !isStatic && internal)
3074 3062
            {
3075 3063
               // can't find an instance member, check for static
3076
               mdat = fuzzyMethodLookup(name, sig, access, true, internal, node);
3064
               result = fuzzyMethodLookup(name, sig, access, true, internal, node);
3077 3065
            }
3078 3066
         }
3079 3067
      }
3080 3068
      
3081
      return mdat;
3069
      return result;
3082 3070
   }
3083 3071
   
3084 3072
   /**
3085
    * Find the named method based on an exact signature match. This method will search up the
3086
    * parent hierarchy (recursively) if no exact match is found in the current class.
3073
    * Find the named method based on an exact signature match. This method searches all methods in the
3074
    * parent hierarchy not just in the current class.  This search the hierarchy approach is needed so
3075
    * that some special exceptions can be honored:
3076
    * <p>
3077
    * <ul>
3078
    *    <li>If the caller does not specify a mode and there is an otherwise exact match, that method is
3079
    *        selected.  Buffers have no mode processing so this case is ignored for them.</li>
3080
    *    <li>A caller's table-handle or dataset-handle parameter which has an exact match is disallowed
3081
    *        if there are any alternatives with a table/dataset at that parameter AND that parameter type
3082
    *        is what determines that the signatures are different.</li>
3083
    * </ul>
3087 3084
    *
3088 3085
    * @param    name
3089 3086
    *           Resource name.
3090
    * @param    sig
3087
    * @param    caller
3091 3088
    *           Method call signature.
3092 3089
    * @param    access
3093 3090
    *           Access mode (<code>KW_PUBLIC</code>, <code>KW_PROTECTD</code> or
......
3100 3097
    *
3101 3098
    * @return   Resource found or <code>null</code> if no match exists.
3102 3099
    */
3103
   private MemberData exactMethodLookup(String         name,
3104
                                        ParameterKey[] sig,
3105
                                        int            access,
3106
                                        boolean        isStatic,
3107
                                        boolean        internal)
3108
   {
3109
      MemberData mdat = null;
3110
      
3111
      synchronized (lock)
3112
      {
3113
         Map<String, Map<SignatureKey, MemberData>> map = isStatic ? smethods : imethods;
3114
         
3115
         if (map != null)
3116
         {
3117
            Map<SignatureKey, MemberData> defs = map.get(name.toLowerCase());
3118
            
3119
            if (defs != null)
3120
            {
3121
               // exact signature match is based on the key lookup
3122
               mdat = checkAccessRights(defs.get(new SignatureKey(sig)), access);
3123
            }
3124
         }
3125
         
3126
         // ignore if the static/instance does not match
3127
         if (mdat != null && !((!isStatic && (!mdat.isStatic || internal)) ||
3128
                               (isStatic && mdat.isStatic)))
3129
         {
3130
            // clear our result
3131
            mdat = null;
3132
         }
3133
         
3134
         // only look up the parent hierarchy if we haven't got a match yet; static lookups only
3135
         // work for internal usage, non-static lookups always work
3136
         if (mdat == null && parents != null && (!isStatic || internal))
3137
         {
3138
            // change access mode since we aren't allowed to see private stuff in parent
3139
            int _access = (access == KW_PRIVATE) ? KW_PROTECTD : access;
3140
            
3141
            for (ClassDefinition parent : parents)
3142
            {
3143
               // recurse up inheritence hierarchy
3144
               mdat = parent.exactMethodLookup(name, sig, _access, isStatic, internal);
3145
               
3146
               if (mdat != null)
3147
                  break;
3148
            }
3149
         }
3150
      }
3151
      
3152
      return mdat;
3100
   private MethodSearchResult exactMethodLookup(final String         name,
3101
                                                final ParameterKey[] caller,
3102
                                                final int            access,
3103
                                                final boolean        isStatic,
3104
                                                final boolean        internal)
3105
   {
3106
      List<MatchMetrics> list = candidates(name, caller.length, access, isStatic, internal, false, null);
3107
      
3108
      // quick out if there are no possible matches
3109
      if (list.isEmpty())
3110
      {
3111
         return null;
3112
      }
3113

  
3114
      final boolean[] ignore = new boolean[caller.length];
3115
      
3116
      Predicate<MatchMetrics> exactMatch = (mem) -> 
3117
      {
3118
         for (int i = 0; i < caller.length; i++)
3119
         {
3120
            if (!ignore[i] && !caller[i].equals(mem.data.signature[i]))
3121
            {
3122
               return false;
3123
            }
3124
         }
3125
          
3126
         return true;
3127
      };
3128
       
3129
      List<MatchMetrics> exact = list.stream().filter(exactMatch).collect(Collectors.toList());
3130

  
3131
      Predicate<MatchMetrics> flexModeMatch = (mem) -> 
3132
      {
3133
         for (int i = 0; i < caller.length; i++)
3134
         {
3135
            if (!ignore[i])
3136
            {
3137
               if (caller[i].mode != null)
3138
               {
3139
                  if (!caller[i].equals(mem.data.signature[i]))
3140
                  {
3141
                     return false;
3142
                  }
3143
               }
3144
               else
3145
               {
3146
                  if (!caller[i].typeEquivalent(mem.data.signature[i]))
3147
                  {
3148
                     return false;
3149
                  }
3150
               }
3151
            }
3152
         }
3153
         
3154
         return true;
3155
      };
3156
      
3157
      MethodSearchResult result = null;
3158
      boolean       flex   = false;
3159
      
3160
      if (exact.isEmpty())
3161
      {
3162
         List<MatchMetrics> flexMode = list.stream().filter(flexModeMatch).collect(Collectors.toList());
3163
         
3164
         if (flexMode.size() == 1)
3165
         {
3166
            result = new MethodSearchResult(flexMode.get(0).data, null);
3167
            flex   = true;
3168
         }
3169
      }
3170
      else
3171
      {
3172
         if (exact.size() == 1)
3173
         {
3174
            result = new MethodSearchResult(exact.get(0).data, null);
3175
         }
3176
      }
3177
      
3178
      if (result != null)
3179
      {
3180
         // table-handle/dataset-handle matches are only valid when the only difference in signature is
3181
         // between a table/dataset and table-handle/dataset-handle
3182

  
3183
         Iterator<MatchMetrics> iter = list.iterator();
3184
         
3185
         // calculate which parameters are both table-handles/dataset-handles and have conflicting
3186
         // signature (another signature that has a table/dataset in the same parameter position)
3187
         while (iter.hasNext())
3188
         {
3189
            MatchMetrics m = iter.next();
3190
            
3191
            for (int i = 0; i < caller.length; i++)
3192
            {
3193
               // quick iteration if we already know this parameter should be ignored 
3194
               if (ignore[i])
3195
                  continue;
3196
               
3197
               String type = m.data.signature[i].type;
3198

  
3199
               if ("TABLE-HANDLE".equals(caller[i].type))
3200
               {
3201
                  if (type != null && type.startsWith("TEMP-TABLE"))
3202
                  {
3203
                     ignore[i] = true;
3204
                  }
3205
               }
3206
               else if ("DATASET-HANDLE".equals(caller[i].type))
3207
               {
3208
                  if (type != null && type.startsWith("DATA-SET "))
3209
                  {
3210
                     ignore[i] = true;
3211
                  }
3212
               }
3213
            }
3214
         }
3215

  
3216
         // ignoring the conflicting parameters, can we still calculate find a unique match?
3217
         List<MatchMetrics> ignoreTH = list.stream()
3218
                                            .filter(flex ? flexModeMatch : exactMatch)
3219
                                            .collect(Collectors.toList());
3220
         if (ignoreTH.size() != 1)
3221
         {
3222
            result = null;
3223
         }
3224
      }
3225
      
3226
      return result;
3227
   }
3228
   
3229
   /**
3230
    * Remove any candidates from the list which do not match the caller's extent.  The following are
3231
    * possible matches:
3232
    * <p>
3233
    * <ul>
3234
    *    <li> Neither the caller nor the parameter definition have an extent specification.
3235
    *    <li> Both the caller and the parameter definition have an indeterminate extent.
3236
    *    <li> The caller has a fixed extent value and the parameter definition has the same fixed extent
3237
    *         value.
3238
    *    <li> The caller has a fixed extent value and the parameter definition has an indeterminate
3239
    *         extent.  This is a fuzzy match (all the above criteria are the same as exact matches). 
3240
    *
3241
    * @param    list
3242
    *           The list of candidates.
3243
    * @param    callerExtVal
3244
    *           Caller's extent value (-1 means indeterminate, 0 is scalar and any positive value is
3245
    *           the fixed extent).
3246
    * @param    idx
3247
    *           Parameter number being processed.
3248
    */
3249
   private void processExtentMatches(List<MatchMetrics> list, int callerExtVal, int idx)
3250
   {
3251
      Predicate<MatchMetrics> test = null;
3252
      Consumer<MatchMetrics>  mark = (m) -> m.extExact++;
3253
      
3254
      // no extent in caller
3255
      if (callerExtVal == 0)
3256
      {
3257
         // no extent in the parameter definition
3258
         test = (mem) -> mem.data.signature[idx].type.indexOf("[") == -1;
3259
         
3260
         // no marking needed here
3261
         mark = (m) -> {};
3262
      }
3263
      // indeterminate extent in caller
3264
      else if (callerExtVal == -1)
3265
      {
3266
         // indeterminate extent in the parameter definition
3267
         test = (mem) -> mem.data.signature[idx].type.indexOf("[") != -1 && 
3268
                         getExtent(mem.data.signature[idx].type) == -1;
3269
      }
3270
      // fixed extent in caller
3271
      else
3272
      {
3273
         test = (mem) -> 
3274
         {
3275
            int extIdx = mem.data.signature[idx].type.indexOf("[");
3276
            
3277
            if (extIdx != -1)
3278
            {
3279
               // the parameter definition is an extent
3280
               int parmExtVal = getExtent(mem.data.signature[idx].type);
3281
               
3282
               // fixed extents match exactly or the parm is indeterminate
3283
               return callerExtVal == parmExtVal || parmExtVal == -1;
3284
            }
3285
            else
3286
            {
3287
               // the parameter definition is NOT an extent
3288
               return false;
3289
            }
3290
         };
3291

  
3292
         // this will only be used for where we already know extIdx != -1 above which means it is an
3293
         // extent at the parameter definition (it may be either fixed or indeterminate) 
3294
         mark = (mem) -> 
3295
         {
3296
            // the parameter definition is an extent
3297
            int parmExtVal = getExtent(mem.data.signature[idx].type);
3298
            
3299
            if (callerExtVal == parmExtVal)
3300
            {
3301
               // both are fixed extents match exactly
3302
               mem.extExact++;
3303
            }
3304
            else if (parmExtVal == -1) 
3305
            {
3306
               // or the parm is indeterminate
3307
               mem.extCvt++;
3308
            }
3309
         };
3310
      }
3311
      
3312
      list.removeIf(test.negate());
3313
      list.stream().forEach(mark);
3153 3314
   }
3154 3315
   
3155 3316
   /**
......
3206 3367
   }
3207 3368
   
3208 3369
   /**
3370
    * Check if there is only 1 possible match for the given predicate, if so then remove all candidates
3371
    * from the list except for that one possible match.
3372
    * 
3373
    * @param    list
3374
    *           The list to edit.
3375
    * @param    test
3376
    *           The predicate to test.
3377
    * @param    idx
3378
    *           The parameter index for error messages.
3379
    * @param    callerType
3380
    *           The caller type for error messages.
3381
    * @param    error
3382
    *           A location to store an error message for the caller.
3383
    */
3384
   private boolean allowIfOnlyOne(List<MatchMetrics>      list, 
3385
                                  Predicate<MatchMetrics> test,
3386
                                  int                      idx, 
3387
                                  String                   callerType,
3388
                                  String[]                 error)
3389
   {
3390
      boolean result = false;
3391
      
3392
      List<MatchMetrics> matches = list.stream().filter(test).collect(Collectors.toList());
3393

  
3394
      int num = matches.size();
3395

  
3396
      if (num == 1)
3397
      {
3398
         result = true;
3399
         matches.stream().forEach((m) -> m.cvt++);
3400
         list.removeIf(test.negate());
3401
      }
3402
      else if (num > 1)
3403
      {
3404
         error[0] = String.format("Ambiguous matches (%d) for parameter %d of type %s.",
3405
                               num,
3406
                               idx, 
3407
                               callerType);
3408
      }
3409
      else
3410
      {
3411
         error[0] = String.format("No possible matches for parameter %d of type %s.", idx, callerType);
3412
      }
3413
      
3414
      if (!result)
3415
      {
3416
         list.clear();
3417
      }
3418
      
3419
      return result;
3420
   }
3421
   
3422
   /**
3423
    * Remove any non-exact matches from the given list.
3424
    * 
3425
    * @param    list
3426
    *           The list to edit.
3427
    * @param    exactMatch
3428
    *           The test for an exact match.
3429
    *           
3430
    * @return   {@code true} if edits were made.
3431
    */
3432
   private boolean processExactMatches(List<MatchMetrics> list, Predicate<MatchMetrics> exactMatch)
3433
   {
3434
      // calculate the exact TYPE matches
3435
      List<MatchMetrics> exacts = list.stream().filter(exactMatch).collect(Collectors.toList());
3436
      
3437
      if (!exacts.isEmpty())
3438
      {
3439
         exacts.stream().forEach((m) -> m.exact++);
3440
         
3441
         // there is at least 1 exact match, remove everything else
3442
         list.removeIf(exactMatch.negate());
3443
         
3444
         return true;
3445
      }
3446

  
3447
      return false;
3448
   }
3449
   
3450
   /**
3209 3451
    * Find the named method based on a fuzzy signature match. This method will search up the
3210 3452
    * parent hierarchy (recursively) if no fuzzy match is found in the current class.
3211 3453
    *
......
3226 3468
    *
3227 3469
    * @return   Resource found or <code>null</code> if no match exists.
3228 3470
    */
3229
   private MemberData fuzzyMethodLookup(String         name,
3230
                                        ParameterKey[] caller,
3231
                                        int            access,
3232
                                        boolean        isStatic,
3233
                                        boolean        internal,
3234
                                        Aast           node)
3471
   private MethodSearchResult fuzzyMethodLookup(String         name,
3472
                                                ParameterKey[] caller,
3473
                                                int            access,
3474
                                                boolean        isStatic,
3475
                                                boolean        internal,
3476
                                                Aast           node)
3235 3477
   {
3236
      MemberData mdat = null;
3237
      
3238
      boolean polyParams = false;
3239
      for (ParameterKey p : caller)
3240
      {
3241
         if ("BaseDataType".equals(p.type))
3242
         {
3243
            polyParams = true;
3244
            break;
3245
         }
3246
      }
3247
      
3248 3478
      boolean first = isSetParam(name);
3249 3479
      
3250
      if ("Progress.Json.ObjectModel.JsonObject".equalsIgnoreCase(this.name) &&
3251
          "write".equalsIgnoreCase(name) &&
3252
          caller.length == 2 &&
3253
          caller[0].type.equals("longchar") &&
3254
          caller[1].type.equals("logical"))
3480
      List<MatchMetrics> list = candidates(name, caller.length, access, isStatic, internal, first, null);
3481
      
3482
      // GES_TODO: remove
3483
      // boolean debug = "calc3".equals(name) && caller.length == 1 && caller[0].type.equals("object<? extends oo.basic.compleximplements>");;
3484
      boolean debug = false;
3485
      
3486
      if (debug)
3255 3487
      {
3256
         int bogus = 14;
3488
         System.out.println("\n\n--------------------fuzzyMethodLookup START--------------------\n");
3489
         
3490
         System.out.printf("\n   CALLER %s\n   PARAMETERS: %d\n", node.dumpTree(true), caller.length);
3491
         
3492
         for (int i = 0; i < caller.length; i++)
3493
         {
3494
            System.out.printf("      %s\n", caller[i]);
3495
         }
3496
         
3497
         for (MatchMetrics m : list)
3498
         {
3499
            System.out.printf("DEBUG: %s\n", m.data);
3500
         }
3257 3501
      }
3258
      
3259
      List<MemberData> list = candidates(name, caller.length, access, isStatic, internal, first, null);
3260 3502
            
3261 3503
      // quick out if there are no possible matches
3262 3504
      if (list.isEmpty())
3263 3505
      {
3506
         if (debug)
3507
            System.out.println("--------------------fuzzyMethodLookup QUICK OUT--------------------");
3508
         
3264 3509
         return null;
3265 3510
      }
3266 3511
      
3267 3512
      // special case for ParameterList.setParameter()
3268 3513
      if (first && list.size() == 1)
3269 3514
      {
3270
         return list.get(0);
3515
         return new MethodSearchResult(list.get(0).data, null);
3271 3516
      }
3272
      
3273
      // the fuzzy lookup is done in phases
3274
      // 1. collect all matches, regardless of parameter modes
3275
      // 2. if only one match found, use that
3276
      // 3. if multiple matches found:
3277
      // - if one exact match by exact parameter type, use that
3278
      // - if multiple matches, get by parameter modes
3279
      //   > if caller hasn't specified a mode for an argument, use wildcard
3280
      //   > only one match should be found
3281
      //   > if multiple matches, show warning
3282
      
3283
      List<MemberData> matches = new ArrayList<>();
3284
      List<MemberData> exactMatches = new ArrayList<>();
3285
      
3286
      // TODOS:
3287
      // - constructors
3288
      // - handle dynamic invocation marking here
3289
      // - tables/table handles
3290
      // - datasets/dataset handles
3291
      // - object fuzziness
3292
      // - does primitive type fuzziness have less priority than primitive type exact matches?
3517

  
3518
      String[] error = new String[1];
3519
      
3520
      boolean polyArgs = false;
3521
      
3522
      // TODO: constructors
3523

  
3524
      // PHASE 1: cut down the list of all matches to include only those which are potentially valid
3525
      
3526
      if (debug)
3527
         System.out.println("\n\n---PHASE 1 START---\n");
3293 3528

  
3294 3529
      // process each parameter from left to right; the processing will vary by the parameter type; some
3295
      // types (e.g. objects) have multi-phase checks and other types are a simple comparison; at each
3530
      // types (e.g. objects) have multi-step checks and other types are a simple comparison; at each
3296 3531
      // step of the way the list of possible candidates will be reduced until there are only candidates
3297
      // left which match all criteria; at that point if there are more than one then we either have a 
3298
      // dynamic invocation scenario or there is some ambiguity (which should not happen if the code
3299
      // compiles in the 4GL)
3532
      // left which COULD match all criteria; at that point if there are more than one then we have an
3533
      // additional disambiguation step, a dynamic invocation scenario or there is some ambiguity (which
3534
      // should not happen if the code compiles in the 4GL)
3300 3535
      
3301
      // OUTLINE FOR NEXT CHANGES
3302
      /* 
3303
      caller:
3536
      // process the caller's parameters, left to right; for each caller parameter we examine the matching
3537
      // parameter in the list of candidates, removing any candidates in the list that cannot match
3304 3538
      for (int i = 0; i < caller.length; i++)
3305 3539
      {
3540
         final int idx = i;
3541
         
3542
         final Integer callerMode = caller[i].mode;
3543
         
3306 3544
         // if the caller is passing Java types, then we match them as if they were 4GL types so the get
3307 3545
         // converted here; if they are already 4GL types then no change will happen
3308
         String callerType = fromJava(caller[i].type);
3309
         
3310
         // wildcards: unknown value or BDT (POLY cases) match anything (no cases can be excluded)
3311
         if (callerType == null                     ||
3312
             callerType.equals("BaseDataType")      ||
3313
             callerType.equals("unknown"))
3314
         {
3315
            continue caller;
3316
         }
3317
         
3318
         // primitive types
3319
         
3320
         // primitive types widening/narrowing
3321
         
3322
         // tables/table handles
3323
         
3324
         // handles passed to table
3325
         
3326
         // datasets/dataset handles
3327
         
3328
         // handles passed to datasets
3329
         
3330
         // buffers
3331
         
3332
         // extents
3333
         
3334
         // object direct match
3335
         
3336
         // object fuzziness
3337
         
3338
         // parameter modes
3339
      }
3340
      */
3341
      
3342
      Iterator<MemberData> iter = list.iterator();
3343
      
3344
      outer:
3345
      while (iter.hasNext())
3346
      {
3347
         MemberData dat = iter.next();
3348
         
3349
         ParameterKey[] candidate = dat.signature;
3350
         
3351
         boolean exact = true;
3352
         
3353
         inner:
3546
         final String  callerType = fromJava(caller[i].type);
3547
         
3548
         // these are all specific to the current parameter
3549
         Predicate<MatchMetrics> exactMatch = (m) -> callerType.equals(fromJava(m.data.signature[idx].type)); 
3550
         Predicate<MatchMetrics> isTable    = (m) -> m.data.signature[idx].type.startsWith("TEMP-TABLE");
3551
         Predicate<MatchMetrics> isTH       = (m) -> "TABLE-HANDLE".equals(m.data.signature[idx].type);
3552
         Predicate<MatchMetrics> isDataset  = (m) -> m.data.signature[idx].type.startsWith("DATASET <");
3553
         Predicate<MatchMetrics> isDH       = (m) -> "DATASET-HANDLE".equals(m.data.signature[idx].type);
3554
         Predicate<MatchMetrics> modeMatch  = (m) -> callerMode.equals(m.data.signature[idx].mode);
3555

  
3556
         // exclude any candidates which cannot ever match due to a caller MODE constraint; a null for the
3557
         // caller's mode is a wildcard that can match any mode in the parameter definition; this means that
3558
         // when the caller's mode is null, no matches can be excluded
3559
         if (callerMode != null)
3560
         {
3561
            list.removeIf(modeMatch.negate());
3562
            
3563
            if (debug)
3564
               System.out.printf("PARM %d: mode %d\n", idx, list.size());
3565
         }
3566

  
3567
         // TYPE-specific processing
3568
         
3569
         boolean poly = (callerType == null) || callerType.equals("BaseDataType");
3570
         
3571
         // TYPE wildcards: unknown value or BDT (POLY cases) match any TYPE
3572
         if (poly || callerType.equals("unknown"))
3573
         {
3574
            list.stream().forEach((m) -> m.wildcard++);
3575
            
3576
            if (poly)
3577
               polyArgs = true;
3578
            
3579
            // no cases can be excluded based on type; the list remains unchanged
3580
            if (debug)
3581
               System.out.printf("PARM %d: type wildcard %d\n", idx, list.size());
3582
         }
3583
         else
3584
         {
3585
            // table parameters
3586
            if (callerType.startsWith("TEMP-TABLE"))
3587
            {
3588
               // check for any exact TYPE matches
3589
               if (!processExactMatches(list, exactMatch))
3590
               {
3591
                  // allow a match to a table-handle if there is only 1 table-handle option
3592
                  allowIfOnlyOne(list, isTH, idx, callerType, error);
3593
               }
3594
               
3595
               if (debug)
3596
                  System.out.printf("PARM %d: TT %d\n", idx, list.size());
3597
            }
3598

  
3599
            // table handles
3600
            else if (callerType.equals("TABLE-HANDLE"))
3601
            {
3602
               // at this point we can't know if we have a match, we can just remove anything that is not
3603
               // a table-handle or table; we defer the matching to the next phase once all other removals
3604
               // are done               
3605
               list.removeIf(isTH.or(isTable).negate());
3606
               list.stream().forEach((m) -> m.cvt++);
3607
               
3608
               if (debug)
3609
                  System.out.printf("PARM %d: TH %d\n", idx, list.size());
3610
            }
3611
            
3612
            // dataset parameters
3613
            else if (callerType.startsWith("DATASET <"))
3614
            {
3615
               // check for any exact TYPE matches
3616
               if (!processExactMatches(list, exactMatch))
3617
               {
3618
                  // allow a match to a dataset-handle if there is only 1 dataset-handle option
3619
                  allowIfOnlyOne(list, isDH, idx, callerType, error);
3620
               }
3621
               
3622
               if (debug)
3623
                  System.out.printf("PARM %d: DS %d\n", idx, list.size());
3624
            }
3625
            
3626
            // dataset handles
3627
            else if (callerType.equals("DATASET-HANDLE"))
3628
            {
3629
               // at this point we can't know if we have a match, we can just remove anything that is not
3630
               // a dataset-handle or dataset; we defer the matching to the next phase once all other
3631
               // removals are done               
3632
               list.removeIf(isDH.or(isDataset).negate());
3633
               list.stream().forEach((m) -> m.cvt++);
3634
               
3635
               if (debug)
3636
                  System.out.printf("PARM %d: DH %d\n", idx, list.size());
3637
            }
3638

  
3639
            // buffers
3640
            else if (callerType.startsWith("BUFFER"))
3641
            {
3642
               // only an exact match works (handled above); no fuzzy matching here
3643
               list.removeIf(exactMatch.negate());
3644
               list.stream().forEach((m) -> m.exact++);
3645
               
3646
               if (debug)
3647
                  System.out.printf("PARM %d: buf %d\n", idx, list.size());
3648
            }
3649

  
3650
            // must be one of the BaseDataType wrapper classes
3651
            else
3652
            {
3653
               final int     extIdx    = callerType.indexOf("[");
3654
               final int     extVal    = (extIdx == -1) ? 0 : getExtent(callerType);
3655
               final String  basicType = (extIdx == -1) ? callerType : callerType.substring(0, extIdx);
3656

  
3657
               // extents
3658
               processExtentMatches(list, extVal, idx);
3659
               
... This diff was truncated because it exceeds the maximum size that can be displayed.