How SCL Programs Execute for FRAME Entries

Introduction

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
  • initializing variables
  • importing values through macro variables
  • displaying initial messages on the window's message line
  • 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

Introduction

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 the SAS Component Language: Reference and the _setControl method of the Frame class in the SAS/AF online Help.

Order of Processing for Multiple Window Components

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.

Example: Order of Processing for Multiple Window Components

Consider the following FRAME entry:
Frame with several fields
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

Introduction

Statements in the MAIN section typically perform actions such as:
  • validating field values
  • calculating values for computed variables that are based on user input
  • displaying selection lists that are developed through SCL functions
  • submitting Base SAS software code
  • invoking secondary windows
  • querying and executing commands that are issued by users
  • retrieving values from SAS tables or from external files
By default, MAIN executes each time the follows occurs:
  • 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.)
  • you activate a control by modifying or selecting it.
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 the SAS Component Language: Reference.)

Forcing MAIN to Execute

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 the SAS Component Language: Reference for information about CONTROL statement options.

Handling Invalid Values in MAIN

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

Introduction

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
  • submitting Base SAS software code for execution
  • 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.

Processing an END Command

If you issue the END command,
  • The SCL system variable _STATUS_ is set to E.
  • Modified fields are checked for valid values.
  • 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.
  • Any SCL statements in the TERM section execute.
  • The _term method runs for the frame and for all components.
  • 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.

Processing a CANCEL Command

If you issue the CANCEL command,
  • The SCL system variable _STATUS_ is set to C.
  • No field validation is performed.
  • Sny SCL statements in the TERM section execute.
  • The _term method runs for the frame and for all components.
  • The window closes and control returns to the parent or calling entry.

Processing an ENDSAS, BYE, or System Closure 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:
  • The SCL system variable _STATUS_ is set to ' ' (blank).
  • Statements in the TERM section do not run.
  • The _term method runs for the frame and for all components.
  • The window closes.
Note: The SAS AWS is the container window for SAS software.

Automatic Termination of SCL Objects When an Application Ends

Introduction

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.

Using the AUTOTERM= Option to Control the AUTOTERM Feature

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
ON
enables the AUTOTERM feature. AUTOTERM is on by default. Use this value to enable the feature if it is turned off.
OFF
disables the AUTOTERM feature, which means that the software does not invoke the _term method on objects when a task is terminated. This value makes the AUTOTERM feature compatible with earlier releases of SAS/AF software.
VERBOSE
prints a note and dumps the object list of each object that still exists when a task is terminated. This value works even if AUTOTERM is OFF; it serves as a debugging aid to identify which objects still exist but whose _term method has not run.
NOVERBOSE
disables the VERBOSE option. NOVERBOSE is the default.
You cannot combine options in one string; use separate AUTOTERM= options on the command line, or use separate AFSYS commands.

Using the VERBOSE Value to Help Debug Your Application

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.