Lookup function call in Generic API

Consider a COBOL program called REPORTER that makes a call to an external program named READFILE which takes a string indicating which logical file to access. For example:

01 FILE-1   PIC X(8).

MOVE "SALES" TO FILE-1.
CALL "READFILE" USING FILE-1.

READFILE takes the SALES parameter and maps it to the actual file to be accessed, in this case FILE0002. To enable Enterprise Analyzer to capture the information that program REPORTER reads file FILE0002, you must use the lookup function in the Generic API to perform mapping and generate the required relationship.

To do this, you must first have the mapping from the READFILE parameters to the logical filenames. This is given in a comma-separated values (CSV) file. For example:

CUSTOMER,FILE0001
SALES,FILE0002
ORDERS,FILE0003

Each line contains two fields. The first is the string that is passed to READFILE which indicates which file to access, and the second is the logical file name that READFILE will access when passed that string.

The required output in Enterprise Analyzer for this CALL is:



This relationship indicates that REPORTER reads logical file FILE0002, though the actual access of the file is done via READFILE.

Now you can create a Generic API specification in Legacy.xml to match this code pattern. Each pattern is contained in an APIEntry tag with a unique name:

<APIEntry name="READ1">
</APIEntry>

The first element to add is the type of statement to match, in this case, a CALL to a program named READFILE:

<APIEntry name="READ1">
  <match stmt="CALL">
    <name value="READFILE" />
  </match>
</APIEntry>

The next element to add is a variable for the parts of the CALL statement you want to match. In this case, match the first parameter of the CALL statement, and call it fname:

<APIEntry name="READ1">
  <match stmt="CALL">
    <name value="READFILE" />
  </match>
  <vars>
    <arg var="fname" param="1" type="data" offset="0" size="8" />
  </vars>
</APIEntry>

For the CALL example above, it will assign SALES to fname.

You must now specify the relationship to be generated. The file Legacy.xml.api provides several example patterns that can be used here, such as a file read operation:

<APIEntry name="READ1">
  <match stmt="CALL">
    <name value="READFILE" />
  </match>
  <vars>
    <arg var="fname" param="1" type="data" offset="0" size="8" />
  </vars>
  <rep>
    <file filename”...” action="Reads" data-record="%1" hc-kind="apiXREAD" />
  </rep>
</APIEntry>

You now need to populate the missing filename attribute in the template with the name of the logical file being read. To do this, use the CSV file above to map the fname variable matched earlier to the actual logical file name. The lookup function enables us to do this.

The syntax for the lookup function is:

%{lookup,<file>,<keyCol>,<valueCol>,<expr>}
<file>
Specifies the full path to the CSV file containing the mappings.
<keyCol>
Specifies the field for each line in the file that contains the key value to look up.
Note: The first field is numbered 0.
<valueCol>
Specifies the field for each line containing the corresponding string value that maps to the key.
<expr>
Specifies the actual key value to use in the lookup and can be a variable name or another function call.

In this case, the key column is column 0 in each line, and the value column is column 1 in each line. The key value being searched for is the fname variable which was matched earlier. Therefore, the following expression will perform the appropriate mapping:

%{lookup,map.csv,0,1,fname}

The map.csv is replaced here by the full path to the CSV file shown above.

Putting the whole thing together gives us a full Legacy.xml specification:

<APIEntry name="READ1">
  <match stmt="CALL">
    <name value="READFILE" />
  </match>
  <vars>
    <arg var="fname" param="1" type="data" offset="0" size="8" />
  </vars>
  <rep>
    <file filename="%{lookup,map.csv,0,1,fname}" action="Reads" data-record="%2" hc-kind="apiXREAD" />
  </rep>
</APIEntry>

Generic API will match the CALL READFILE statement, capture the value of first parameter FILE-1 ("SALES") in the variable fname, and then lookup SALES in the CSV file to get the logical filename FILE0002 for the new Reads File relationship in the repository:



If Generic API matches a statement, but the key value used is not present in the CSV file, then a relationship is still generated. However, it will be an unresolved decision, indicating that it could not be fully resolved to a final value. For example:

01 FILE-2   PIC X(8).

MOVE "EMPLOYEE" TO FILE-2.
CALL "READFILE" USING FILE-2.

In this case, the key EMPLOYEE does not appear in the CSV file, so you cannot determine which logical file is being accessed. Enterprise Analyzer generates an unresolved decision:



The generated relationship shows that REPORTER reads a file, but the name could not be determined.

The lookup function can also be used with other Generic API functions for more complex use. For example, suppose you have a similar external program READREG, which is like READFILE but where the string passed has a 2-character prefix indicating a region:

01 FILE-3   PIC X(10).

MOVE "U-CUSTOMER" TO FILE-3.
CALL "READREG" USING FILE-3.

To disregard that region prefix when mapping to a logical file, use the Generic API function substr to remove the prefix from the parameter before trying to match the CSV file.

%{lookup,map.csv,0,1,substr,2,,fname}

This expression instructs that a substring of the fname variable is taken, starting from position 2 to the end of the string. This results in CUSTOMER being used as the lookup value in the CSV file, which will return the logical file name FILE0001 for the generated relationship.

The entire specification becomes:

<APIEntry name="READ2">
  <match stmt="CALL">
    <name value="READREG" />
  </match>
  <vars>
    <arg var="fname" param="1" type="data" offset="0" size="10" />
  </vars>
  <rep>
    <file filename="%{lookup,map.csv,0,1,substr,2,,fname}" action="Reads" data-record="%2" hc-kind="apiXREAD" />
  </rep>
</APIEntry>

and the generated relationship is: