Example: Creating An Object-Oriented Application in SCL |
So far you have created stand-alone SCL classes. SCL class syntax can be used to create SAS/AF visual objects.
This exercise will extend the SAS/AF List Box class. The readers that were previously developed in this tutorial will be used to read data that will be used to initialize the items in the List Box.
To extend the List Box class, you must write a class to extend List Box and modify the READ class that was created in Other Classes and Further Abstraction. The READ class will then be able to pass the new class to the list box to use for initializing the item list instead of having it simply print the character data after reading it.
You must create a new interface and make a minor modification to the LOOP method in READ.
The interface has a single method, CALLBACK:
interface call; callback: method s:string; endinterface;
The modified LOOP method in READ is
loop: method caller:call; do while(i.more()); caller.callback(i.next()); end; endmethod;
The method now takes a CALL interface parameter and calls its CALLBACK method.
Note: You do not specify the implementation of a method in an interface; you simply supply the name and parameter list.
It is not necessary to know what the implementation for CALLBACK is at this point, only that you call it in the read loop and pass a character value to it. Whatever class supports the interface will supply the implementation for CALLBACK.Now, create the extended List Box class (depending on which version of SAS you have, you may need to create an empty MLIST class first in order for the following to work).
import sashelp.classes; class mlist extends listbox_c supports call; /* Local item list */ private list listid; /* Set method */ set: method r: read; listid = makelist(); r.loop(_self_); endmethod; /* Store the character value in the local list */ callback: method s:string; rc = insertc(listid, s, -1); endmethod; /* Set the items attribute */ setattr: method; _self_.items = listid; endmethod; endclass;
Note how the IMPORT statement and the LOOP, SET, SETATTR, and CALLBACK methods will be used:
The IMPORT statement defines a search path for CLASS entry references in an SCL program so that you can refer to a class by its two-level name instead of having to specify the four-level name each time. It is used to specify a catalog to search for abbreviated class names. For example, the MLIST class extends LISTBOX_C, but if LISTBOX_C is not in the current catalog, the compiler will not know where to find it. The IMPORT statement tells the compiler to search the SASHELP.CLASSES catalog for any classes it cannot find in the current catalog.
The SET method is used to set up a local list that will hold the new set of items for the list box. It will also call the LOOP method in READ, with MLIST's object as a parameter. Recall that MLIST supports the CALL interface, so this will work with the new LOOP method that was created above.
As the LOOP method executes, it will call the CALLBACK method for each character variable that it reads. The CALLBACK method will store the variable in the local list that was created in the SET method.
Finally, the SETATTR method will assign the local list to MLIST's item list, thus changing the list of items seen when the List Box, which is actually MLIST, is displayed.
To see how this works, create the CALL interface, as well as the classes READ and MLIST, by using the SAVECLASS command. Then edit a frame. In the Components window, add the MLIST class to the class list (via AddClasses on the pop-up menu). After it appears on the list, drag and drop MLIST to the frame. In the frame's source, enter
init: dcl ddata d; dcl read r; dcl string filename = "sasuser.x"; d = _new_ ddata(filename, "i", 2); r = _new_ read(d); mlist1.set(r); mlist1.setattr(); return;
This will create a DDATA reader with an associated READ class. Now call the SET and SETATTR methods on the new List Box class (MLIST).
Compile and use the TESTAF command on the frame. The initial list of items will be
San Francisco Honolulu New York Miami
Flexibility |
Using the CALL interface in the above exercise allows a great deal of flexibility in modifying the MLIST and READ classes.
For example, to process numeric data instead of character data, you could simply overload the CALLBACK method in the interface
interface call; callback: method s:string; callback: method n:num; endinterface;
and support it in the MLIST class
callback: method n:num; /* process numeric value */ endmethod;
Now, the READ class - or any class that supports CALL - can call the CALLBACK method with a numeric parameter. Clearly, this process can be generalized to make use of any possible parameter lists that are needed for CALLBACK.
Another feature is that any class that supports the READER interface can be used to read the data into the list box. For example, to use an external file, change the frame's SCL to
init: dcl fdata f; dcl read r; dcl string filename = "some_file"; f = _new_ fdata(filename, "i"); r = _new_ read(f); mlist1.set(r); mlist1.setattr(); return;
We can consolidate the code further by creating another class to set up the reader:
init: dcl SetReader g = _new_ SetReader(); dcl read r = g.get(); mlist1.set(r); mlist1.setattr(); return;
SetReader sets up whatever reader is necessary even if your program is using external data. Then, at the frame level, you can read from any type of data source, such as a data set, an external file, an SCL list, or any other user-defined data source. The only requirement is that SetReader support the reader interface.
Copyright © 2009 by SAS Institute Inc., Cary, NC, USA. All rights reserved.