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.
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.
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.
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.
Using Micro Focus's .NET implementation the code can use a "perform varying through" extension to go through the list.
So the tweaked version 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
PROPERTY PROP-Count AS "Count"
PROPERTY PROP-Current AS "Current"
.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 myList OBJECT REFERENCE ListString.
01 myLoopCounter PIC 9(4) value zero.
01 myCobolCount PIC S9(9) COMP-5 VALUE ZERO.
01 myCobolString PIC X(30) value spaces.
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
move 0 to myLoopCounter
PERFORM varying myCobolString through myList
DISPLAY myLoopCounter " " myCobolString
ADD 1 to myLoopCounter
END-PERFORM
DISPLAY "END OF PROGRAM - PRESS ENTER TO END"
ACCEPT WS-ACCEPT-INPUT
.
END PROGRAM MAIN.
Next, rather than using the repository syntax and expands, you can use a inline declaration, for example:
IDENTIFICATION DIVISION.
PROGRAM-ID. MAIN AS "COBOLEnumerationSample.Main".
WORKING-STORAGE SECTION.
01 myList type "System.Collections.Generic.List"[string].
01 myLoopCounter PIC 9(4) value zero.
01 myCobolCount PIC S9(9) COMP-5 VALUE ZERO.
01 myCobolString PIC X(30) value spaces.
01 WS-ACCEPT-INPUT PIC X(80) value spaces.
PROCEDURE DIVISION.
1000-START.
INVOKE type "System.Collections.Generic.List"[string]::"NEW"
returning myList
INVOKE myList "Add" using "Apples"
INVOKE myList "Add" using "Bananas"
INVOKE myList "Add" using "Orange"
SET myCobolCount TO myList::"Count"
DISPLAY "Size of MyList = " myCobolCount
MOVE 0 TO myLoopCounter
PERFORM varying myCobolString through myList
DISPLAY myLoopCounter " " myCobolString
ADD 1 TO myLoopCounter
END-PERFORM
DISPLAY "END OF PROGRAM - PRESS ENTER TO END"
ACCEPT WS-ACCEPT-INPUT
.
END PROGRAM MAIN.
Anyway, I just love that we can do this in COBOL, no matter which product is being used.. Enjoy...
精彩评论