XmlObject - Adaptive Object Model

Better Designs Faster

CLI Example

When I was an undergrad in pursuit of my degree I had the good fortune to work with a small group of engineers doing research.  As with any research effort, there was a great deal of focus on data.  Every week we would collect megabytes of data that would need to be processed and analyzed.  My job was to create simple command line applications to perform this analysis.  I was pretty good at the data processing part, but because of inexperience I wasn’t all that great at the infrastructure.  Every application was a new effort started from scratch even though they largely did the same sorts of processing.

All that changed when a vendor sent me a prototype application, an exceedingly flexible prototype.  I have no idea who to credit, but it was obviously written by someone with experience and it was my first taste of how one could data drive an application.  The prototype was a procedural application written in “C’.  Over the next few years I slowly migrated it to “C++”, made it object oriented and continued to refine its operation.  Awhile back I once again needed my old work horse and I realized how easy it would be to recreate it as an Adaptive Object Model.  This application is the result of that effort. 

The CLI Example

The CLI application uses an Adaptive Object Model architecture to define the commands it supports.  Think of the DOS command prompt, it checks for parameter validity, executes commands and provides help with syntax when asked.  The CLI abstracts this functionality.  Help is defined in metadata, as well as the number, type and order of parameters.  The CLI checks for the presence of required parameters and checks parameter type.  Applications built using the CLI are relieved of this responsibility.  This makes it very easy to add new commands.  For example a month or so back I needed to add a beep command to aid in some debugging.  I defined the following command element in XML.

                 <cli:command id='beep' catagory='normal'>

                                  <cli:help type='description'>Plays a sound through the system speaker.</cli:help>

                                  <cli:help type='syntax'>BEEP [/f frequency] [/d duration]</cli:help>

                                  <cli:help type='syntax'>    frequency - a value between 40 and 32000.</cli:help>

                                  <cli:help type='syntax'>    duration - a value greater than zero.</cli:help>

                                  <cli:parameters>

                                                   <cli:parameter id='/d' type='int' optional='true'/>

                                                   <cli:parameter id='/f' type='int' optional='true'/>

                                  </cli:parameters>

                                  <cli:delegate>BeepDelegate</cli:delegate>

                 </cli:command>

 

As you can see, this fragment tells the executable what information to display when the user asks for syntax help.  It also specifies the number and type of parameters the command expects.  Notice that both parameters are optional.  Finally, it specifies the delegate object that does the actual work for the command.  Being a new command I of course needed to write the BeepDelegate code.  All commands have a similar fragment.  If at anytime you want to see the applications XML metadata, enter the “metadata” command at the CLI prompt.  The command will display the metadata as XML in a browser window.  Here is the list of native commands available before loading an application.

ABOUT                   Displays assembly version information for the application.

BEEP                       Plays a sound through the system speaker.

CLEAR                    Clears all text from the application window.

EVAL                      Evaluates a script file containing commands.

EXIT                        Exits the application.

HELP                       Provides help information for Command Interpreter applications.

LOAD                      Loads a new application into the CLI.

REM                         Records comments (remarks) in a script file.

RESTART             Restarts the application.

SHELL                     Starts a command line shell.

XML                        Show the application XML.

 

There is additional discussion of delegates in the WavePlayer Example but for now notice that all commands have the same basic execution semantics.  That is, they all are initialized, they all check syntax, and they all execute.  These three steps are built into the relationship between the CommandElement class and the CommandDelegate class.  Developers only need to derive from CommandDelegate and to implement the Init, CheckParameters, and Execute methods.  The framework does the rest, making it very easy to add new commands. 

It is probably obvious, but one can switch between delegates by simply editing a file.   The CLI application defines two beep delegates. One named BeepDelegate, the other is imaginatively named AltBeepDelegate.  To try this, start the application and enter the beep command.  Make note of the pitch.  Now without exiting the application, edit the default.xml file to use the AltBeepDelegate.  Make sure you edit the file that is adjacent to the executable you are runningEnter the restart command followed by the beep command again.  You should notice that the pitch has changed.

Making the CLI useful

By itself the CLI is not all that useful.  A poor shadow of a DOS command prompt.  However the DOS command prompt does not have a Load command.  The XML for the Load command looks like this.

                 <cli:command id='load' catagory='cloaked'>

                                  <cli:help type='description'>Loads a new application into the CLI.</cli:help>

                                  <cli:help type='syntax'>LOAD filename[.xml]</cli:help>

                                  <cli:help type='syntax'>    filename - filename for the new application.</cli:help>

                                  <cli:help type='syntax'>             - the .xml extension is optional.</cli:help>

                                  <cli:parameters>

                                                   <cli:parameter id='param0' type='string'/>

                                  </cli:parameters>

                                  <cli:delegate>LoadDelegate</cli:delegate>

                 </cli:command>

 

You can see that the Load command takes a single required parameter.  This parameter is another XML file that defines another application.  At runtime the CLI will merge the two XML documents creating a new and totally different application.  I have included examples for two such applications.  They are a simple Tic-Tac-Toe game and a application to play wave files called WavePlayer.  The CLI can instantly switch between these applications.  Of course, I only added this ability because I thought it would be effective in demonstrating the adaptive nature of the XmlObject Framework, but I think you’ll admit that it is pretty cool.  If you look with a debugger when switching applications you can actually see the teardown of the old application and the instantiation of the new one.  Accepting that I am easily impressed, it still amazes me.

Now, I only hope that I one day I will have the opportunity to send a prototype to a kid that needs to analyze some data.

XmlObject Graphic