Chapter 22: Collections, Intrinsics and Dictionaries Tutorial

This tutorial is about using the collection classes. Collections are very useful in object-oriented programming. They are used to store groups of objects. You usually store objects of the same type in any particular collection, although you can also store objects of different types in a collection.

OO COBOL also enables you to store COBOL intrinsic data in collections. This is done by a mechanism which enables you to treat an item of intrinsic data, for example a PIC X(20), as though it were an object. You cannot mix intrinsic data and objects inside a single collection, or mix different types of intrinsic data.

Finally, this tutorial covers dictionaries, which are another type of collection.

This tutorial uses Micro Focus alternatives and extensions to the ISO 2002 OO COBOL syntax.

This tutorial consists of the following sessions:

Collections

In this session you will debug a simple COBOL program, coll0.cbl which illustrates some of the differences and similarities between the main types of collection. The program is not an OO COBOL class program, but procedural COBOL code which uses the Class Library collection objects.

For an explanation of the different types of collection, see the section Different Categories of Collection in the chapter Collection Frameworks.

To debug coll0.cbl

  1. Start the Net Express development environment.
  2. Click File > Open, and open Examples\Net Express IDE\Collect0\coll0.app. You'll see the .cbl files in the project window.
  3. Rebuild the project.
  4. Click Debug > Start Debugging Click OK.
  5. Before you begin stepping through the code, scroll through the Working-Storage Section until you see the declaration for fruitData.

    This is a set of strings the program stores in different types of collection.

  6. Return the cursor to the execution point.
  7. Step through the statements from A001 to A005 to create instances of the different types of collection.

    You need to specify an initial size for all types of collection. In the case of an Array, you can't exceed the initial capacity of the collection unless you send it the "grow" message. Other types of collection will grow in size if you add more elements than initially specified.

    However, growing collections can be an expensive operation at run time, so you should still always try to pick an initial size which will reduce the number of times a collection needs to grow.

  8. Step through the statements below tag A006.

    Data item i holds the length for the instances of CharacterArray which will be created as elements for the different types of collection.

  9. Step the statement below tag A007.

    A CharacterArray is an object for storing strings. The "withLengthValue" message creates a new instance of CharacterArray and initializes it with some data.

  10. Step the statements below tag A008 until the End-Perform statement.

    With all the different collection types, except Array, you use the "add" message to add new elements. With the Array, which does not grow automatically, you use "atPut" which stores the element at the specified index position.

    Although OrderedCollection is indexed, you can't use "atPut" until you have added elements to the collection. For example, once you have used "add" to add the first five elements, you can use "atPut" and an index between one and five to replace any of those elements. You can't do an "atPut" with an index greater than five until you have added more elements.

    You can't ever use "atPut" to put an element in a SortedCollection. A SortedCollection uses the value of each element to determine its position in the collection.

  11. Move the text cursor down to the statement below tag A010, and select Debug > Run to cursor. This completes execution of the perform loop without you needing to step through each statement in turn.
  12. Step the statements up to tag A011.

    This retrieves the object reference to the fourth element in the Array instance. Sending the "display" message to the CharacterArray displays it on the screen. Its contents should be the word "banana". (If you can't see the Output Window, click View > Debug Windows > Application Output.)

  13. Step the next statement.

    Because a Bag is not indexed, you can't retrieve elements from it directly. Instead, you can query it to find out whether it has one or more occurrences of an object with a given value.

    The "includes" message returns 1 if a Bag contains an object with a matching value and 0 if it doesn't. You can also examine the contents of unindexed collections (Bags and ValueSets) by using the iterator methods. These are covered later in this tutorial.

  14. Step the statements up until tag A012.

    These test the result of the returned value and tell you whether or not the bag contained an object with a matching value.

  15. Step the statement below tag A012.

    This adds the string to the Bag again. A Bag stores objects with duplicate values by recording the number of occurrences of each object with a different value.

  16. Step the statement below tag A013.

    The "occurrencesOf" message returns the number of elements a collection has that match the specified object in value.

  17. Step the statements up until tag A014.

    This displays the information that this Bag has two occurrences of the specified string.

  18. Step the statements between tag A014 and A015.

    This code demonstrates how the Array class also responds to the "occurrencesOf" message. You can use "occurrencesOf" and "includes" on indexed collections as well as instances of Bag and ValueSet. This tells you whether or not the element exists, but not its position.

  19. Step the first statement below tag A015.

    Instances of ValueSet do not maintain duplicate elements. The ValueSet already contains an element with value "banana", so it will not be added a second time.

  20. Step the next statement.

    This message always returns 1 or 0 for ValueSet instances.

  21. Step the other statements to tag A016.

    This displays the result of the "occurrencesOf" message.

  22. Click Debug > Run to execute the rest of the code in the program.

    The statements below tag A016 display every string in the OrderedCollection and SortedCollection in indexed order. The elements in the OrderedCollection appear in the order in which they were added. The elements in the SortedCollection appear sorted into ascending alphabetical order. Ascending order is the default for a SortedCollection.

  23. Click Debug > Stop Debugging to stop the Debugger.

At the end of this section, you may have some questions to ask:

Program coll0.cbl stores instances of CharacterArray in the collections it creates. An instance of a CharacterArray is a simple object, with an obvious single value (the string you store in it). But if you stored Account objects, like the ones used in the Inheritance Tutorial in a collection, how would you determine the value? Would it be the name, the balance or the account number?

The answer is that the collection objects provide a framework within which objects stored as elements must work. When a collection needs to know whether two objects are equal, it sends one object the "equal" message, passing it the other as a parameter. It is then up to the object to interrogate the other element and decide whether they are equal or not.

The default sort method for the SortedCollection works in a similar way. The SortedCollection sends one element the "lessThanOrEqual" method and a second element as a parameter. The receiving element can then compare itself to the second element and return a result.

If you are writing your own objects to store in collections, you may need to implement these methods yourself, unless you are subclassing from a class like CharacterArray, which implements them for you. There is also a default "equal" method in Base which compares the object handles of two objects. This implementation of "equal" will only find two elements equal if they are actually the same object.

For full information on the methods you might need to implement to use the Collection classes, see the chapter Collection Frameworks.

Using Intrinsics

In the previous session, you looked at a program which stored objects in different types of collection. There may be occasions when you want to store intrinsic COBOL data, like numbers, in collections. You can do this by using the intrinsic classes of the Base Class Library.

Micro Focus OO COBOL provides a mechanism that enables you to send a message to an intrinsic data item, as though it were an object, using the Invoke ... As statement.

Before you can send messages to intrinsic data, you have to clone an intrinsic class to create a new class for the type and length of intrinsic data you are using. The Base Class Library includes classes for three different types of intrinsic data (PIC X, PIC X COMP-X, PIC X COMP-5). These classes are templates which handle data of fixed length. When you clone the class, you specify the actual size of data you want to handle.

We will now look at a short sample program, coll1.cbl, which uses the intrinsic classes to store a set of integers in an array.

To debug coll1.cbl

  1. Start the Net Express development environment.
  2. Click File > Open, and open Examples\Net Express IDE\Collect1\coll1.app. You'll see the .cbl files in the project window.
  3. Rebuild the project.
  4. Click Debug > Start Debugging. Click OK.
  5. Step the two statements below tag A001.

    The "newClass" message creates a clone of the CobolComp5 class, initialized in this case for data four bytes in length. The object returned is a new class object, and not an instance of CobolComp5.

  6. Step the two statements below tag A002.

    This creates an instance of Array with space for ten elements. This time the message to create the Array instance is "ofValues" (in the example in the previous section it was "ofReferences"). When a collection is created with the "ofValues" message, it stores intrinsic data instead of object handles. The cloned class, PicX4Comp5, is used as a template so that the Array knows how much space to allocate for each element.

    You can't mix objects and intrinsics inside a single collection. Once you create a collection you can only store the type of data for which it is initialized. If you want to mix many different kinds of data in a collection, you should create the collection using "ofReferences", and use different types of objects to represent the different types of data. There is no restriction on mixing different kinds of object inside a collection of references.

  7. Step the first statement below tag A003.
  8. Run through the perform loop - click Debug > Run Thru..

    This executes the entire perform loop to initialize the array.

  9. Step the statements below tag A004.

    This retrieves the fourth element from the array (which has a value of 7) and displays it. Because a data item is returned, we can show it using the Display verb; when we got back objects in the previous example we had to send them the "display" message to show them.

  10. Click Debug > Stop Debugging to stop the Debugger.

This completes this part of the tutorial on using intrinsic data. For more information see the chapter Intrinsic Data.

Dictionaries

In this session you will debug some code that uses a dictionary to store account objects. The account objects are the ones that were introduced in the Inheritance Tutorial.

For more information about dictionaries, see the section Creating Dictionaries in the chapter Collection Frameworks.

In this section we introduce the Bank application. This provides a GUI front-end for creating and working on Account objects. The Bank application is used for the next few tutorials as it demonstrates many features of object-oriented programming with COBOL. In this part of the tutorial the Bank application is used to demonstrate use of a dictionary. Each account created is stored in the dictionary with the account number as its key. You are going to look at the code in the AccountManager class (accmgrd.cbl), which is responsible for storing and retrieving accounts using a Dictionary object.

To debug dictionary creation code:

  1. Start the Net Express development environment.
  2. Click File > Open, and open Examples\Net Express IDE\Bank\resBank.app.
  3. Rebuild the project.

    You are next going to find the methods in the AccountManager class we want to step through, and set breakpoints on them.

  4. Open the Browse window: click Search > Browse.
  5. Select the AccountManager class (it is a subclass of Base, so you will have to expand the entries below base).
  6. Load the "initialize" method into a text editor window: click on INITIALIZE in the Browser.
  7. Set a breakpoint on the statement below tag M002.
  8. Move to the "add" method: click on ADD in the Browser.
  9. Set a breakpoint on the first statement in this method.
  10. Move to the "getAccount" method.
  11. Set a breakpoint on the first statement in this method.
  12. Run the application: click Debug > Run.

    After a pause, the Debugger stops inside the "initialize" method of AccountManager.This method creates an instance of Dictionary, with PIC X(8) intrinsic data as the key and objects as the data.

  13. Step the first statement.

    This sets an object reference data item to null. This will be used to set the data template to hold objects (this is the meaning of a null template).

  14. Step the next two statements.

    This clones the CobolPicX class to create a template for strings eight bytes long. This is the key template.

  15. Step the statement below tag M003.

    You have now created an Association template for the key-data pairs to be stored in the dictionary.

  16. Step the statement below tag M004.

    This creates a dictionary which the AccountManager will use to store the account data.

  17. Run the program: click Debug > Run.

    The rest of this method creates another dictionary, and sets up an ExceptionHandler for the dictionary; exception handling is the subject of a later tutorial.

  18. Open a checking account for Fred, with a $10.00 balance:
    1. Click the first button on the Bankaccount toolbar.
    2. Fill the account details in on the Open Account dialog box.
    3. Click OK.
    4. Click OK in the message box.
  19. Step through the first statement.

    This is just a check that the method has been passed an object handle.

  20. Run through the next statement: click Debug > Run thru.
  21. Step the next statement.

    This stores the account in the dictionary using the account number as the key.

  22. Click Debug > Run.
  23. Retrieve account 10000000 (the one you just created):
    1. Click the third button in the Bank window
    2. Click OK in the Retrieve Account dialog box (this already has 10000000 in the Account Number field

    The Debugger stops inside AccountManager instance method "getAccount".

  24. Step the first statement of this method.

    This retrieves the account from the dictionary, and puts the reference in anAccount.

  25. Run the program.

    The BankApplication also opens the Account Details dialog box, which shows you the information held in the account object.

  26. Click Cancel to dismiss the dialog box.
  27. Click Debug > Breakpoints.
  28. Click Clear All In Program.

You have now completed this session. Don't shut down Debugger as the next session carries on directly from this one.

Iterator Methods

This session shows how you can use an iterator to search through the dictionary used by the bank application. The instructions below assume that you are continuing directly from the end of the previous section.

For more information about iterator methods, see the section Iterator Methods in the chapter Collection Frameworks.

To see the use of iterators:

  1. Set a breakpoint on the first statement of the "retrieve" method in AccountManager.
  2. Open a Savings account for Wilma.
  3. Click the second button on the Bank window toolbar to open the Enter Name dialog box.

    This function searches through the accounts looking for those with names that match.

  4. Type "Wilma" into the Name field and click OK.

    The execution point switches to the first statement of the AccountManager "retrieve" method..

  5. Step through until you reach the first statement below tag M050.

    This creates a string object containing the name for which you are going to search.

  6. Step the statement to create the Callback.

    This passes nameString as a parameter to the "new" message to create the Callback. Every time the method in the Callback ("searchAccounts") is invoked, it will get nameString passed to it ahead of any other parameters sent with the "invoke" message.

  7. Step the statement below tag M051.

    Execution jumps to the first statement of the AccountManager "searchAccounts" method. This has been passed the string object with the name we are looking for, and the first account in the collection.

  8. Run the application to the statement
    if acName = srchName
    .

    At this point, acName and srchName contain the name from the account and the name you are searching for. Both have been set to upper case to avoid case mismatches. If you double-click on acName, you will see that it contains "FRED".

  9. Step the next statement.

    The test fails.

  10. Step through until the Exit Method statement.

    This sets the level-88 item noMatch to true. When you exit the method, it will return a 0 to the code in the Dictionary "select" method which is performing the iteration.

  11. Step the Exit Method statement.

    Execution returns to the top of the "searchAccounts" method, as the collection invokes it with the next account.

  12. Step through the code for the method.

    The names match this time, as this is the account for Wilma. The method returns a value of 1 to the dictionary, so Wilma is added as an element to the collection of results being created.

    When you step the Exit Method statement, execution returns to the statement below tag M052. The result of the "select" is a new Dictionary, which contains only the elements which were marked as true (a value of 1 was returned by the selection Callback).

  13. Step the next statement.

    The "select" and "reject" methods always return a collection of the same type as the original collection, in this case a Dictionary. This message copies the data part of the dictionary into an OrderedCollection. The other statements in this method "finalize" all the objects created by this method which are no longer needed.

  14. Run the application.

    The application opens a dialog box with all the matching accounts shown in a list box.

  15. Cancel the dialog box and close the Bank application.
  16. Click Debug > Breakpoints. Click Clear All In Project.

You have now completed this part of the tutorial.

Summary

This tutorial covered the following:


Copyright © 2009 Micro Focus (IP) Ltd. All rights reserved.