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.