Article – Building A Better REPORT.PKG

Flexlines Volume IX Number 5 (a Data Access publication)

Building A Better REPORT.PKG

by Michael Steffano

With the advent of DataFlex 3.0's object orientation comes a much brighter possibility for the writing of 
truly sophisticated and classy programs.  Class inheritance is one of the keys to developing powerful tools 
that are easy to incorporate into everyone's programming arsenal.

As one of my first projects in object oriented development I was approached by a business client and 
fellow developer to create a powerful reporting package.  Having read many articles in FlexLines 
concerning this very topic, I thought this would be an easy task.  It turned out to be interesting and 
challenging, but not easy.

My first breakthrough came with obtaining 2E Software's 3.0 REPORT.PKG written by John Tuohy.  This 
excellent package contained within it many of the necessary building blocks I required.  And, since part of 
the package was written as a class, I could easily add its functionality to the new package (which I'll refer 
to as NEW_RPT.PKG) through class inheritance.  In fact, Tuohy had already written several examples 
that used class inheritance.  Here is an example of class inheritance:

	Class Report is an Edit  (from REPORT.PKG using a 3.0 class)
	Class New_Report is a Report (from NEW_RPT.PKG)

Some of  the elements of what I considered necessary for a world-class report package were:
	* a consistent visual appearance easily manipulated by the programmer
	* totally hidden inner mechanics
	* easy rewrite incorporation for existing programs
	* simultaneous multiple channel output (screen, file, printer)
	* report interruption and subsequent viewing of the partial report

A tall order but I felt 3.0 was up to the task.  The key to the project was the starting point.  As mentioned 
above, Tuohy's REPORT.PKG allowed me to jumpstart the project; in one fell swoop giving 
NEW_RPT.PKG a consistent powerful reporting mechanism by which an application programmer could 
immediately become productive.  This is the promise held out to us by object-oriented programming: 
using powerful tools to develop complex programs and even more powerful tools.  As one of the 
converted, believe me IT WORKS!

By using Tuohy's REPORT.PKG I was able to concentrate on the user interface, output device selection, 
management of the user interface during report processing, and simultaneous multiple channel output.  
Still a challenge!

USER INTERFACE
I saw no reason to limit the application programmer to my idea of a report input screen.  For this reason, 
the programmer was left responsible for creating his or her parameter selection screen.  These 
parameters are then used when creating the actual program logic for report selection.  This level of 
program design takes place within the functionality of Tuohy's REPORT class, so I had little to do here 
(thankfully).  However, once parameters were selected it became time for output device selection and 
direction.  Now NEW_RPT.PKG's functionality came into play.  One quick word on packages and 
classes.  A package can hold more than just a class (and typically does).  NEW_RPT.PKG has within it 
multiple classes, objects, global variables, and of course, documentation.

OUTPUT DEVICE SELECTION & DIRECTION
By designing a sub-class from Tuohy's REPORT class I was able to augment or override any of his 
internal methods (procedures and functions).  This ability makes it easy for me to intercept his 
RUN_REPORT message (which is sent by the application programmer's logic as the last step in report 
initiation).  At this point an object responsible for obtaining output device selection and direction 
information is invoked.  Since this is all contained within NEW_RPT.PKG, the programmer does not need 
to know anything about it's function or how it is invoked (unless he or she wants to).  All the programmer 
has to do is include the USE NEW_RPT.PKG command.  There is the possibility that the screen 
appearance of the Output Device Selection object may not harmonize with the program's screen 
appearance.  This is easily remedied by editing NEW_RPT.PKG and changing the appearance of the 
object's screen.   This object (which is actually a client) contains within it a check box, entry form, and 
buttons.  The check box is used to toggle select the printer and file output devices built into 
NEW_RPT.PKG.  As part of the package's operation, output is always directed to the screen, thus giving 
the user a consistent visual interface  (and obviating the programmer's need to display some type of  
customised "busywork" screen).  As a device is selected the default device name is displayed in the entry 
form object.  The programmer has the ability to default choices here (such as report file name, printer 
device) from within the application program through the use of class properties.  The button object 
contains confirmation and cancel buttons from which program control may be passed on to the heart of 
the new report class.

REPORT PROCESSING (or Let's Write A Virtualized Edit Object)
This is the real heart of the package.  The user not only gets an ongoing visual display of the report as it 
is processing, but also has the ability to interrupt the report at any point and VIEW the entire completed 
portion of the report.  Upon first approach, I thought this would be easy.  I felt the VConsole and Edit 
classes  had all the functionality necessary.  The virtual console would be used to give the user a dynamic 
display of the report as it processed.  This worked great until wide (greater than 80 column) reports were 
tried.  You guessed it: screen wrap!  The goal of  creating a world-class package made this a real thorn in 
my side.  As there currently is no property SCREEN_WRAP_MODE for the VConsole class (hint, hint 
DAC) I ended up being saved through a trick:  if you Activate, then DeActivate, and finally Activate again 
the same VConsole object the text will no longer wrap.  Frankly I don't know why this is, but it works great 
on 132 column virtual consoles (I have not tried on it on smaller ones).

     Object VC is a Vconsole
       set Size to 24 132  // use full screen
       Set Location to  0 0   
     End_Object
     Send Activate to (VC(Current_Object))  // sets it up
     // This command sends all screen IO to the virtual console.
     Send virtual_console to (VC(Current_Object))
     // These next two lines take care of the word wrap problem
     Send DeActivate to (VC(Current_Object))  // 
     Send Activate to (VC(Current_Object))  // sets it up

Another requirement was allowing the user to be able to view the report during any point in the 
processing by entering into an EDIT object.  The Edit class would give the user full cursoring and 
scrolling/paging functions.  One of  the basic design premises had report output always directed to a file.  
This allows the package to close the reporting file and reopen it as an input stream to the Edit object (the 
Edit class supports this option).  This was great except one large problem surfaced: the Edit object is 
RAM-bound.  That is, there is a limitation of how many lines of text it can hold (32767 or less).  For small 
reports this isn't much of a problem, but in larger ones the user would not be able to view all of the 
completed report  (which was one of the package's requirements).  It was decision time: live with the 
limitation or modify the Edit object's behavior (and incur a large amount of programming overhead)?  
That's another nice thing about 3.0- you almost always have choices.  I decided to create a virtualized 
edit class, that is, one which would work on any size file.  Since NEW_RPT.PKG  was now in control of 
the edit object's window into the report file, its MAX_LINES property was decreased from 32767 to only  
24, thus conserving precious conventional memory.  However, now all the functions taken for granted 
when using the Edit class: top of file, bottom of file, page up, page down, scroll up, scroll down, scroll left, 
scroll right, etc. had to be programmed.  This was probably the hardest part of the project and was 
accomplished with a fair degree of effort.   

SIMULTANEOUS MULTIPLE CHANNEL OUTPUT
3.0 brings with it powerful new I/O capabilities through the CHANNEL command.  Rather than rehash 
them here, suffice to say multiple I/O channels can be easily managed in 3.0.  As part of the package 
design, I wanted the user to be able to direct output to the printer, screen, and file at the same time if 
desired, rather than having to rerun the report multiple times for each output device.  The screen always 
gets output to as part of the user visuals, while as mentioned above, the class makes use of a report file 
to allow report reviewing.  The report file is not retained unless explicitly requested by the user during 
output device selection.  This leaves only the printer device.  One of the many design choices 
encountered was whether to output the same data to all three output devices.  Should a report file or 
screen display contain formfeed characters, page number headings, multiple footers, etc.?  The decision 
was reached that only the printer should contain these which made for more programming work.  The 
class had to be able to know which output devices were active and decide what to print (or not print) to 
each of them.  The hardest part of this task was managing the line counts and augmenting or overriding 
internal methods in Tuohy's REPORT.PKG. Fortunately, the CHANNEL command is smart enough to 
maintain a separate LineCount variable for each channel opened, thus when you change channels 
DataFlex will automatically reset LineCount to that channel.  

     // Direct output to the named file
     direct_output channel FILE_CHANNEL (File_Name(EC(Current_Object)))
     // Direct output to the console
     direct_output channel CONSOLE_CHANNEL 'CON:'
     // If printer is selected direct output to it also
     If (To_Printer_State(Current_Object)) begin
        direct_output channel PRINTER_CHANNEL 'LST:'
        send Initialize_Printer  // designed for override
        end
     direct_output channel FILE_CHANNEL   // this is the primary channel

So there you have it: one world-class report package riding "piggyback" on top of another world-class 
report package, adding functionality and versatility all via the use of the USE command.

A special thanks goes to David King of King Business Systems for his technical review, programming 
advice, and monetary support during this development process.  Also, thanks to John Tuohy for his 
REPORT.PKG and technical advice.

Michael D. Steffano is a long-time DataFlex developer, and creator of ExReport.pkg.