Controlling Yourself: A Framework for Configurable Options
Written by John Holt
Most programs have a set of persistent options that are under user control. These options must be managed. This article discusses an approach to uniform management of option sets that may reside in a profile (INI file) or on a file's extended attributes (EA's).
The home for user specified general application defaults varies by system. In a UNIX system, environment variables or entries in .mwmrc (the Motif resource file) are used for system-wide defaults. In Windows and OS/2, INI files are used to hold the general application defaults. The OS/2 file system also provides an additional means of keeping file specific default settings.
Problem and Environment Description
Any general framework of classes must have certain attributes. Two attributes that are germane to this article are programmer-extensibility and harmony with the external environment. The facility for programmer extensions is simply providing a capability for the programmer to add new classes to be stored in the profile or extended attributes. The attribute of harmony with the external environment in this case is using profiles and extended attributes in a way that facilitates the use of existing tools.
Several base and implementation classes were developed. The task of managing persistent option sets is common to many programs, and therefore is an excellent candidate for reusable objects. The classes developed for the article represent an attempt to divide the implementation into classes so as to reuse common behaviors. The model for implementation is individual objects grouped into a set. The set is then stored on either a profile or in EAs.
Class Relationship StructureOverview
The class structure is depicted in the diagram below. The relationship marked IS-A denotes class derivation. The relationship marked HAS-A shows that the class contains an object of the class. The KeyedOptionSet class is the result of a template generation using the KeyedSequentialSet class in the compiler collection classes library.
The option set behaviors have been factored into a base class (BOptionSet) and two implementation classes (INIOptionSet and EAOptionSet). The behaviors specific to manipulation of EAs or profiles is in EAOptionSet and INIOptionSet respectively.
BOptionSet is concerned with the maintenance of the list of items to be stored or retrieved. The following public methods are implemented:
The OptionSetElem is a simple object holding only the name of the option and a pointer to a BOptionItem object.
The derived classes EAOptionSet and INIOptionSet provide the implementations for the loadSet, storeSet, and resetSet as well as the usual expected complement of constructors and an equal operator.
The loadSet method updates each item with the value stored for that item's name. For the case of the items stored as EAs, the name is <application name>.<item name> and is mono-case. For objects stored in profiles, the name is forced to be in uppercase for consistency. The storeSet method is the reverse of the loadSet.
The resetSet method performs two operations. First, the set of names in the profile or EAs is made to correspond with the set of names in the set. Any names with a prefix other than <application name> are ignored. The second operation is to perform a storeSet.
The BOptionItem class provides some default implementations for the behaviors. The class interface is primarily intended to be the communication portal between an option set and the real data objects that are the elements of that set. The interface is:
Interface to Programmer Supplied Objects
The interface used by the option set to manipulate the items in memory is BOptionItem. Because all option items are derived from BOptionItem, the correct method is invoked.
When an addItem method is invoked on a set, the set obtains a BOptionItem type pointer to that item. When the set needs the size of the item, e.g. to allocate a buffer, the pointer to a BOptionItem is used to invoke the correct method. Similarly, the importData and exportData methods are invoked to move the value to and from the persistent store.
Adding New Object Classes
There are two different strategies that can be employed for extending the range of types of items that can be used in option sets. The programmer may implement interface behaviors directly in the object to be added, or the programmer can implement a wrapper class. If the class is under control of the programmer, direct implementation is usually the best approach. If the object to be used is provided by a third party class library, or is not a first class object (e.g., an INT) then a wrapper class is the right approach. The sample program uses the later approach because it is the most non-obvious of the two approaches. The reader will notice that the example is contrived.
Walkthrough the Sample/Test Program
The sample/test program illustrates how to create a set, add option items to that set, and store the set of option items. To exercise the code, these tasks were performed for both profile and EA based option sets. The file test.cpp [see test_src.zip within options.zip - Editor] should be followed for this discussion. You may also want look at BOptionSet.cpp and StrOptionItem.cpp [see source.zip within options.zip - Editor] for additional detail.
There are several things worth keeping in mind. The flow for the above example is designed to test the logic, and not how the objects would be used in a real application. In a real application, the steps for use would be:
At an appropriate time, the option sets would be written to either the profile or the EAs or both.
This section discusses some of the less obvious aspects of the set classes. To warrant the appellation of "less obvious," it had to be something I needed some effort to figure out. Which of course means that they may be completely obvious to you.
The API for the profile was just a joy to use (compared to EAs), and the documentation makes everything pretty clear.
The EAs are not at all the same story. I used two books, and the documentation, to puzzle my way through how EAs worked. The books used were The Art of OS/2 2.1 C Programming, by Panov, Salomon, and Panov; the other book was OS/2 2.1 Application Programmers Guide, by Kelly et. al.
The EAOptionSet operations work on all of the EAs at once. That way there are no windows where a a particular EA may change, or the ordinal positions of the EAs change.
The layout of the EA buffer is as follows:
This article has present a set of classes that can be used to manage an application's set of options items. The basic model is that there exists a set of default values for the options of an application; the user is permitted to modify those options in a persistent manner; and that there may be some sub-set of options that are associated with particular files.
There are several obvious features lacking which are required for an industrial strength implementation. In the case of EAs, there are no checks for exclusive control while writing or reading the set of EAs from the file. In the realm of memory management, there are no safeguards against bad pointers.
One extension worthwhile making is that BOptionItems could have reference counts and perhaps references lists of the option sets where they are members.
Useful Test Tools
There are a couple of REXX scripts included in the zip file. In addition, there are several very useful tools at ftp-os2 such as EABrowse. The REXX script to print EAs only works when the the test program has closed the file, but I have no idea why I could not get it to share read access.