COBOL Data Groups
COBOL programmers have for years used '01' level (or Group Descriptions) as a means of creating a data structure for related items. It would be safe to say these Data Groups are used in just about every COBOL program that has been written and probably will ever be written. They provide a quick way for COBOL programmers to group together data that has some form of relationship. For example, the name of a region and the total sales for a particular timeframe as in the following structure:
In the above sample we see an '01' (or Group) description for a field called WS-Branch-Rec. This is the highest definition for a related group of data items. Underneath this item there are two additional data items, WS-BRANCH-NAME and WS-BRANCH-TOT. Each of these items occurs 10 times in a table. You can think of the structure in terms of a spreadsheet, 10 rows with two columns of data. The variable WS-BRANCH-NAME is defined as a "PIC X(50)" which means it is 50 characters in length and is an alphanumeric data item (or a String in C#). WS-BRANCH-TOT is defined as a "PIC 9(10)" is a numeric data item (we will use decimal in C#).
In this article we will begin with a C# console application calling a COBOL DLL file. The C# application will pass it a number of occurrences to create a table (much like a user requesting a specific number of months to report on). The COBOL program will accept the user request (which we have currently defaulted to 5), create the data group and pass control back to the C# program. The C# program will then read in the data group as an array and display the results on the screen.
Please keep in mind that while the example is not of a completely finished and polished application the intent is to demonstrate how to utilize your existing COBOL business logic and interact with a new C# front-end.
We will assume you are comfortable with the C# language. If you have any questions in this area please consult with one of your local C# experts.
As we mentioned above, we will call the COBOL program, passing it a variable to define the number of occurrences we wish to populate. The following code illustrates how to do this:
The first two lines are basic display statements to inform the operator we are beginning the process. The third line of code though instantiates our COBOL program. As we will soon see our COBOL program has instance methods and must be instantiated prior to use. The fourth line of code is the key to working with the COBOL data groups. In this line we create an instance of a data class that we have defined using COBOL. This data class is simply a class definition with the data variables defined as PROPERTIES of the class, as illustrated by the following:
Referring back to the C# program, we define the above COBOL class as an array (note the square brackets in the definition) we can mimic the definition of the COBOL data group. This will permit our existing COBOL code to continue to process as before while at the same time expanding access to our process in a manner in which C# (or any other .NET enabled language) will readily recognize and work with.
Getting back to our C# program, the lines of code down to the invocation of the COBOL method are housekeeping, they initialize some variables we are going to use and display a message to the operator. When the COBOL method is invoked 2 parameters are being passed, the number of occurrences that we wish to create (Ctr) and the array object we created for the data group (BranchArray). Prior to invoking the COBOL method, BranchArray is initialized but contains no data.
The COBOL method
The COBOL method “TransTestIn” accepts two parameters as input and creates the data group. The coding for the method is as follows:
There are two PERFORM loops in this method. The initial loop creates the number of occurrences in the table based on the input parameter CTR. This is standard COBOL and we will not delve into an explanation of this code other than to mention this could be your existing COBOL programs. They would continue to operate and perform the tasks for which they were designed.
Upon completion of their processing however is where new coding should be placed to create a .NET data type of the data for use by other .NET languages. This second PERFORM loop could in actuality become a called module that would be separate from the existing COBOL source. The second PERFORM loop is where we populate the array we created in the C# program and is how we can interact with the .NET data types.
Within the first line of the PERFORM loop a new instance of the data record, defined in the .NET data types, is created. If you will remember these were defined in the data class we explored using the .NET data types. The next two lines populate the data in the BranchRecObj with the data from the COBOL table. Notice we are doing 'SET's. Whenever you are referencing an object identifier (i.e. you use OBJECT REFERENCE as the PIC clause for the data item) you must use a SET statement. Due to the COBOL data types being strongly defined to the .NET data types the data is marshaled accordingly and no further data conversion is required.
The next line
COMPUTE ArrayCtr = Cbl-Ctr - 1
is a subtle reminder of the difference in COBOL indexing and .NET indexing. In COBOL, arrays (or tables), are '1' based, that is they begin with an index of '1'. .NET is zero based or they begin with an index of zero. If you forget this little goodie you will eventually generate an 'Index Out Of Range' error and will spend more than a few minutes trying to identify the source of the error. Take heed and remember the indexing difference!
The next line in the PERFORM loop is the actual write or update of the data to the array object passed to the method, BRANCHARRAY. In our instance the array object utilizes 2 variables, an index to tell the array where to store the object, and the object that we wish to store in the array. Finally we increment the array counter and test to determine if we are to do another loop through the process.
Returning to the C# program
Upon our return to the C# program we need to retrieve the data from the array. The following code accomplishes this and displays it to the console window:
The first line of code instantiates an object of the BranchRec class that we will use to retrieve the data into. We have created a 'for loop' to process the correct number of times according to our previously defined counter (Ctr). Again, one note, the .NET Framework operates as zero based, not one based so we need to begin our array index at zero. This implies then that our ending number will be one less than the count the COBOL system utilized, or 4 in our case. The 'for loop' has been created to begin at zero, determine if "ctr" is less than or equal to 4 and then increment the counter if it is.
Within the 'for loop' the first line of code retrieves an element of the array and stores it in the object instance. Lines two and three retrieve the specific data items to temporary variables and lines four and five display the data to the system console. When the application is executed the console window should return results similar to the following screen (dependant upon the number of iterations you programmed for the counter):
The ZIP file has all the necessary source code for you to follow along and see how the code is to be structured. Being able to utilize an existing COBOL data structure in the .NET Framework will greatly expand the interoperability and life of your existing COBOL applications. While the process may seem complicated at first, spend a few minutes and analyze what we've done in this article. The steps will be basically the same for you and will greatly extend the usefulness of your existing applications to the .NET Framework. And that's what it's all about...