This paper discusses the use of Level Breaks in LANSA. LANSA is a sequential language unlike RPG and as such the ability to perform calculations when a value changes is not intrinsic to LANSA. Despite this, LANSA does provide that facility through the use of report BREAKS and associated sub-routines. This functionality can be very powerful. However, it does require a knowledge of BREAK handling by LANSA. This paper attempts to provide an appropriate level of knowledge to utilize BREAK handling to its fullest.
A common theme during the production of reports is the presence of sub-totals, and other segmentation of the details contained on the report. In older forms of RPG these were referred to as level breaks. Often they are accompanied by special processing.
In cyclic languages, such as RPG, there is typically a built in capability to determine when a level break has occurred and to take special action at that time. The resulting code is very simple and might look something like the following:
where the L1 and L2 are indicators used to determine that a break has occurred. In sequential languages, such as RDML, C, COBOL, and BASIC, in order to provide that functionality it is necessary to code the break checking using brute force. The resulting pseudo-code might look like the following:
As you can see, the resulting code is considerably more verbose than the cyclic language. As the number of levels increases, the complexity increases exponentially. If printing is involved then the processing becomes even more complex as it becomes necessary to ensure that the correct version of data is printed. Examples of this are left to the detailed logic presentation.
LANSA provides a capability to perform sub-totals for printing. It does this through the DEF_BREAK statement. By providing both before details (*LEADING) and after details (*TRAILING) versions, LANSA is capable of providing most normal report requirements for break handling. By combining this with the KEEP_xxxxx commands and the KEEP_LAST parameter in the FETCH command it is possible to perform the most common break handling functions without having to code any special break handling.
However, there are many cases where more complex coding beyond a simple file read and/or a print line is required.
There are three basic solutions to providing this:
Ignoring the level break is not as foolish as it seems. Often the only reason for doing the code at level break time is because of a total needing to be cleared at break time or processing efficiency. LANSA provides a solution for each of these. In order to calculate totals which need to be cleared at breaks LANSA provides the KEEP_xxxxx commands. There are several versions which will keep counts, totals, averages, maximum and minimum and then reset when a set of fields change. By judicious use of the SELECT statement and the KEEP_xxxxx commands it is possible to do most sub-total calculations with minimal coding. Efficiency of code is often a misused term. The primary source of coding (in)efficiency is I/O. Only if there is a high volume of code AND a high volume of transactions, will non-I/O commands materially affect the execution time of the code. LANSA provides the ability to retain the last x number of records fetched. This eliminates most of the I/O as a source of level break inefficiency. Very little will be achieved by trying to remove the remaining non-I/O command inefficiencies and the cost of testing will often exceed the cost of performing the commands.
Sometimes it is not possible to ignore the level break. This may occur if special page handling is occurring or if a SELECT within a select is occurring or for many other reasons. If there is only one level break involved the brute force method, discussed above, may be appropriate. Generally, this involves the retention of prior values of the level break fields and comparison to the current values.
If there are a large number of breaks, then it may be appropriate to use the Sub-Routine parameter within the DEF_BREAK command. This parameter assigns a sub-routine to be initiated whenever the break line is to be printed. These sub-routines have a number of special restrictions:
Only the first restriction is enforced. The others can be performed if the process is sufficiently understood. HOWEVER, do not execute other subroutines and do not perform screen panel interactions. In the former, it is too easy to lose track of what is included in the break subroutine. In the later, it is too easy to cause invalid data to be printed.
This paper will attempt to give you sufficient information to understand the capabilities and limitations to field manipulations and printing which gives rise to those restrictions. Once you understand the reason for the rules you will know when to ignore them.
A Simple Example:
Let’s start with a simple example, a function which will read a file with three fields, print the fields, and print a sub-total line and a batch header line when one of the fields changes and a new page when the other changes. The RDML would look something like this:
Notice that there is a logic error. The final totals will not print because there is an ENDPRINT missing. This has been left off for clarity sake.
Let's look at how LANSA would compile this into pseudo code .... (click on the object to see a larger version) ...:
An interesting thing to observe in this is that while the Detail line (#LINE) and the Leading Break line (#BEFORE) print the current values of the fields, the Trailing Break line (#AFTER) actually prints a work field which was set on the previous cycle. Another interesting point is that overflow testing occurs at the beginning of each DEF_xxxxx command. Notice that in the Trailing Break Header code, the KEYA test is actually testing the prior KEYA and the current saved KEYA. Therefore, only the line level breaks will actually work. In this way, page breaks (Header lines) other than overflow will print only after the Trailing Break lines have printed. Finally notice the order of executing the print lines: *TRAILING, *LEADING, Detail.
Using A Subroutine
Now for a slightly more complex model. In this one we will add a subroutine to the break line and a value to be printed which will be set in the subroutine. We will also add two commands to illustrate where main line commands occur. The RDML code would look as follows:
Let's look at how LANSA would compile this into pseudo code (changes are highlighted).... (click on the object to see a larger version) ...:
Notice a number of interesting conclusions about this code which explains why LANSA recommends that some things should not be done. Notice how the page overflow always occurs at the beginning of the command group. Therefore, if a Break subroutine prints, the overflow capability no longer exists. Also notice how the Trailing Break logic manipulates fields. By printing the "_after" versions of the fields it ensures that the previous version of the field will be printed. However, it also means that any printed fields must be updated after the Break subroutine and any previous versions reset to their current version. This is why there is a save and restore of all fields before and after the break subroutine.
Notice, however, that the restore creates a number of important situations. It effectively prevents the communication from the subroutine to the rest of the function. Thus if you read a file within a Trailing Break subroutine any data received will not be passed along. Similarly, if you wish to set on a flag (e.g., force a page feed), the flag will not be passed to any subsequent commands. Notice also that the only fields passed to the subroutine correctly are those set within the subroutine (e.g., by a FETCH) or those being printed. This includes the page control information (e.g., current line which becomes the last line printed by a normal print line).
Another interesting observation is that the field manipulation related to Trailing Breaks does not occur for Leading Breaks. This means that Leading Break subroutines are not subject to the same data manipulation and communications restrictions as Trailing Breaks. So if we need to set on a flag such as a force page feed, we need to do it during a Leading Break subroutine.
Also observe that the processing of Break subroutines occurs after the page header logic, and that the page header logic is repeated for at the beginning of each print definition. This is why LANSA recommends that you not try to print anything inside a Break Subroutine. Notice also that header logic will occur once per DEF_xxxxx no matter how many print lines are in the DEF_xxxxx. Note that the actual generated code calculates the overflow so that all lines in a single DEF_xxxxx are printed together.
Observations & Applications:
Examples of Application: Comparision of two solutions to "orphan" totals.
Using the above code, if a detail line prints on the last line the totals will print on the next page without and the user may not be able to determine what KEYB they go with.
Example 1: The simple way.
Print the headers regardless of breaks, if a new page is to be printed.
Example 2: The "Full Blown" way.
Don’t print the headers and detail on the current page if the total can’t be printed. If the line is to be printed on a new page then (re)print the headers.
(NOTE: The following code is simplified. Each report will need to be debugged in order to detemine the actual values to use. Also in a general report, the PAG_BEF code will need to split into PAG_BEF and PAG_AFT in order to allow for multiple total lines etc.)
Example 3: The "LANSA" way.
Put the above code into the "PERFECT" Lansa solution (assuming no one really cares how the problems are solved). Use the power of LANSA rather than forcing RDML. Use alternate solutions where necessary.
Comparison of Examples:
Copyright © 1999 & 2000 Glen Ford & Can Da Software div of 1112993 Ontario Inc
Send mail to firstname.lastname@example.org with
questions or comments about this web site.