TweetFollow Us on Twitter

September 93 - OSL Scripting Components

OSL Scripting Components

Bo Klintberg

A Generic Scripting Architecture for Standalone and Client/Server-based MacApp Applications.
Part 1: The Client Side.

If you're desperately seeking scripting and recording source code to incorporate into your own MacApp programs, you might want to check out a new product called Developer's C++ Source Code Library: OSL Scripting Components.

This product provides the developer with well over 100 carefully designed and documented classes for scripting, recording, and Apple events communication. This full-fledged library of extensible building blocks also helps you to implement other related features such as the management of client and server applications in computer networks. And all this without having to modify a single line of the original MacApp 3.0.x source code!

Using the classes in the OSL Scripting Components package will radically decrease the time it takes to learn what Apple events really is all about. In fact, to guarantee you the quickest possible learning curve, I designed a couple of classes that produces a Sourcebug-like log window, so that you easily can monitor what's really happening inside your application. The ScriptSender and ScriptServer demos (included in the OSL Scripting Components package) both use log windows to let you see what's happening (see Figure 1). Here's how the OSL Scripting Components can be used to communicate with itself, both over the network (as seen here) and within the same application. Since both the client and the server send and handle standard Apple events according to the Required and the Core suites defined by Apple, they can communicate with any other application that also uses the same protocol, for example the ScriptEditor shown in Figure 2.

Note that the client's log window reports that the menu command has been handled and that the Apple event is sent away to its server destination. A moment later the server application's log window reports that it has handled the Apple event, resolved the object specifiers and reached its final goal: for example to get the data we wanted. Then the server sends back the result to the client, thus producing a new line in the client's log window, confirming the result. The last action in a log window is always autoscrolled and highlighted for maximum convenience.)

SENDING, RECEIVING, AND RECORDING

Scripting can be implemented in an application in a variety of ways. The three basic mechanisms are sending, receiving and recording. All of these are supported by the OSL Scripting Components package, which enables you to smoothly and easily attach scripting functionality to your existing or up-coming product.

Script-sending Applications

Some of you might like to create client applications that send scripting Apple events to server applications like FileMaker, Excel, Canvas, or the ScriptServer. With the OSL Scripting Components source code in your hands, you can easily select a server application and then send standard core suite events to it. Thus, you can mimic the sending behavior of, for example, Apple Computer's Script Editor (previously called Toy Surprise), or you could create a script-sending application named Updater that:
  • Retrieves the data from all of John's currently open Excel documents;
  • Processes that retrieved data inside your Updater application;
  • Sends away the processed data to the "Results" FileMaker file on Anne's Macintosh.

Script-receiving Applications

Some developers may like to create applications that can handle the receiving and replying of standard suite scripting events. Such events could come from script-sending applications like the ScriptEditor or the ScriptSender. A good script-receiving application can handle requests from many clients simultaneously (see Figure 2). Using the OSL Scripting Components package as a server, you can receive and properly handle standard scripting Apple events from any other script-sending application that implements the Core suite protocol. An example of such an application is Apple Computer's Script Editor as seen above.)

Script-recordable Applications

Some developers would want to develop recordable applications-that is, applications that can send and receive scripting events within themselves. In this case, the application acts both as a sender and a receiver and sends the events to itself.

DESIGN OBJECTIVES

Don't "Fix" MacApp

One of the main design objectives was to leave the MacApp original code untouched. This has been achieved, and you can use the OSL Scripting Components package without modifying the original MacApp code.

Since we don't rely on having to modify MacApp itself, this package should also be compatible, direct out of the box, with the forthcoming MacApp 3.0.2 / 3.1. Also, if Bedrock can't/won't implement scripting in its first release, it should be no great obstacle to convert it to that platform, if necessary.

"Fix" MacApp

The OSL Scripting Components is designed to leave the MacApp code untouched (see the section above). But, of course, there's nothing stopping you from copying and pasting the code from the TOSLObject, TOSLObjectDescription, and TObjectResolver classes in the OSL Scripting Components package into MacApp's TObject and nearby. In fact, it's designed to be able to do that too, if you really want it. Also, you would have to provide support for the generation of TOSLObject-Descriptions and TObjectResolvers, but again, you can copy the same thing I did in TOSLApplication, TOSLWindow, and TOSLFileBasedDocument.

And, don't forget to recompile MacApp!

Should Work BETWEEN Applications as Well as WITHIN Applications

Another really important matter was the need to streamline the sending and receiving of Apple events so that the packing and the unpacking of Apple events would be done in exactly the same way when using a client app-server app configuration as when using a client-side/server-side configuration. This is an important matter, because you really can REUSE code this way. This has been achieved with asynchronous Apple events.

Example: If you have posted a TGetBooleanDataFromServer command in the client, it doesn't matter where the server is (it could be the same application as the client or another application on another machine on the network), as long as you have a valid address to that server.

Please note that I whenever I say "server" or "client" in this article (and don't specify it further) this means it's applicable both in a client-side/server-side configuration within one application as well as in a client app/server app setup.

The Server Should Report Errors to its Client(s)

Sending and receiving Apple events is not all that hard to do, especially when doing it only within one application. But at the moment when you're deciding to use a client application/server application setup, you have to give the error handling just a little bit more attention.

One of the most important issues is the one about letting the server application respond with an error code and error string whenever an error occurs in the server. Just issuing a FailOSErr() is unfortunately not good enough-this doesn't send a reply error to the client. Instead I had to add a couple of extra lines of code on the server side for proper and timely responding to the client.

THE BIG PICTURE

Very soon after I started with the OSL and Apple events coding, I realized that I had some major design and modeling issues to investigate in order to make the creation, sending, and receiving of OSL Apple events easy, flexible, and secure to use (and reuse) for the end programmer. My primary goal was to be able to create an architecture so that I, with a handful of lines of code, could administrate the whole packing, sending, and receiving process, without ever worrying about all those boring details underneath. Here's how I wanted the code to look like in a typical client's DoMenuCommand:
case cSetCellData: this->DoSetCellData();

OK, that was real easy, I admit. Now let's look at what happens inside the DoSetCellData method.

pascal void DoSetCellData()
{
    TOSLObjectDescription* cellDescription =
        this->BuildCellDescription();
    CStr255 newStringData = GetStringDataFromUser();    
        // or from somewhere else…
    TSetServerCellDataCmd* aSetServerCellDataCmd = 
        new TSetServerCellDataCmd;
    aSetServerCellDataCmd ->ISetServerCellDataCmd 
        (cellDescription,   newStringData );
    gApplication->PostCommand(aSetServerCellDataCmd );
}

On the first line I call the BuildCellDescription method, which creates a TOSLObjectDescription object. The TOSLObject-Description object contains a full description of a standard Apple Event Registry cCell object.

On the second line I get the data I want to put into the cell in the server application.

On the third, fourth and fifth lines I create the client command, initiate it and post it to the client application's event queue, which immediately performs the command, thus sending the kAESetDataApple event to the server. We'll look at the details in just a minute.

BASIC CLASS COMPONENTS

OSL Object Descriptions

A central class in the OSL Scripting Components package is the TOSLObjectDescription class, which is used to universally describe an object. This means that you, the programmer, can more easily manage the sending and receiving of Apple events according to the OSL standards.

Since Apple events are verb-oriented (each event is a verb like Create, Delete or Open), it made sense to me to define a set of standard event-sending commands that corresponded to each and every one of these verbs. Each such command could have an object description as a parameter, telling the verb which object it should perform its actions on.

The TOSLObjectDescription is a class descending directly from TObject. As you can see in the more detailed description below, it contains a generic description of an object and its "outer" containers.

OSL Objects

Now that we have an universal way of describing objects (the TOSLObjectDescription), it is time to define one of the other main components of this library, the TOSLObject. Any object that you would want to be scriptable must inherit from TOSLObject. This could be, for instance, an application, a window, or a file. The TOSLObject provides a dozen methods, of which you must override only a few. Also, the OSLObject has a strong relation to the TOSLObjectDescription, since you may want to generate object descriptions for any "live" object you can have.

OSL Object Resolvers

These are almost without exception used on the server side of the application. Their mission in life is to resolve the incoming Apple events and their stuffed object descriptions into something the server can understand. TOSLObjectResolvers will be discussed in greater detail in an upcoming article here in FrameWorks.

Client and Server Commands

An important part of the OSL Scripting Components library is the commands that send and receive all those Apple events. On the server side we use subclasses of MacApp's TServer-Command to handle an incoming event and to generate a reply to the client. On the client side we use a subclass of TClient Command (also a MacApp class) to send it and handle the reply from the server.

THE OSL OBJECT DESCRIPTION class

When it comes to understanding how the OSL and scripting really works, we have to talk about how to describe objects in a generic way. The OSL uses a concept that's called "object specifier" to describe an object. Simply put it's a full path to the object, much like a Macintosh file's path in HFS.

If we're describing only one object (for example an application object) we have what's called a simple object specifier: application "ScriptSender". However, if one object is put into another object, we have what's called a complex object specifier: window "Client Log" of application "ScriptSender".

An object specifier can be constructed in different ways. Some of these different ways, called reference forms, are:

  • by index ("car 23")
  • by absolute position ("last car")
  • by relative position ("next car")
  • by name ("the car with the name of BMW")

What Exactly is a TOSLObjectDescription?

The object that we are describing doesn't actually have to exist in the application as an object instance; instead, it's enough that the application understands the description of the object so that it can instantiate it on the fly, if the application finds that it represents something tangible. If the server can't locate the object (it might not be a valid reference like the third window in application, but there are only two windows right now in the application) it sends an error message back to the client saying that it couldn't find the object.

Since object specifiers are somewhat hard to handle properly (have you ever seen one of Apple's non-object oriented source code examples?), I decided to build a class around them. The benefits of using a TOSLObjectDescription class are many: easy to create, easy to send, and easy to garbage collect.

Easy to Create Descriptions Even Without the "Real" Object.

You'll quickly create a description of the object of your choice totally from scratch. Here's how you would create a description of the last table in the document "my doc":
TOSLFileBasedDocumentDescription* docDecription;
TOSLTableDescription* tableDescription;

docDecription = new TOSLFileBasedDocumentDescription;
docDecription->IOSLFileBasedDocumentDescriptionByName("my doc");
tableDescription = new TOSLTableDescription;
tableDescription->IOSLTableDescriptionByAbsolutePosition
    (docDecription, kAELast);

Use this strategy any time you want, especially when you want to describe an object that's somewhere on the server side but has no "live" object instance on the client side.

Sometimes, however, you may have object instances on the client side that are not of the same class as the objects on the server side. A typical example could be to have a TOSLFile-BasedDocument object in the server application, which you represent in the client application with a TMyDocument object. In your TMyDocument your could define a method called GetDescriptionOfMyServerDocument which would do this:

pascal TOSLObjectDescription* TMyDocument::
        GetDescriptionOfMyServerDoc()
{
    // Get the doc's name:
    CStr255 name;       // Assuming your client's doc representation
                        // has the this->GetTitle(name);    
                        // same name as the TFileBasedDocument 
                        // in the server

    //Synthesize the document description:
    TOSLFileBasedDocumentDescription* docDecription;
    docDecription = new TOSLFileBasedDocumentDescription;
    docDecription->IOSLFileBasedDocumentDescriptionByName(name);

    return docDecription;   // return the description
}

An even better way to do it would be to let the TMyDocument class inherit from TOSLDocument instead of from TDocument and to define a GetDescriptionOfMe method, similar to the one shown in the next section.

Easy-to-create Descriptions When You Already Have an Object

The OSL Scripting and Recording library also supports automatic generation of a full description of any valid object that is a subclass from the TOSLApplication, TOSLWindow, and so on. Each TOSLObject has a method called GetDescription-OfMeAndMyContainers(), which returns each TOSLObject's "best" and usually, most complete description, containing all of its containers too. The method GetDescriptionOfMeAnd-MyContainers is defined in the TOSLObjectClass and need not be overridden:
pascal TOSLObjectDescription* TOSLObject::
        GetDescriptionOfMeAndMyContainers()
{
    TOSLObjectDescription* myContainers = 
        this->GetDescriptionOfMyContainers();
    TOSLObjectDescription* meAndMyContainers = 
        this->GetDescriptionOfMe(myContainers);
    return meAndMyContainers;
}

The GetDescriptionOfMe method that gets called above must be overridden by its direct subclass. Here's an example of how I do just that when I define a TOSLTable, which inherits directly from TOSLObject:

pascal TOSLObjectDescription* TOSLTable::GetDescriptionOfMe(
        TOSLObjectDescription* theOSLObjectDescription) // override
{
    CStr255 theTableName = this->GetName();
    TOSLTableDescription* anOSLTableDescription = 
        new TOSLTableDescription;
    anOSLTableDescription>IOSLTableDescriptionByName
        (theOSLObjectDescription, theTableName);
    return anOSLTableDescription;
}

Using this strategy is very convenient, both in the client and in the server. A typical situation is this: the client prepares an object description from a "live" object and puts the result into an Apple event and sends it away to the server. Normally, the server resolves the request and returns some data without using object descriptions at all. The only events that return typeObjectSpecifiers (which means that you should use a TOSLObjectDescription) are the kAECreateElement and the kAEClone events.

Easy to Send

All client commands that are subclassed from TAbstract-OSLObjectDescriptionCommand class take a TOSLObject-Description object as a parameter. This command object administrates the sending of the Apple event to the server and handles the response sent from the server in a generic way-it prints the result to the built-in log window (if you have not defined the compile-time variable qWantLogWindow then the window is displayed). If you actually want to put the result of the command inside your own data field, then that's OK too. Then you just have to override two methods to achieve that result: the I-method and the ProcessResult-method. For example, override the IGetServerTableNameCommand to be able to provide your command with a field that is some pointer to a place where you want to store the command's resulting data. Then you override the ProcessResult method to actually use that field to put the data into the client.

So, sending for example a kAEGetData command to the server in order to retrieve the name of the last table in the document "my doc" is no harder than this:

TGetServerTableNameCommand* aGetServerTableNameCommand;
aGetServerTableNameCommand->             
        IGetServerTableNameCommand(tableDescription);

Easy to Garbage Collect

Since we put the AEDescs inside a container (the TOSLObject-Description) we can dispose of them in a controlled manner. And since we actually put the TOSLObjectDescriptions inside the command that we're sending, the command can be (and actually is) responsible for the disposal. So when the command has finished its sending (and the handling of the reply, if necessary), then it's time for it to say "farewell" to the TOSL-ObjectDescription and its contained AEDescs.

THE STRUCTURE OF THE OBJECT DESCRIPTION class

Now, let's inspect the TOSLObjectDescription class a little closer. As you can see from the declaration in the interface of the class below, there are seven magic data members that we want to keep track of.
class TOSLObjectDescription: public TObject
{
private:
    DescType                fObjectSpecifierClass;
    DescType                fPropertyID;
    DescType                fKeyForm;
    AEDesc                  fKeyData;
    AEDesc                  fObjectSpecifier;
    AEDesc                  fPropertySpecifier;
    TOSLObjectDescription*  fItsContainer;

public:
// Construction/Destruction:
    virtual pascal void Initialize();   // override
    
    virtual pascal void IOSLObjectDescription(
        DescType theObjectSpecifierClass,
        TOSLObjectDescription* itsContainer);   
    virtual pascal void IOSLObjectDescriptionByAbsoluteIndex(
        DescType theObjectSpecifierClass,
        TOSLObjectDescription* itsContainer,
        long theIndex); 
    virtual pascal void IOSLObjectDescriptionByAbsolutePosition(
        DescType theObjectSpecifierClass,
        TOSLObjectDescription* itsContainer,
        DescType theAbsolutePosition);  
    virtual pascal void IOSLObjectDescriptionByRelativePosition(
        DescType theObjectSpecifierClass,
        TOSLObjectDescription* itsContainer,
        DescType theRelativePosition);  
    virtual pascal void IOSLObjectDescriptionByName(
        DescType theObjectSpecifierClass,
        TOSLObjectDescription* itsContainer,
        const CStr255& theName);    

    virtual pascal void Free();     // override

// Access:
    virtual pascal TOSLObjectDescription* GetItsContainer();
    virtual pascal AEDesc GetObjectSpecifier();
    virtual pascal DescType GetPropertyID();
    virtual pascal AEDesc GetPropertySpecifier();
    virtual pascal void SetObjectSpecifierClass(
        DescType theObjectSpecifierClass);
    virtual pascal void SetItsContainer(
        TOSLObjectDescription*  itsContainer);
    virtual pascal void SetObjectSpecifier(
        AEDesc theObjectSpecifier);
    virtual pascal void SetPropertyID(DescType thePropertyID);
    virtual pascal void SetPropertySpecifier(
        AEDesc  thePropertySpecifier);
// Action:
    virtual pascal AEDesc DuplicateObjectSpecifier();
    virtual pascal AEDesc DuplicatePropertySpecifier();
    virtual pascal AEDesc DuplicateKeyData();
    virtual pascal void PrepareObjectSpecifierByName(
        const CStr255& theName);
    virtual pascal void PrepareObjectSpecifierByAbsoluteIndex(
        long theIndex);
    virtual pascal void CreateObjectSpecifier();
    virtual pascal void CreatePropertySpecifier(
        DescType thePropertyID);
}

Only three fields are filled with data during the "inner" initialization phase (in the IOSLObjectDescription method) of the object: fObjectSpecifierClass, fPropertyID and fItsContainer. The fObjectSpecifierClass is the object's Apple event registry object class (for example cWindow, cApplication, or cTable). The property ID is the ID of the property we are interested in, for example a pName property. The fItsContainer is this object's outer container. For example, a window description's fItsContainer is very likely an application description.

What about the rest of the fields in the TOSLObject-Description class? Well, they are initialized by the "outer" initialization phase, which is, as seen by the method names, more specific in its nature. For example, the IOSLObject-DescriptionByAbsoluteIndex initialization method requests an absolute index as an extra parameter. Since we specify "absolute index" and supply a value of type long, we can start the actual preparing of the object specifier description. The preparing is (in this case) done in the method PrepareObjectSpecifier-ByAbsoluteIndex, which loads the fields fKeyData with an pure fresh AEDesc of type typeLongInteger and the field fKeyForm gets the constant formAbsolutePosition.

Other examples of "outer" initialization methods are IOSLObjectDescriptionByName, IOSLObjectDescriptionBy-AbsolutePosition, and IOSLObjectDescriptionByRelative-Position.

CREATING YOUR OWN DESCRIPTION CLASSES

OK, so now you want create your own TOSLTableDescription class. What do you have to do? Again, it's quite simple. Say that you would want to describe a table description either by its name or its index. All you would have to do would be to create two I-methods in your new table describer-class, which you would probably name TOSLTableDescription. Each reference form (by name, by index) demands its own I-method.

In the implementation part of IOSLTableDescription-ByAbsoluteIndex you'll only do one thing: call the IOSLObject-DescriptionByAbsoluteIndex method. And the only thing that you do in the IOSLTableDescriptionByName method is to call IOSLObjectDescriptionByName. Here's the class declaration:

class TOSLTableDescription: public TOSLObjectDescription
{
public:
// Construction/Destruction
    virtual pascal void IOSLTableDescriptionByAbsoluteIndex(
        TOSLObjectDescription* itsContainer, long theIndex);
    virtual pascal void IOSLTableDescriptionByName(
        TOSLObjectDescription* itsContainer,
        const CStr255& theName);
}

and here 's the definition:

pascal void TOSLTableDescription::
    IOSLTableDescriptionByAbsoluteIndex(
    TOSLObjectDescription* itsContainer, long theIndex)
{
    this->IOSLObjectDescriptionByAbsoluteIndex
        (cTable,itsContainer,theIndex);
}
pascal void TOSLTableDescription::IOSLTableDescriptionByName(
    TOSLObjectDescription* itsContainer, const CStr255& theName)
{
    this->IOSLObjectDescriptionByName(cTable,itsContainer,theName);
}

THE OSL OBJECT CLASS

Any object in your application that you want to be scriptable must inherit from TOSLObject. What exactly does a TOSL-Object do?

Well, investigating the interface for the TOSLObject might enlighten you a bit (see below). Here you'll see that a TOSLObject always stores a reference to its parent object in the fContainerObject field. Reading the Apple Event Registry gives you detailed information on how you should arrange your object hierarchies in your application. Looking for example at the object cApplication in the Apple Event Registry, this tells you that cApplication has two element classes: cDocument and cWindow. Thus, if you should define your own TOSLWindow (but you don't have to, since it's already supplied with the product), you would know that it should be initialized with an application object in the itsContainer parameter.

class TOSLObject : public TObject
{
private :
TObject* fContainerObject;
public :
// Create / delete
    virtual pascal void Initialize();
    virtual pascal void IOSLObject(TObject* itsContainer);
    virtual pascal void Free();

//Access:
    virtual pascal TObject* GetContainerObject();
    
// TOSLObjectDescription support:
    virtual pascal TOSLObjectDescription*
                GetDescriptionOfMeAndMyContainers();
    virtual pascal TOSLObjectDescription*
                GetDescriptionOfMyContainers();
    virtual pascal TOSLObjectDescription* GetDescriptionOfMe(
                TOSLObjectDescription* theOSLObjectDescription); 
    //subclass this one
(more)
}

Another important feature of the TOSLObject class is its close relationship with TOSLObjectContainer class. It has three methods which all return a TOSLObjectDescription object. The one that you will be calling yourself in your code is the GetDescriptionOfMeAndMyContainers, which calls the other two methods to construct an up-to-date description of the object in question:

pascal TOSLObjectDescription* TOSLObject::
        GetDescriptionOfMeAndMyContainers()
{
    TOSLObjectDescription* myContainers = 
        this->GetDescriptionOfMyContainers();
    TOSLObjectDescription* meAndMyContainers = 
        this->GetDescriptionOfMe(myContainers);
    return meAndMyContainers;
}

You will, however, have to override the GetDescriptionOfMe() method, since I cannot decide for you what the "best" description of a generic object should be. Some objects might be better off by being described with an index, and others may want to be described with names instead (or colors, sounds, IDs, whatever). Since I didn't put any extra fields in the TOSLObject class to minimize the overhead, I simply can't describe the object.

Here's my override of GetDescriptionOfMeAndMyContainers from the TOSLTable class:

pascal TOSLObjectDescription* TOSLTable::GetDescriptionOfMe(
            TOSLObjectDescription* theOSLObjectDescription)
{
    CStr255 theTableName = this->GetName();
    TOSLTableDescription* anOSLTableDescription = 
        new TOSLTableDescription;
    anOSLTableDescription->IOSLTableDescriptionByName(
        theOSLObjectDescription,theTableName);
    return anOSLTableDescription;
}

An alternative in the implementation of the OSL Scripting Components library could have been to define a fName or/and a fIndex field in the TOSLObject class and to provide a fully functional GetDescriptionOfMe method which by default returns for example a description by index.

To give you the best of two worlds, I decided to override TOSLObject with a new class called TOSLSearchableObject, which has these two fields inside. By default I have implemented the GetDescriptionOfMe method, so that it uses its name to describe it. Of course, there's nothing stopping you from overriding this method anyway. But before you do, take a look at TSearchableObject: it works together with a list object and supplies basic functionality of objects in small lists and includes searching and sorting the objects with their name and their index. And since these objects are TOSLObjects, you'll get immediate scriptability. And which application hasn't the need for objects in small lists?

CREATING YOUR OWN OSL OBJECT CLASSES

When you're in the mood for switching to scriptability you want to get it done with-fast. And fast it is: the only thing you have to do is to change the inheritance of those of your classes that you want to have scriptable. If it inherits from TObject then you should change that to TOSLObject instead. If it inherits from TApplication, TDocument, TFile, or any other of the "major" classes of MacApp, then you should inherit your classes from TOSLApplication, TOSLWindow, and TOSL-Document instead.

This way, you immediately can enjoy scriptability in your own application, without writing any new lines!

IMPLEMENTING THE APPLE EVENT COMMUNICATION

If you started like me, you might have experimented with just using MacApp's TAppleEvent class on its own to fire off an event or two. The problem with using the TAppleEvent class on its own, however, is that there's no coordination whatsoever between the TAppleEvent and the MacApp's event handling mechanism-if you send a TAppleEvent to a server destination, and the server sends back a TAppleEvent reply, you're still not sure if the event you receive in the client actually is the reply or a completely different and unrelated event.

TClientCommand, however, uses some features in MacApp's TApplication class to correctly dispatch the reply that is coming back from the server. In fact, MacApp has defined its own commando-constant called cAppleEventReply to be able to dispatch it in its own way, instead of letting the event "get in the programmer's way" into the DoAppleCommand method. When MacApp receives such an event it tries to match it with the events in TApplication's fPendingReplyList, which is a list of all commands that are sent but not yet received. If the incoming event's keyReturnIDAttr is the same as one in the fPending-ReplyList, then MacApp considers it to actually be the reply.

Using TClientCommand and TServerCommand

The idea of command objects is great-a housing for actions that has to be performed, and possibly, undone. In user interface-heavy MacApp programs, you normally use commands because you want to be able to undo user actions. This is not, however, the only time when you would want to work with commands.

Another place where they really fit in is when we're administrating the communication in client/server-based systems. These systems tend to be rather complex: all of the commands that are sent between the client and the server can have different numbers and types of sending and replying parameters. So, to be able to create a simple-to-use but yet powerful Apple event communications architecture, I decided to build it around MacApp's TClientCommand and TServerCommand.

The nice thing about TClient-Command and TServer-Command is that they identify the need to administrate the sending and receiving of a command in just one single place. This way, you'll get the benefit of having one method in the class handling the sending parameters and another method in the same class handling the replies.

The Layers Of Communication

Just as in any communications architecture, the communication is divided into several layers, which handle different things. In the AppleTalk communication there are for example the ADSP, ATP, ASP, and DDP. Here, I have implemented a set of classes that are built on each other to separate the responsibilities for a cleaner design. In the example below, I've just walked through one of the possible ways through the framework-see the simplified class diagram for a more complete picture (see Figure 3). Here's a class diagram for some of the commands in the OSL Scripting Components library. Note that the layers are oriented from left to right, with each column a little bit more "powerful" than its column to the left.):
  • TOSLClientCommand
  • TOSLClientReplyCommand
  • TAbstractOSLObjectDescription
  • TAbstractAEGetDataCommand
  • TStringAEGetDataCommand
  • TGetServerApplicationNameCommand

The TOSLClientCommand is responsible for seeing to it that the command can handle TOSLAppleEvents instead of TAppleEvents.

TOSLClientReplyCommand makes the command into an asynchronous command (if you want that).

The TAbstractOSLDescription uses an objectDescription as a parameter and detaches and frees it when ready.

The TAbstractAEGetDataCommand identifies that it is a kAEGetData core suite command, but leaves it to its subclasses to actually coerce the reply correctly.

TStringAEGetDataCommand gets a string from the server and processes it as a CStr255.

TheGetServerApplicationNameCommand builds a TOSLApplicationDescription automatically and passes along the pName property to the TStringAEGetData command. You should actually use an override of this command to be able to store the retrieved data in a field or wherever is convenient for you.

 
AAPL
$100.85
Apple Inc.
+0.32
MSFT
$45.07
Microsoft Corpora
-0.26
GOOG
$584.58
Google Inc.
-2.28

MacTech Search:
Community Search:

Software Updates via MacUpdate

Tidy Up 3.0.15.0 - Find duplicate files...
Tidy Up is a complete duplicate finder and disk-tidiness utility. With Tidy Up you can search for duplicate files and packages by the owner application, content, type, creator, extension, time... Read more
Parallels Desktop 10.0 - Run Windows app...
Parallels Desktop is simply the world's bestselling, top-rated, and most trusted solution for running Windows applications on your Mac. With Parallels Desktop for Mac, you can seamlessly run both... Read more
Apple Final Cut Pro X 10.1.3 - Professio...
Apple Final Cut Pro X is a professional video editing solution.Completely redesigned from the ground up, Final Cut Pro adds extraordinary speed, quality, and flexibility to every part of the post-... Read more
Apple Compressor 4.1.3 - Adds power and...
Compressor adds power and flexibility to Final Cut Pro X export. Customize output settings, work faster with distributed encoding, and tap into a comprehensive set of delivery features. Powerful... Read more
Chromium 36.0.1985.143 - Fast and stable...
Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all Internet users to experience the web. FreeSMUG-Free OpenSource Mac User Group build is... Read more
Macgo Blu-ray Player 2.10.6.1691 - Blu-r...
Macgo Mac Blu-ray Player can bring you the most unforgettable Blu-ray experience on your Mac. Overview Macgo Mac Blu-ray Player can satisfy just about every need you could possibly have in a Blu-ray... Read more
Apple Motion 5.1.2 - Create and customiz...
Apple Motion is designed for video editors, Motion 5 lets you customize Final Cut Pro titles, transitions, and effects. Or create your own dazzling animations in 2D or 3D space, with real-time... Read more
A Better Finder Rename 9.39 - File, phot...
A Better Finder Rename is the most complete renaming solution available on the market today. That's why, since 1996, tens of thousands of hobbyists, professionals and businesses depend on A Better... Read more
PopChar X 6.6 - Floating window shows av...
PopChar X helps you get the most out of your font collection. With its crystal-clear interface, PopChar X provides a frustration-free way to access any font's special characters. Expanded... Read more
MacUpdate Desktop 6.0.2 - Install Mac ap...
MacUpdate Desktop 6 brings seamless 1-click installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on macupdate.... Read more

Latest Forum Discussions

See All

Frontier Heroes Review
Frontier Heroes Review By Andrew Fisher on August 20th, 2014 Our Rating: :: BLAZES NO TRAILSUniversal App - Designed for iPhone and iPad Despite awesome visuals and great music, Frontier Heroes just doesn’t quite deliver enough fun... | Read more »
Echo Prime is Now on Sale for $0.99
Echo Prime is Now on Sale for $0.99 Posted by Jessica Fisher on August 20th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Star Realms Review
Star Realms Review By Andrew Fisher on August 20th, 2014 Our Rating: :: A STAR IS BORNUniversal App - Designed for iPhone and iPad Star Realms is an excellent adaptation of an outstanding deck-builder. With great visuals and an... | Read more »
This. Is. SPRINGFIELD! War comes to The...
This. Is. SPRINGFIELD! | Read more »
One Tap RPG Review
One Tap RPG Review By Campbell Bird on August 20th, 2014 Our Rating: :: DUNGEON SLIDERUniversal App - Designed for iPhone and iPad This casual arcade game introduces some very light rpg elements into its fantasy-themed pachinko... | Read more »
Goodbye Paywall – Table Tennis Touch Eli...
Goodbye Paywall – Table Tennis Touch Eliminates In-App Purchases Posted by Jessica Fisher on August 20th, 2014 [ permalink ] | Read more »
Go to Bed – An Interview With Touchfight...
Touchfight Games is an exciting new indie studio that was co-formed between game journalist and author Nathan Meunier, artist Leonard Kenyon, and programmer Jon Kenyon. Their debut game Go To Bed will be released this fall, and with all the... | Read more »
Assault Vector Review
Assault Vector Review By Rob Thomas on August 19th, 2014 Our Rating: :: SIMPLE STRATEGYUniversal App - Designed for iPhone and iPad While this hex-based warp ride is fine in theory, Assault Vector is a bit too simple to be... | Read more »
Tom Hanks’ Hanx Writer has Taken the App...
Tom Hanks’ Hanx Writer has Taken the App Store by Storm Posted by Jessica Fisher on August 19th, 2014 [ permalink ] iPad Only App - Designed for the iPad | Read more »
It Came from Canada: Galaxy Dash
With its use of well-established tropes like endless flying and sci-fi space shooting, the upcoming Galaxy Dash: Race to the Outer Run most likely won’t confound expectations. However, with its robust amount of opportunities for fun player... | Read more »

Price Scanner via MacPrices.net

Best Buy’s College Student Deals: $100 off Ma...
Take an additional $100 off all MacBooks and iMacs, $50 off iPad Airs and iPad minis, at Best Buy Online with their College Students Deals Savings, valid through September 6th. Anyone with a valid .... Read more
MacBook Airs on sale for $100 off MSRP, free...
B&H Photo has three 2014 MacBook Airs on sale for $100 off MSRP. Shipping is free, and B&H charges NY sales tax only. They also include free copies of Parallels Desktop and LoJack for Laptops... Read more
Razer Taipan Mouse For Gamers And Non-Gamers...
If you’re a serious gamer on either Mac or Windows PCs, a serious gaming mouse is a necessity for first-tier performance. However, even if like me you’re not much of a gamer, there’s still a strong... Read more
15-inch 2.2GHz MacBook Pro on sale for $1899,...
Adorama has the new 15″ 2.2GHz Retina MacBook Pro on sale for $1899 including free shipping plus NY & NJ sales tax only. Their price is $100 off MSRP, and it’s the lowest price available for this... Read more
Mid-Size Tablet Shootout Posted: iPad mini wi...
I ‘m curious about how many iPads Apple is actually selling these days. It’s been widely rumored and anticipated that new models with A8 SoCs, 2 GB of RAM, 8 megapixel cameras, and fingerprint... Read more
The 15 Biggest iPad Air Problems And How To A...
What’s this? Fifteen “biggest” problems with the iPad Air? Does that mean there are a lot of smaller problems as well? Say it isn’t so! My old iPad 2 has manifested no hardware problems in three... Read more
TYLT Syncable-Duo, 2-in-1 USB Cable With Appl...
TYLT has introduced the Syncable-Duo, a universal cable solution for charging and syncing data to smartphones and tablets. The Syncable-Duo eliminates the need for multiple cables by incorporating... Read more
Save up to $140 off MSRP with Apple refurbish...
Apple is offering Certified Refurbished iPad Airs for up to $140 off MSRP. Apple’s one-year warranty is included with each model, and shipping is free. Stock tends to come and go with some of these... Read more
2.5GHz Mac mini on sale for $549, save $50
B&H Photo has the 2.5GHz Mac mini on sale for $549.99 including free shipping. That’s $50 off MSRP, and B&H will also include a free copy of Parallels Desktop software. NY sales tax only. Read more
Chromebooks Are Actually Sometimes A Better C...
Mac360′s Kate MacKenzie notes that when Steve Jobs announced the post-PC era, he was assuming that the dominant post-PC device would be the iPad he was unveiling, or another Apple product like the... Read more

Jobs Board

Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Solutions Consultant - Apple (United...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** Being a Business Manager at an Apple Store means you're the catalyst for businesses to discover and leverage the power, ease, and flexibility of Apple Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** At the Apple Store, you connect business professionals and entrepreneurs with the tools they need in order to put Apple solutions to work in their Read more
Project Manager / Business Analyst, WW *Appl...
…a senior project manager / business analyst to work within our Worldwide Apple Fulfillment Operations and the Business Process Re-engineering team. This role will work Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.