Flow of Control |
SCL programs for FRAME entries use code sections that are named with reserved labels to process the phases of program initialization (INIT), main processing (MAIN), and termination (TERM).
Processing the INIT Section |
Statements in the INIT section typically perform actions such as
opening SAS tables and external files that are used by the entry
processing parameter values that are passed to the FRAME entry.
By default, INIT executes only once, before the entry's window opens, for each invocation of a FRAME.
Processing Labeled Sections for Components |
Sections of frame SCL programs that are named with object labels execute when an action is performed on the component, such as selecting the component or changing its value. The CONTROL LABEL statement in SCL controls the execution of labeled sections. CONTROL LABEL is on by default for FRAME entries, making it easier to create labeled statement sections to execute when you perform an action on a component. For details on the CONTROL LABEL statement, see SAS Component Language: Reference and the _setControl method of the Frame class in the SAS/AF online Help.
When a frame needs to process more than one component, a predefined order (known as the window order) controls the processing order. The window order starts from the top row of the window and moves to the bottom row. Additionally, window order moves from left to right in each row.
However, if one component (such as a container box) contains other components, then all the contained components are processed as a group before other components on the right are processed.
Some hosts and display devices also honor the window order when you use the TAB key to move the cursor between fields. See "Setting the Tab Order" in the SAS/AF online Help for more information.
Note: There is an exception to the window order. When the _preTerm, _postInit, and _refresh methods are run, all extended tables in the frame are processed before any other components.
Consider the following FRAME entry:
The order of processing for this window is FIELD1, FIELD2, FIELD3, FIELD4, FIELD5, FIELD6, BOX, FIELD7, FIELD8, FIELD9, FIELD10, TABLE, FIELD11, FIELD12, FIELD13, and FIELD14.
Note carefully the order of processing components in the container box labeled Box and the extended table labeled Table, as well as the fields within them. For example, FIELD10 is not processed until after FIELD7, FIELD8, and FIELD9 are processed because they are contained in Box, which appears before FIELD10. Also, FIELD11 and FIELD12 are processed before FIELD13 because they are contained in Table, which starts in the row above FIELD13.
Processing the MAIN Section |
Statements in the MAIN section typically perform actions such as
calculating values for computed variables that are based on user input
displaying selection lists that are developed through SCL functions
By default, MAIN executes each time
a user presses ENTER or RETURN (or any function key) after modifying one or more fields or text entry controls, provided that the modified fields contain valid values. After you modify fields and press ENTER, all modified fields are checked to verify that their values satisfy their attribute specifications. (However, the required attribute is not checked until you attempt to end from the entry.)
Each of these actions first runs the labeled section that is associated with the object and then runs MAIN. The new values of the window variables are then displayed in the corresponding fields.
To override the default behavior of when MAIN executes, use the CONTROL statement in SCL. (For details, see SAS Component Language: Reference.)
Although the default behavior is for MAIN to execute only when all field or component values are valid, you can force MAIN to execute even when some fields or components contain invalid values or when an application-specific command is issued. SCL provides CONTROL ENTER, CONTROL ERROR, CONTROL ALLCMDS, and CONTROL ALWAYS statements that cause the MAIN section to execute whenever you press ENTER or RETURN, even if no fields are modified or if modified fields do not contain valid values. If the FRAME entry contains no selectable or modifiable objects, then MAIN cannot execute unless a CONTROL ENTER or CONTROL ALWAYS statement is in effect.
To enable MAIN to execute even if a field is in error, you must specify CONTROL ERROR or CONTROL ALWAYS. The _setControl method of the Frame class also modifies how the FRAME entry processes errors and input events.
See SAS Component Language: Reference for information about CONTROL statement options.
If a modification to a text entry control introduces an attribute error, the labeled section for that control does not execute. However, the labeled sections for other controls that are also modified will execute.
If an attribute error is detected, MAIN does not execute. Instead, fields or controls that contain invalid values are highlighted and an error message is displayed. The error attributes that you specified determine what is flagged as an error. You can enable users to correct an attribute error in the window. If the program contains CONTROL ERROR and CONTROL LABEL, or if the _setControl method has put these statements into effect, you can include statements in the labeled section that make a correction, as shown in the following example:
INIT: control error; return; TEXTENTRY1: if error(textentry1) then do; textentry1.text=.; erroroff textentry1; _msg_='Value was invalid. It has been reset.'; end; return;
Processing the TERM Section |
Statements in the TERM section typically perform actions such as
updating and closing SAS tables and external files that were opened by the entry
exporting values of SCL variables to macro variables for later use
branching to another entry in the application by using the DISPLAY routine
deleting SCL lists or SCL objects that are created in the FRAME entry.
TERM executes when you issue either an END command or a CANCEL command.
statements in the MAIN section execute if fields are modified and their values are valid
required fields are checked to verify that they are not blank
the window closes and control returns to the parent or calling entry.
Note: If any fields contain invalid values when MAIN executes, MAIN processing highlights the errors and returns control to the application. In that case, TERM does not execute.
If any empty fields have the required attribute, TERM does not execute. Instead, the empty required fields are highlighted and an error message is displayed. You must provide values for all required fields and issue the END command again before the TERM section will execute.
After all these conditions are satisfied, the SCL statements in TERM execute. After the TERM section executes, the window closes and the entry terminates. Control returns to the SAS process that invoked the entry.
If you issue the CANCEL command,
If you issue the ENDSAS or BYE command, or if you use the system closure menu when the FRAME entry is running without the SAS Application Work Space (AWS), then
Note: The SAS AWS is the container window for SAS software.
Automatic Termination of SCL Objects When an Application Ends |
When an application ends, the software scans for any remaining SCL objects that have not yet been deleted and sends a _term method to them. In most cases, this is completely benign. In a few cases, it might indicate a defect in your application; that is, perhaps you did not delete something that you should have, or perhaps your _term method omitted a SUPER(_self_, '_term_'); .
Sometimes an application cannot delete an object because it does not know whether the object is still in use; the AUTOTERM feature deletes such objects by running the _term method.
If the AUTOTERM feature causes problems in your application, you can disable it by adding the AUTOTERM= option to your AF, AFA, or AFSYS command:
AF C=lib.cat.member.name AUTOTERM=term-value AFA C=lib.cat.member.name AUTOTERM=term-value AFSYS AUTOTERM term-value
(You can use the AFSYS command once an application is running.) Values for term-value are
You cannot combine options in one string; use separate AUTOTERM= options on the command line, or use separate AFSYS commands.
Use the VERBOSE value when you invoke your application or while the application is running. With VERBOSE, the software displays a note about any object that still exists at task termination, and it dumps the object list to the log before sending the _term method. Without VERBOSE, _term is invoked and no note is displayed in the log. Using VERBOSE as you develop your applications is helpful because it can highlight objects that your application fails to delete.
In a few rare cases, you might begin to see program halts as your application completes. These halts are often caused when one object is deleted and another object that references it does not know that it has been deleted. For example, consider a class named Manager that maintains a list of instances of Message File objects. The Manager class has a _term method that unconditionally sends _term to each item in its list of Message File objects:
term: private method; dcl num i; do i=1 to listlen(msgObjs); dcl object msgObj; msgObj=getitemn(msgObjs,i); msgObj._term(); end; _self_._super(); endmethod;
However, when a task ends, AUTOTERM may send _term to a Message File object before running the Manager's _term, so the Manager's list becomes stale (that is, it contains object identifiers of deleted Message File objects). The proper fix in this and similar situations is to verify other dependent objects in the _term before sending methods to them. (In this case, the Message File object is not aware of the Manager and therefore cannot ask the Manager to remove the object identifier from the Manager's list of Message File objects.)
To determine whether a number is a valid list identifier, use
LISTLEN(number) > -1
Then, to determine whether a list identifier is an object identifier, use
HASATTR(listid, 'O')
(The 'O' is a letter O as in Object.) For example, the Manager's _term method is more correctly implemented as follows:
term: private method; dcl num i; do i=1 to listlen(msgObjs); dcl object msgObj; msgObj=getitemn(msgObjs,i); if listlen(msgObj) > -1 and hasattr(msgObj,'O') then msgObj._term(); end; _self_._super(); endmethod;
Note: As an alternative to the above solution, when _term is run on a Message File object, the Message File object could send an event. The manager would then have a handler for the event and could remove the object from its list.
Copyright © 2007 by SAS Institute Inc., Cary, NC, USA. All rights reserved.