Search code examples
.netcobolnetcobol

Iterating Systems.Collections.List in Fujitsu COBOL


How do I loop through a collection?

I'm on a trial version of Fujitsu/Alchemy compiler, and getting slow and poor support from the vendor.

I basically want to pass a List from C# to COBOL, and then let the COBOL use it and potentially update it.

In C#, the normal way of iterating through a collection is to use the "foreach" construct.

However, the C# "foreach" construct is a shortcut for the following:

private static void test1()
{
    List<IDMSMapField> list1 = new List<IDMSMapField>();
    int listSize = list1.Count;
    // was just checking exact variablename and case here to copy into COBOL code.
    int itemNumber = 0;

    System.Collections.Generic.List<IDMSMapField>.Enumerator enumerator1 = list1.GetEnumerator();

    while (enumerator1.MoveNext())
    {
        Console.Write("Okay" + enumerator1.Current);
    }
}

I can write this in COBOL if you can help me figure out to declare this class:

System.Collections.Generic.List<IDMSMapField>.Enumerator

The "Enumerator" structure is documented on Microsoft's MSDN site.

It tells that "Enumerator" is a Struct, not a Class!

From what I can tell in the manual "CreatingCOBOLfromDotnetFrameworkDox.pdf", structures are defined as classes in the COBOL REPOSITORY.

Example from the manual:

Define specifiers for structure in REPOSITORY, and any struct members:
CLASS STRUCT-name AS "struct-namespace"
PROPERTY PROP-struct-member AS "external-property-name"
Handle structures like classes. E.g. object to store a struct instance:
01 struct-object OBJECT REFERENCE STRUCT-name.

Below, I am repeating some of the variations I tried that have all failed to compile, because of "cannot be resolved" error. If you can show me howto to declare this properly, I think we can move forward.

1.

    REPOSITORY.
           CLASS  CLASS-LIST         AS "System.Collections.Generic.List<>"
           CLASS  STRUCT-Enumerator  AS "System.Collections.Generic.List<>.Enumerator"
           .

Error on second line:

error JMN1795I-S: The named reference 'System.Collections.Generic.List<>.Enumerator' cannot be resolved.

  1. Exact same error for this:

        REPOSITORY.
               CLASS  CLASS-LIST         AS "System.Collections.Generic.List<>"
               CLASS  STRUCT-Enumerator  AS "System.Collections.Generic.List<T>.Enumerator"
               .
    

error JMN1795I-S: The named reference 'System.Collections.Generic.List.Enumerator' cannot be resolved.

  1. Same error for this:

    REPOSITORY. CLASS CLASS-LIST AS "System.Collections.Generic.List<>" CLASS STRUCT-Enumerator as "System.Collections.Generic.List.Enumerator" .

error JMN1795I-S: The named reference 'System.Collections.Generic.List.Enumerator' cannot be resolved.

The other alternative is to treat it as an array, but I'm stuck on that as well.

REPOSITORY.
    CLASS LIST-IDMSMapField    AS "System.Collections.Generic.List<>[]"
    CLASS CLASS-IDMSMapField   AS "Lightyear.ERCB.IDMSDC.IDMSMapField"
    CLASS CLASS-LIST-IDMSMapField EXPANDS LIST-IDMSMapField USING CLASS-IDMSMapField.

METHOD-ID. TW1DR4000-PF06 AS "TW1DR4000_PF06".
DATA DIVISION.
WORKING-STORAGE SECTION.

01 MapFieldItem USAGE OBJECT REFERENCE CLASS-IDMSMapField.

LINKAGE SECTION.

01 MapFieldList USAGE OBJECT REFERENCE CLASS-LIST-IDMSMapField.

PROCEDURE DIVISION...
...
SET MapFieldItem TO MapFieldList(1).

error JMN2671I-S: ':' must be specified in the reference modifier. ':' is assumed.

I think the compiler sees the (1) as a substring operation perhaps.


Solution

  • It took a while, but here's a complete working sample. There were a couple of other tricks getting to the properties, and moving the property values such as Count to a COBOL variable with the proper USAGE clause. The "EXPANDS" keyword (in the repository) is another key part of the solution.

    In my actual program, I'll receive the list in the LINKAGE section, and I'll have a list of more complex objects... but the sample-code is a simpler scenario that stands-alone and runs "as is".

       IDENTIFICATION DIVISION.
       PROGRAM-ID. MAIN AS "COBOLEnumerationSample.Main".
       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       SPECIAL-NAMES.
       REPOSITORY.
           CLASS List        AS "System.Collections.Generic.List<>"
           CLASS SysString   AS "System.String"
           CLASS SysBoolean  AS "System.Boolean" 
           CLASS ListString EXPANDS List USING SysString
           CLASS Enumerator  AS "System.Collections.Generic.List<>+Enumerator"
           CLASS StringEnumerator EXPANDS Enumerator USING SysString
           PROPERTY PROP-Count   AS "Count"      
           PROPERTY PROP-Current AS "Current"
           .       
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 myList              OBJECT REFERENCE ListString.
       01 myEnum              OBJECT REFERENCE StringEnumerator.   
       01 myBooleanEnumResult OBJECT REFERENCE SysBoolean. 
       01 myDotNetString      OBJECT REFERENCE SysString.  
       01 myLoopCounter       PIC 9(4)  value zero. 
       01 myCobolCount        PIC S9(9) COMP-5 VALUE ZERO.
       01 myCobolString       PIC X(30) value spaces. 
       01 YN-END-LOOP         PIC X     value "N". 
       01 WS-ACCEPT-INPUT     PIC X(80) value spaces. 
    
       PROCEDURE DIVISION.
       1000-START.
           INVOKE ListString "NEW" returning myList.
           INVOKE myList "Add" using "Apples"
           INVOKE myList "Add" using "Bananas"
           INVOKE myList "Add" using "Orange"
    
    
           SET myCobolCount to PROP-Count of myList 
           DISPLAY "Size of MyList = " myCobolCount
    
           INVOKE myList "GetEnumerator" returning myEnum.
    
    
           PERFORM UNTIL YN-END-LOOP = "Y" 
                 INVOKE myEnum "MoveNext" returning myBooleanEnumResult  
      * How to test for Boolean, True = B'1' and False = B'0' 
                 IF myBooleanEnumResult = B'0' 
                    MOVE "Y" TO YN-END-LOOP 
                 ELSE       
                    SET myDotNetString TO PROP-Current of myEnum 
                    ADD 1 TO myLoopCounter
                    SET myCobolString to myDotNetString 
                    DISPLAY myLoopCounter " " myCobolString 
                 END-IF    
    
           END-PERFORM 
    
           DISPLAY "END OF PROGRAM - PRESS ENTER TO END" 
           ACCEPT WS-ACCEPT-INPUT
           .
    
       END PROGRAM MAIN.