In this section: |
How to: |
The DESCRIBE command enables you to define and work with objects (called classes) that cannot be defined using the standard set of classifications. Standard classifications include data types such as Alphanumeric, Numeric, and Integer (a subset of Numeric).
A class can represent a data type synonym. You can assign a name to a specific data type and then use this name as the format specification for variables. In this way, you can change the formats of multiple variables by changing the class definition.
A class can also represent an object consisting of other objects, called components of the class. You can define two types of components for a class, fields and functions (cases).
The DESCRIBE command defines the structure and behavior of a class. You then use the COMPUTE or DECLARE command to create an instance of the class. The COMPUTE command defines a global instance of the class. To create an instance that is local to a specific case, use the DECLARE command within that case.
To reference a component of a class instance, qualify the name of the component with the class instance name, separating them with a period (.). For example, consider a class called Room, which has components length and width. You can create a class instance named MyRoom in a COMPUTE or DECLARE command. For example:
COMPUTE MyRoom/Room;
To reference the length component of the MyRoom instance, qualify the component name with the class instance name:
MyRoom.length
This is similar to qualifying a field name in a data source with its file or segment name.
Within the DESCRIBE and ENDDESCRIBE commands that define a class, you do not qualify component names for that class.
You must issue the DESCRIBE command outside of a function (case), for example, at the beginning of the procedure prior to all functions.
The syntax of the DESCRIBE command to define a new class is
DESCRIBE classname = ([superclass +] memvar/type [, memvar/type] ...) [;] [memfunction [memfunction] ... ENDDESCRIBE]
where:
Is the name of the class that you are defining. The name is subject to the standard naming rules of the Maintain Data language. See Specifying Names for more information.
Is the name of the superclass from which to derive this class. Used only to describe a subclass.
Names one of the member variables of the class. The name is subject to the standard naming rules of the Maintain Data language. See Specifying Names for more information.
Is a data type (a built-in format or a class).
Defines one of the member functions of the class. Member functions are defined the same way as other Maintain Data functions, using the CASE command. See CASE for more information.
For class definitions, this terminates the definition if the definition omits member functions. If it includes member functions, the semicolon is omitted and the ENDDESCRIBE command is required.
For synonym definitions, this terminates the definition and is required.
Ends the class definition if it includes member functions. If it omits member functions, the ENDDESCRIBE command must also be omitted, and the definition terminates with a semicolon (;).
The syntax of the DESCRIBE command to define a synonym for a data type is
DESCRIBE synonym = datatype ;
where:
Is a synonym for a data type (a class or format). The synonym is subject to the standard naming rules of the Maintain Data language. See Specifying Names for more information.
For class definitions, this terminates the definition if the definition omits member functions. If it includes member functions, the semicolon is omitted and the ENDDESCRIBE command is required.
For synonym definitions, this terminates the definition and is required.
Data type synonyms can make it easier for you to maintain variable declarations. For example, if your procedure creates many variables for names of people, and defines them all as A30, you would define a data type synonym for A30:
DESCRIBE NameType = A30;
You would then define all of the name variables as NameType:
DECLARE UserName/NameType; COMPUTE ManagerName/NameType; DECLARE CustomerName/NameType;
To change all name variables to A40, you could change all of them at once simply by changing one data type synonym:
DESCRIBE NameType = A40;
The following DESCRIBE command defines a class named Room in an architecture application. The components of the class are three fields, Width, Height, and Length:
DESCRIBE Room = (Width/I4, Height/I4, Length/I4);
The following COMPUTE command creates an instance of the Room class named abc and assigns values to the components, qualifying each component with the class name:
COMPUTE abc/Room; abc.Width = 10; abc.Height = 20; abc.Length = 30;
Once the instance is created, you can use it in other Maintain Data commands. For example, the following TYPE command types each component value:
TYPE "Width=<abc.Width Height=<abc.Height Length=<abc.Length"
Reference: |
Functions included within a class definition specify operations that can be performed using the components of the class.
Two function names, StartUp and CleanUp, are reserved and have specific uses.
If you define a case called StartUp, that case is executed whenever an instance of the class is created. A global instance is created at the beginning of the Maintain Data procedure. A local instance is created each time the case in which it is declared is performed.
If you define a case called CleanUp, that case is executed whenever an instance of the class reaches the end of its scope. The scope of a global instance ends after execution of the Maintain Data procedure. The scope of a local instance ends each time execution returns to the procedure that performed the case in which it was declared.
You can create a global instance of a class using the COMPUTE command anywhere in the Maintain Data procedure. To create an instance local to a specific case, use the DECLARE command within that case.
You can use the Startup case to assign initial values to the components of a global instance of a class.
To pass initial values for class components to the Startup case:
COMPUTE MyRoom/Room(15,10);
[COMPUTE|DECLARE] MyRoom/Room; MyRoom.length = 20; MyRoom.width = 15;
Just as you can reference components of the class by qualifying the component names with the class instance name, you can execute a function within the class by qualifying the function name with the class instance name. For example, if the Room class contains a function called FINDAREA that takes the arguments length and width and returns the area, you can execute this function and type the returned value with the following commands:
AREA/I4 = MyRoom.FINDAREA(MyRoom.length, MyRoom.width) TYPE "AREA = <AREA";
If the function operates on the components of the class, those components are available to the function without passing them as arguments. Therefore, if length and width are components of the Room class, the FINDAREA function does not need to take any arguments. In this case, you invoke the function as follows:
AREA/I4 = MyRoom.FINDAREA() TYPE "AREA = <AREA";
Note that parentheses are required when invoking a member function even if the function does not take arguments.
The DESCRIBE command in the following Maintain Data procedure defines a class named Floor in an architecture application. The components of the class are three fields (Length, Width, and Area) and one case (PrintFloor). The COMPUTE command creates an instance of the class named MYFLOOR, assigns values to the components, and calls the PrintFloor function. Although the PrintFloor function does not take arguments, the parentheses are needed to identify PrintFloor as a function:
MAINTAIN DESCRIBE Floor = (Length/I4, Width/I4, Area/I4) CASE PrintFloor TYPE "length=<Length width=<Width area=<Area"; ENDCASE ENDDESCRIBE COMPUTE MYFLOOR/FLOOR; MYFLOOR.Length = 15; MYFLOOR.Width = 10; MYFLOOR.Area = MYFLOOR.Length * MYFLOOR.Width; MYFLOOR.PrintFloor(); END
The output is:
length=15 width=10 area=150
The DESCRIBE command in the following Maintain Data procedure defines a class named Floor in an architecture application. The components of the class are three fields (Length, Width, and Area) and one case (PrintFloor). The COMPUTE command creates an instance of the class named MYFLOOR, passes values for the components to the Startup case, and calls the PrintFloor function. The Startup case initializes the component fields with the values passed in the COMPUTE command:
MAINTAIN DESCRIBE Floor = (Length/I4, Width/I4, Area/I4) CASE Startup TAKES L/I4, W/I4, A/I4 Length = L; Width = W; Area = A; ENDCASE CASE PrintFloor TYPE "In PrintFloor: length=<Length width=<Width area=<Area"; ENDCASE ENDDESCRIBE COMPUTE MYFLOOR/FLOOR(15, 10, 150); TYPE "After Startup: LENGTH=<MYFLOOR.LENGTH" | " WIDTH=<MYFLOOR.WIDTH AREA=<MYFLOOR.AREA"; MYFLOOR.PrintFloor(); END
The output is:
After Startup: LENGTH=15 WIDTH=10 AREA=150 In PrintFloor: length=15 width=10 area=150
In the following Maintain Data procedure, the DESCRIBE command defines a class named FNAME with components LAST and FIRST. The FORMNAME case concatenates the last and first names and separates them with a comma (,) and a space.
The main procedure loops through the first five records of the EMPLOYEE data source, and passes each last name and first name to case PRTFULL.
Case PRTFULL creates a local instance of the FNAME class, invokes the FORMNAME member function, and types the full name returned from that class. Although FORMNAME does not take any arguments, the parentheses used when invoking FORMNAME identify it as a function.
TYPE commands in each case illustrate the flow of control:
MAINTAIN FILE VIDEOTRK DESCRIBE FNAME = (LAST/A15, FIRST/A10) CASE STARTUP; TYPE "IN CASE STARTUP: I = <I"; ENDCASE CASE FORMNAME RETURNS FULLNAME/A30; FULLNAME/A30 = LAST || ', ' | FIRST; TYPE "IN CASE FORMNAME: I = <I FULLNAME = <FULLNAME"; ENDCASE CASE CLEANUP; TYPE "IN CASE CLEANUP: I = <I"; ENDCASE ENDDESCRIBE -* MAIN PROCEDURE FOR 5 NEXT CUSTID INTO CUSTSTK; REPEAT 5 I/I1 = 1; COMPUTE LAST/A15 = CUSTSTK(I).LASTNAME; COMPUTE FIRST/A10 = CUSTSTK(I).FIRSTNAME; TYPE "IN MAIN PROCEDURE: I = <I LAST = <LAST FIRST = <FIRST"; PERFORM PRTFULL(LAST, FIRST); ENDREPEAT I = I+1; CASE PRTFULL TAKES LAST/A15, FIRST/A10; -* MEMNAME IS A LOCAL VARIABLE DECLARE MEMNAME/FNAME; MEMNAME.LAST=LAST; MEMNAME.FIRST=FIRST; NEWNAME/A30 = MEMNAME.FORMNAME(); TYPE "IN CASE PRTFULL: I = <I MEMBER NAME IS <NEWNAME"; ENDCASE END
The output shows that the Startup case is called prior to each invocation of case PRTFULL (which defines the local instance), and the Cleanup case is called at the end of each invocation of case PRTFULL:
IN MAIN PROCEDURE: I = 1 LAST = CRUZ FIRST = IVY IN CASE STARTUP: I = 1 IN CASE FORMNAME: I = 1 FULLNAME = CRUZ, IVY IN CASE PRTFULL: I = 1 MEMBER NAME IS CRUZ, IVY IN CASE CLEANUP: I = 1 IN MAIN PROCEDURE: I = 2 LAST = HANDLER FIRST = EVAN IN CASE STARTUP: I = 2 IN CASE FORMNAME: I = 2 FULLNAME = HANDLER, EVAN IN CASE PRTFULL: I = 2 MEMBER NAME IS HANDLER, EVAN IN CASE CLEANUP: I = 2 IN MAIN PROCEDURE: I = 3 LAST = WILSON FIRST = KELLY IN CASE STARTUP: I = 3 IN CASE FORMNAME: I = 3 FULLNAME = WILSON, KELLY IN CASE PRTFULL: I = 3 MEMBER NAME IS WILSON, KELLY IN CASE CLEANUP: I = 3 IN MAIN PROCEDURE: I = 4 LAST = KRAMER FIRST = CHERYL IN CASE STARTUP: I = 4 IN CASE FORMNAME: I = 4 FULLNAME = KRAMER, CHERYL IN CASE PRTFULL: I = 4 MEMBER NAME IS KRAMER, CHERYL IN CASE CLEANUP: I = 4 IN MAIN PROCEDURE: I = 5 LAST = GOODMAN FIRST = JOHN IN CASE STARTUP: I = 5 IN CASE FORMNAME: I = 5 FULLNAME = GOODMAN, JOHN IN CASE PRTFULL: I = 5 MEMBER NAME IS GOODMAN, JOHN IN CASE CLEANUP: I = 5 TRANSACTIONS: COMMITS = 1 ROLLBACKS = 0 SEGMENTS : INCLUDED = 0 UPDATED = 0 DELETED = 0
In the following Maintain Data procedure, the DESCRIBE command defines a class named FNAME with components LAST and FIRST. The FORMNAME case concatenates the last and first names and separates them with a comma (,) and a space.
The main procedure loops through the first five records of the EMPLOYEE data source, and passes each last name and first name to case PRTFULL.
Case PRTFULL creates a global instance of the FNAME class, invokes the FORMNAME member function, and types the full name returned from that class. Although FORMNAME does not take any arguments, the parentheses used when invoking FORMNAME identify it as a function.
TYPE commands in each case illustrate the flow of control:
MAINTAIN FILE VIDEOTRK DESCRIBE FNAME = (LAST/A15, FIRST/A10) CASE STARTUP TAKES LASTNAME/A15, FIRSTNAME/A10; TYPE "IN CASE STARTUP: I = <I LAST = <LASTNAME FIRST = <FIRSTNAME"; LAST = LASTNAME; FIRST = FIRSTNAME; ENDCASE CASE FORMNAME RETURNS FULLNAME/A30; FULLNAME/A30 = LAST || ', ' | FIRST; TYPE "IN CASE FORMNAME: I = <I FULLNAME = <FULLNAME"; ENDCASE CASE CLEANUP; TYPE "IN CASE CLEANUP: I = <I "; ENDCASE ENDDESCRIBE -*MAIN PROCEDURE FOR 5 NEXT CUSTID INTO CUSTSTK; REPEAT 5 I/I1 = 1; COMPUTE LAST/A15 = CUSTSTK(I).LASTNAME; COMPUTE FIRST/A10 = CUSTSTK(I).FIRSTNAME; TYPE "IN MAIN PROCEDURE: I = <I LAST = <LAST FIRST = <FIRST"; PERFORM PRTFULL(LAST, FIRST); ENDREPEAT I = I+1; CASE PRTFULL TAKES LAST/A15, FIRST/A10; COMPUTE MEMNAME/FNAME('ABEL', 'AARON'); NEWNAME/A30 = MEMNAME.FORMNAME(); TYPE "IN CASE PRTFULL: I = <I MEMBER NAME IS <NEWNAME"; ENDCASE END
The output shows that the Startup case is called at the start of the Maintain Data procedure, and the Cleanup case is called following the execution of the entire Maintain Data procedure:
IN CASE STARTUP: I = 0 LAST = ABEL FIRST = AARON IN MAIN PROCEDURE: I = 1 LAST = CRUZ FIRST = IVY IN CASE FORMNAME: I = 1 FULLNAME = CRUZ, IVY IN CASE PRTFULL: I = 1 MEMBER NAME IS CRUZ, IVY IN MAIN PROCEDURE: I = 2 LAST = HANDLER FIRST = EVAN IN CASE FORMNAME: I = 2 FULLNAME = HANDLER, EVAN IN CASE PRTFULL: I = 2 MEMBER NAME IS HANDLER, EVAN IN MAIN PROCEDURE: I = 3 LAST = WILSON FIRST = KELLY IN CASE FORMNAME: I = 3 FULLNAME = WILSON, KELLY IN CASE PRTFULL: I = 3 MEMBER NAME IS WILSON, KELLY IN MAIN PROCEDURE: I = 4 LAST = KRAMER FIRST = CHERYL IN CASE FORMNAME: I = 4 FULLNAME = KRAMER, CHERYL IN CASE PRTFULL: I = 4 MEMBER NAME IS KRAMER, CHERYL IN MAIN PROCEDURE: I = 5 LAST = GOODMAN FIRST = JOHN IN CASE FORMNAME: I = 5 FULLNAME = GOODMAN, JOHN IN CASE PRTFULL: I = 5 MEMBER NAME IS GOODMAN, JOHN TRANSACTIONS: COMMITS = 1 ROLLBACKS = 0 SEGMENTS : INCLUDED = 0 UPDATED = 0 DELETED = 0 IN CASE CLEANUP: I = 6
Reference: |
After you describe a class, you can derive other classes from it. Subclasses inherit member variables and functions from their superclasses.
Order of classes matters when defining superclasses and subclasses. You must describe a superclass prior to its subclasses.
A class can also use another class as one of its components. Again, order matters. You must describe the class you are using as a component prior to the class that uses it.
The following example describes two classes, Floor and Room. Floor consists of components Length and Width and member function FloorArea.
Room is a subclass of Floor. It inherits components Length and Width and member function FloorArea. It adds component Depth and function RoomVolume.
The main procedure creates an instance of Room called MYROOM. It then assigns values to the components, including inherited components Length and Width. It invokes the inherited member function FloorArea as well as the RoomVolume function.
The main procedure then types the component values and the values returned by the member functions.
MAINTAIN DESCRIBE Floor = (Length/I4, Width/I4) CASE FloorArea RETURNS Area/I4; Area = Length * Width; ENDCASE ENDDESCRIBE DESCRIBE Room = (Floor + Depth/I4) CASE RoomVolume RETURNS Volume/I4; Volume = FLOORAREA() * Depth; ENDCASE ENDDESCRIBE COMPUTE MYROOM/ROOM; MYROOM.LENGTH = 15; MYROOM.WIDTH = 10; MYROOM.DEPTH = 10; AREA/I4 = MYROOM.FLOORAREA(); VOLUME/I4 = MYROOM.RoomVolume(); TYPE "LENGTH=<MYROOM.Length, WIDTH=<MYROOM.Width, " | "DEPTH=<MYROOM.DEPTH, AREA=<AREA, " | "VOLUME=<VOLUME"; END
The output is:
LENGTH=15, WIDTH=10, DEPTH=10, AREA=150, VOLUME=1500
The following example describes three classes: RoomDetail, Floor, and Room.
RoomDetail has no member functions. It consists of two components, Depth and RmType.
Floor consists of components Length and Width and member function FloorArea.
Room is a subclass of Floor. It inherits components Length and Width and member function FloorArea. In addition, it has member function RoomVolume and component RmType, which is an instance of class RoomDetail. When referring to the components of RoomDetail, you must qualify them with their instance name. For example, the Rtype component is referenced as follows:
RmType.Rtype
The main procedure creates an instance of Room called MYROOM. It then assigns values to the components, including inherited components Length and Width. When assigning values to the components of the RmType instance of the RoomDetail class, it must qualify them with the instance name, MYROOM. Since these names already had one level of qualification when they were referenced in the Room class, they now have two levels of qualification. For example, the following assigns the value 10 to the Depth component:
MYROOM.RmType.Depth = 10;
Note that when no ambiguity in the variable name will result, only one level of qualification is actually enforced. MYROOM.Depth is understood as MYROOM.RmType.Depth since Depth does not appear in any other context.
The main procedure invokes the inherited member function FloorArea, as well as the RoomVolume function, then types the component values and the values returned by the member functions.
MAINTAIN DESCRIBE RoomDetail = (Depth/I4, Rtype/A10); DESCRIBE Floor = (Length/I4, Width/I4) CASE FloorArea RETURNS Area/I4; Area = Length * Width; ENDCASE ENDDESCRIBE DESCRIBE Room = (Floor + RmType/RoomDetail) CASE RoomVolume RETURNS Volume/I4; Volume = FLOORAREA() * RmType.Depth; TYPE "ROOM TYPE IS <RmType.Rtype"; ENDCASE ENDDESCRIBE COMPUTE MYROOM/ROOM; MYROOM.LENGTH = 15; MYROOM.WIDTH = 10; MYROOM.RmType.Depth = 10; MYROOM.RmType.Rtype = 'DINING '; AREA/I4 = MYROOM.FLOORAREA(); VOLUME/I4 = MYROOM.RoomVolume(); TYPE "LENGTH=<MYROOM.LENGTH, WIDTH=<MYROOM.WIDTH, "| "DEPTH=<MYROOM.rmtype.DEPTH, AREA=<AREA," | " VOLUME=<VOLUME"; END
The output is:
ROOM TYPE IS DINING LENGTH=15, WIDTH=10, DEPTH=10, AREA=150, VOLUME=1500