The ClassDefinition object was introduced in PowerBuilder 6.0 a long time ago. It allows you to retrieve information for an object at runtime. Most of us didn't pay too much attention to this object and it only attracts our attention when we see it in the debugger.
Listing the Objects from a Library
In the first article we had some theory about the ClassDefinition object and we were able to show the libraries of a PB application in a treeview control. This month, we read the objects from the libraries and inspect their content.
When a user expands an entry in the treeview, we check if it was expanded once already. If so, we don’t take any action. We code this in the itemexpanding event of the treeview control, where we get the clicked treeviewitem by calling This.GetItem. If it wasn’t expanded, we check if the level of the treeviewitem is equal to two, which means we’re expanding a library name. We could create an NVO to include all the logic for the parsing, but for demonstration purposes, I want to keep the things simple. We define all the functions we need on the window itself, but use arguments to refer to the controls. This will allow you to move the logic to an NVO later quite easily.
The next function we create is called of_ShowLibraryItems. It’s used to read the PB objects from a PBL. I use the function LibraryDirectoryEx to extract the objects. Compared to the function LibraryDirectory, it provides besides the name, the date/time modified and the comment, the type of the objects also. As I want the objects to be sorted, I use a DataWindow with an external datasource to achieve this. See Figure 3 for the definition of the DataWindow. Set the sorting to the column objtype and save it as d_libobj.
The string returned by LibraryDirectoryEx will be imported into the datastore with ImportString. After sorting the data, the script loops through the objects and adds them to the treeview. You can find the code for this function in Listing 6.
Call the of_ShowLibraryItems function in the itemexpanding event of the treeview control when the level is 2, whereas the ll_TviCurrent is the treeviewitem identified by the handle argument.
Parent.of_ShowLibraryItems( This, handle, ltvi_Current.Data )
Getting on to Class Details
The application so far shows the libraries and the PB objects located in them. Now it’s time for the classdefinitionobject object and its descendants. As you have seen last month we will display the information in various child entries. The first part is called Variables where we list all the variables of this object. In the second section we have the scripts and in the third section the nested classes. Finally the last section includes a reference to the ancestor of the class. The idea behind the functions we create is that they can be called recursive and can be used for each type of PB object.
It’s not possible to loop through the variable list of the ClassDefinition object of a PB object and display all the information about it nicely. Therefore the next step is the creation of two helper functions. The first function is called of_ValueString and translates the enumerated values to strings (see Listing 7). The arguments of this function are the type of a variable (e.g. Cardinality, InitialValue) and the value itself. It returns both as a readable string. Converting the value to a string is simple with e.g. boolean values, however if we have enumerated data types we have to handle them individually. If the variable is an enumerated datatype we use the FindTypeDefinition get all the possible enumerations of this datatype. The second function is called of_FormatInfo and takes a classdefinitionobject as argument. Its purpose is to create a formatted string out of the classdefinitionobject information. The argument is of type classdefinitionobject, which allows us to pass variables of type classdefinition, scriptdefinition and variabledefinition. The function is available online only (Listing 8), as it takes too much space in the article and can’t be formatted easily for printing.
Display the Information about a Class
After setting up the helper functions lets come back to the treeview. When we expand a PB object in the treeview, we have to call the function of_ShowClassDefinition. The definition of the function is available online in Listing 9. It takes three arguments: the treeview control, the handle of the clicked item and a ClassDefinition object. For the first level of information the ClassDefinition object is determined by a call to the FindClassDefinition function with the argument equals the label of the treeview item. Here is the code in the itemexpanding event of the treeview when the level is greater than two. The variable lcd_Temp is of type ClassDefinition and ll_Child is a long type.
ll_Child = This.FindItem( ChildTreeItem!, handle )
IF ll_Child = -1 THEN // No children yet
lcd_Temp= FindClassDefinition ( ltvi_Current.Label,il_LibList )
IF NOT IsNull(lcd_Temp) THEN
The variable section is filled by looping through the VariableList property of the ClassDefinition, using the properties IsControl to prevent the adding of controls and IsUserDefined to set the picture of the treeviewitem. For each variable the function of_ShowVarible (see Listing 10) is called to fill the variable information into the data property of the treeviewitem. After handling the variables, the function goes on to the scripts. For each entry in the ScriptList property of the ClassDefinition, the function of_ShowScript (Listing 11) is called. Here we use the properties IsLocallyScripted and IsScripted to set the picture of the treeviewitem. The data property of the treeviewitem is used for general information about the script. For each script we add, an entry Source (saving the source of the script), one called ArgumentList and one LocalVariableList. We add each argument variable and each local variable including their definitions under the appropriate entry. This is done by calling the function of_ShowVariable with each variable. To compile you have to define of_ShowVariableFirst, then of_ShowScript and of_ShowClassDefinition in the end.
Then the nested classes are shown. The naming convention of a nested class is objectname`nestedclassname. We need the whole name to find more information about the nested class when calling FindClassDefinition. For nested classes we check the property ParentClass. If this is null we don’t add the class. Further the NestedClassList returns not only the controls on an object, but also the controls on the ancestor of this object. We prevent showing ancestor data by comparing the name of the ParentClass of the NestedClass with the name of the current object. If they don’t match, the control is inherited from the ancestor. At the end of the list we add an entry for the ancestor object if there is one.
To display the additional information stored in the data property of the treeview items you have to place the following code in the selectionchanged event of the treeview.
When running the application and expanding a library entry, all the information of the entry itself is loaded at once. We don’t parse information about the nested classes and no information about possible ancestors (except its name) is retrieved. On large objects this might take some time. To drill down on an entry you have to expand the item in the treeview. If it’s a nested class or the ancestor class and the data isn’t loaded, the ClassDefinition object of the chosen entry is retrieved by FindClassDefiniton and the function of_ShowClassDefinition is called, passing the ClassDefinition object as an argument. As the ClassDefinition object is available all PB classes, not further coding is required and our application is ready to browse. Browsing PFC based applications is quite interesting and allows you to drill down many levels.
I hope this gave you a basic understanding of the ClassDefinition object and related objects. Creating a browser for PB objects could be one possible usage of these objects. Maybe you can make use of it in an application, where you need to get information about an object’s inheritance or the availability of a certain function (see the function FindMatchingFunction). Another possible use might be the dynamic creation of WSDL files for a WebService.
Download all the source from our website at: https://www.dropbox.com/s/odksu9rsgttqxzy/SourceClassDefinition.txt?dl=1