TweetFollow Us on Twitter

OD Dynamic Updating
Volume Number:12
Issue Number:8
Column Tag:Opendoc

Updating Parts Dynamically With SOM

Fast, automatic inter-part communication

By Jeremy Roschelle

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

As a developer of learning technologies, I am concerned with integrated tools for math and science education. Learners need a wide variety of views, such as meters, graphs, tables, calculators, simulations, data analyzers. Ideally the same object (an accelerated particle, say) can be simultaneously updated in every view.

In the past, all these visualizations would have to be built as a parts of a single monolithic application - a huge effort. With OpenDoc, my colleagues and I are building each view as a separate OpenDoc component. Students can simply drag and drop a particle from a simulation to a graph to a meter. For example, Figure 1 shows a simulation of an oscillating ball, with a vector meter (written by Arni McKinley of Minds in Motion), an adjustable equation (which I wrote), a text editor part, and a graph (by Jim Correia).

The simulation, equation, graph, and vector meter are each separate components, written and compiled at different times, and with different frameworks (PowerPlant, OpenDoc Development Framework, and SamplePart). Yet, as the particle moves, all views instantly update to show its current position, velocity, and acceleration. Furthermore, new views can be added to the system by any developer. For example, Steve Beardslee at Tufts University has written a data collection component that drives a particle with real data from a cart moving on a frictionless track.

Figure 1. A component-based physics lesson

The ability to mix and match components should lead to a rapidly expanding and improving collection of OpenDoc components for math and science education. This article is your opportunity to help education and learn something about OpenDoc at the same time. By the end, you’ll know everything you need to contribute a particle viewer to the suite. In fact, you’ll build a digital meter that drops right into a window and interoperates with the simulation. I guarantee you’ll have a warm, satisfied feeling when you see your very own component working alongside the ones that Arni, Steve, Jim, and I wrote.

The techniques described in this article also apply to many areas besides education. In fact, they will be useful in any application where one kind of data object is displayed in many different views. Examples would be a personal information manager or a financial checkbook program. If you implement the techniques here, OpenDoc could offer your customers some powerful solutions. They could freely mix and match various viewers to suit their needs. You could sell specific advanced editors to specific niche markets. Fast, object-based data sharing could let your company offer a highly integrated solution with many option packages and upgrade potentials.

SOM: A Better Way to Share Data

Using the standard OpenDoc APIs, editors and viewers share data by writing out a stream format either to the clipboard or to a link storage unit. This is similar to the familiar techniques for handling the clipboard, or the System 7 publish-and-subscribe mechanism. An obvious problem with stream-based sharing is speed. The delay is particularly painful if the object is large and the client only needs a small portion of the data. A less obvious problem is that you need to standardize a stream format, which can be hard for objects with complex data. Finally, you can only change the data at the source.

A better way to share data is to share an object. Getting data is done with a method call. For example, I can get the position vector of my particle as follows:

SDataVector position(3);
myParticle->GetPosition(ev,time,position);

The chief advantage of getting data via a method call is speed. A particle might have thousands of data points. Using this method, I get only the data point I need. But there are other advantages as well. Any viewer that has a particle can call its methods to change its data. Furthermore, you can use class inheritance to manage the growing complexity of your objects. For example, my base particle only has data access methods. My sampled particle builds upon this by introducing methods for entering sampled data. My constant acceleration particle adds specific methods for constant acceleration situations. Rather than define one format that meets every need, I can introduce classes that inherit from the core and specialize appropriately for particular situations. Due to the magic of object-oriented programming, older viewers will continue to work with newer, more advanced classes (whereas older viewers would typically crash with newer file formats).

OpenDoc makes it possible to share objects among parts by using the System Object Model (SOM). SOM packages an object binary in a special shared library, where it can be used from many OpenDoc editors, regardless of the compiler or language used to code the editor. Among the numerous advantages of SOM, it solves the fragile base class problem; you can change an object’s interface methods, and older editors will continue to work without your needing to recompile them. While in-depth coverage of SOM is not possible here, you will get a strong feeling for its ease of use and power. Full reference materials for SOM are on the OpenDoc Developer CD and at the OpenDoc Web site.

Becoming a SOM User

Soma: an intoxicating plant juice referred to
in the literature of Vedic ritual.

- Webster’s New Collegiate Dictionary, 1972, p. 1356

Unlike soma, SOM is not a drug. But I’ll admit that SOM’s ability to make component software flow is somewhat intoxicating, even if it does require a few arcane rituals.

To use a SOM-based class, you first include its header, which has the .xh suffix. This is just like including the .h header for a C++ class. You call the methods of the class, including new and delete, just as you would for an ordinary C++ class. You must also include the SOM shared library in your project.

The example below uses the particle class to calculate the position of a ball that is thrown in the air for 10 seconds.

Calculate Position
#include "EduConstAccParticle.xh"

double CalculatePosition(Environment *ev)
{
 double finalPos = 0.0;
 EduConstAccParticle *p = nil;
SOM_TRY
 p = new EduConstAccParticle;
    // SDataVector is a C++ utility class that makes it easy
    // to manipulate vectors. 1 is the number of dimensions.
    // SDataVector uses 1-based indexing, because I have trouble thinking
    // of the x-axis as the zeroeth dimension of a 3D vector.
 SDataVector pos(1), vel(1), acc(1);
 pos[1] = 0.0;
 vel[1] = 300.0;
 acc[1] = -9.8;
 
    // set to constant acceleration motion, with 0.0 initial position and 300.0 m/s
    // and gravitational acceleration.
 p->SetConstantAcceleration(ev,pos,vel,acc);

    // get the value at 10 seconds
 finalPos = p->Get1Position(ev,10.0,1);
 delete p;
SOM_CATCH_ALL
 delete p;
 RERAISE;
SOM_ENDTRY
 return finalPos;
}

One difference you will note in using SOM is the first argument, which is always an environment pointer. The environment is a struct that SOM uses to return exceptions. The environment is necessary because a SOM object may not be in the same address space as its caller, and thus may not be able to throw an ordinary exception. Upon returning from SOM code you should always check the environment for errors. To make this easy, most SOM headers are produced with an option (CHECK-ENV) that automatically converts the environment into a C++ exception if it contains an error. Thus your code can catch SOM-based exceptions and C++-based exceptions with the same try-catch blocks. You should always have at least one exception handler active at the time you call a SOM method (lest you abort your process upon the first bad environment that is returned).

Since you now know how to call SOM methods - it’s not very different from C++ - we can move to the real issue: how can particle classes be designed to allow multiple live views?

Sharing Particles: A Class Act

My goal is to enable multiple components, like simulations, graphs, and tables, to simultaneously display the data of a single object. To accomplish this, I use the model-view-controller strategy (Figure 2), made famous in SmallTalk. In this strategy, the controller (a simulation) makes changes to the model (a particle), and the particle notifies all its views (graphs, meters, tables). The views call the particle’s methods to fetch the appropriate data for updating the display.

Figure 2. Model-View-Controller Architecture

Three classes are needed to implement the strategy in OpenDoc:

1. EduObject: a sharable object. (EduParticle inherits from EduObject.)

2. EduObjectListener: registers for change notification with a particular EduObject.

3. EduObjectMgr: publishes EduObjects to other components.

Each display will have a pointer to an EduObject. A meter, for example, gets a pointer to an EduParticle by receiving a reference in a drag. The method EduObjectListener::GetObject uses the particle reference to get access to the EduParticle, and the meter uses this EduParticle to get data it can display.

The simulation changes a particle by calling its methods. For example, SetCurrentTime changes the simulation time for the particle. This method will also broadcast a message to every EduObjectListener. When a meter receives the message in its listener, for example, it will call EduParticle methods to extract data and update its display. Every time the simulation changes a particle, all the listeners will be notified and will update their displays.

Finally, it is desirable for the meter to remember its particle when the document is saved, so that the link between the simulation and the meter will persist when the document is opened again. Since the particle is a pointer, it cannot be saved directly. Instead, the meter’s listener writes out a reference to the particle (in exactly the same format used to receive a drag). When it is opened, the meter can get the particle object using the GetObject method as described above.

The listings below are an abridged version of the interface definitions which specify the API to the classes. These are written in SOM’s Interface Definition Language (IDL). IDL reads almost like a C++ class definition, except that each argument is explicitly marked for direction of data flow: in, out, or inout.

EduObject Interface
interface EduObject : SOMObject
{
    // abridged interface definition

    // change notification methods
 void RegisterListener(in EduObjectListener listener);
 void UnRegisterListener(in EduObjectListener listener);
 void TellListeners(in long message, inout ODByteArray param);
 
    // support for dragging: externalizes a reference to this object.
    // storage should be the drag storage unit,
    // EduObjectListener has a method for internalizing this from storage
 void ExternalizeReference(in ODStorageUnit storage);
};


EduParticle Interface
// inherits from EduObject
interface EduParticle : EduObject
{ 
    // abridged interface definition

    // inherits all EduObject methods
    // EduConstAccParticle and EduSampledPosParticle 
    // add methods for setting data values
  
    // access to data values
 void GetPosition(in double time, inout EduDataVector vector);
 void GetVelocity(in double time, inout EduDataVector vector);
 void GetAcceleration(in double time, inout EduDataVector vector);
 
    // time related information
 double GetCurrentTime();
 void SetCurrentTime(in double time);
};

EduObjectListener Interface
interface EduObjectListener : SOMObject
{
    // abridged interface definition

    // messages are received here
 void ListenToMessage(in long message, inout ODByteArray param);
 
    // support for receiving drags
    // call HasValidObjectReference to see if you should accept this drag
    // call InternalizeReference to receive the drag
 ODBoolean HasValidObjectReference(
 in ODStorageUnit dragStorage);
 void InternalizeReference(in ODStorageUnit dragStorage);

    // utility methods for storing and retrieving a particle reference
    // in this components storage unit (the OpenDoc generalization of a file)
 void Externalize(
 in ODStorageUnit su, 
 in ODDraftKey key, 
 in ODFrame scopeFrame);

 void Internalize(in ODStorageUnit su);
  
 
    // get the object associated with this listener
    // may return nil if no object is connected.
 EduObject GetObject();
};

EduObjectMgr Interface
interface EduObjectMgr : ODExtension
{
    // abridged interface definition
 
    // called from another component to get an object
 EduObject GetObject(in long id);
 
    // called from within this component to publish objects
 void AddObject(in EduObject object);
 void RemoveObject(in EduObject object);
};

This article takes advantage of the encapsulation offered in these classes by separating the details of use from implementation. I’ll start with how to use the classes, since that is really easy. At the end, I’ll draw back the curtain and reveal how it works.

Building a DigitalMeter Component

The simplest way to make an OpenDoc component is with PartMaker (available at http://www.opendoc.apple.com). PartMaker will generate a complete CodeWarrior project, ready to compile and run. We’ll start with SamplePart, a very simple “Hello, World!” type component, and modify it to be a digital meter for a particle. To begin, drop SamplePart C++ onto PartMaker. Name your project “DigitalMeter”. You can use whatever company name you like. When PartMaker finishes grinding away, you will have a new folder named DigitalMeter.

Open the project DigitalMeter.µ, and perform a make. This will produce a new OpenDoc component. Make an alias to this component and put the alias in your Editors folder. Then drop the component onto the OpenDoc icon (found in System Folder:Extensions:OpenDoc Libraries:) to make stationery. The stationery will appear in your Stationery Folder. You should be able to double-click this stationery in order to open your brand new component. Yeah!

Now you should download EduObject and EMSim from my Web site, using the EMDemo link (http://www.slip.net/~jeremy/). Drop the EduObject library, and EMSim and EMMeter parts, into your Editors folder. You will also need ODFDraw or KickStart Draw, as I use these as a generic container. You can get them from the OpenDoc CD or Web site. To see if you have everything installed correctly, try opening the Example document. You should see the simulation and my existing meter. Select the simulation, and choose Go from its menu. The simulation will run and the meter will update.

Drag your Digital Meter stationery into the Example document. Your component will be added to the window. Now all we need to do is to make your DigitalMeter act like it really is a meter. There are five steps involved:

1. Creating a particle listener

2. Displaying particle data

3. Receiving a particle drag

4. Listening to particle changes

5. Saving a particle reference

1. Creating a particle listener

To begin, add the EduObject library to your project (it is a shared library so it won’t add its code into your component). Then add the SDataVector.cp file to your project.

The code below creates an EduObjectListener in your DigitalMeter. You need a listener because it is both your handle to the EduParticle (which is created by the simulation part) and your way to receive messages when the particle changes. As you can see, creating a SOM object is no big deal.

DigitalMeter::Initialize

void DigitalMeter::Initialize( Environment* ev )
{
 SOM_Trace("DigitalMeter","Initialize");
  
 fListener = new EduObjectListener;
 fListener->InitEduObjectListener(
 ev,fSelf,ListenToMessage,(unsigned long)this);
 CopyPascalString(fCurrentValue,"\pDrag a particle here");
    // unchanged code below here, omitted for clarity
}

DigitalMeter::ReleaseAll 

void DigitalMeter::ReleaseAll( Environment* ev )
{
 SOM_Trace("DigitalMeter","ReleaseAll");
 delete fListener;
    // unchanged code below here, omitted for clarity
}

Below I’ve listed the additions to the DigitalMeter header for all the code in this article:

// original top of header omitted for clarity
// my changes below
#include <EduObjectListener.xh> /
#include "ListenerHelper.h"
#include <EduParticle.xh> //+
#include "SDataVector.h"
#include <DgItmIt.xh>
#include <FacetItr.xh>
#include <stdio.h>

class DigitalMeter {
    // new methods
 public:
 static void ListenToMessage(
 Environment *ev, unsigned long inRefCon, 
 long inMessage, ODByteArray *ioParam);
 
 ODBooleanIsAcceptable(
 Environment *ev, ODDragItemIterator *dragInfo);
 void   ReceiveDrop(
 Environment *ev, ODDragItemIterator *dragInfo);
 
 void   UpdateDisplay(Environment *ev);
 void   DrawAll(Environment *ev);
 
    // new members
 private:
 EduObjectListener *fListener;
 Str255 fCurrentValue;
    // unchanged class definition below here, omitted for clarity
};

2. Digital display

The digital display just draws a string containing the position of the particle. It gets the particle from the listener. (You will see how the listener gets the particle in the next section.) The string is stored in the fCurrentValue member, which will be set by the UpdateDisplay method. Once the string is built, the UpdateDisplay method calls DrawAll, which redraws every facet of every frame with the new string.

DigitalMeter::UpdateDisplay

void DigitalMeter::UpdateDisplay(Environment *ev)
{
 EduParticle *particle = (EduParticle *)
 fListener->GetObject(ev);
 if (! particle) return;
 
    // get current position
 double time = particle->GetCurrentTime(ev);
 SDataVectorpos(2);
 particle->GetPosition(ev,time,pos);
 
    // use ANSI routines to write numbers into Pascal string
 char *output = (char *)&fCurrentValue[1];
 sprintf(output, "%.1f,%.1f",pos[1],pos[2]);
    // set length of Pascal string
 fCurrentValue[0] = strlen(output);
 
 DrawAll(ev);
}

DigitalMeter::DrawFrameView
void DigitalMeter::DrawFrameView(
 Environment* ev,ODFacet* facet )
{
    // setup text characteristics
 TextSize(12);
 TextFont(1);
 TextFace(bold);
 PenNormal();    
 FontInfo finfo;
 GetFontInfo(&finfo);
 MoveTo(0,finfo.ascent + 2);
 
 EraseRect(&port->portRect);
 DrawString(fCurrentValue);
}

DigitalMeter::DrawAll 
void DigitalMeter::DrawAll(Environment *ev)
{
    // iterate through frames, then facets
 CListIterator fiter(fDisplayFrames);
 for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First();
 fiter.IsNotComplete(); 
 proxy = (CFrameProxy*) fiter.Next() )
 {
    // draw only frames in RAM, not any still on disk
 if (proxy->FrameIsLoaded(ev)) {
 ODFrame*frame = proxy->GetFrame(ev);
    // iterate through facets
 ODFrameFacetIterator *iter =
 frame->CreateFacetIterator(ev);
 
 for (ODFacet *facet = iter->First(ev);
  iter->IsNotComplete(ev);
  facet = iter->Next(ev)) {
 
 Draw(ev,facet,kODNULL);   
  
 }
 delete iter;
 }
 }
}

3. Its a drag!

The DigitalMeter’s listener initially learns about a particle by receiving a dragged particle reference. To receive any drag, each frame must have its flag set correctly. We set the flag when your frame is added or connected:

DigitalMeter::DisplayFrameAdded

void DigitalMeter::DisplayFrameAdded(
 Environment* ev, ODFrame* frame )
{
 SOM_Trace("DigitalMeter","DisplayFrameAdded");
 frame->SetDroppable(ev,kODTrue);
    // unchanged code below here, omitted for clarity
}

DigitalMeter::DisplayFrameConnected

void DigitalMeter:: DisplayFrameConnected (
  Environment* ev, ODFrame* frame )
{
 SOM_Trace("DigitalMeter","DisplayFrameConnected");
 frame->SetDroppable(ev,kODTrue);
    // unchanged code below here, omitted for clarity
}

The basic principle of receiving a dragged particle is simple. When something is dragged into your frame, OpenDoc will call DragEnter and pass in a storage unit that contains the dragged content. If your part decides the drag is acceptable, then when the user releases the drag, your receive drop method will be called. EduObjectListener has two methods for receiving drags. HasValidObjectReference returns true if the listener can accept the drag, and InternalizeObjectReference actually handles the drop.

DigitalMeter::IsAcceptable
ODBoolean DigitalMeter::IsAcceptable(
 Environment *ev, ODDragItemIterator *dragInfo)
{
 ODStorageUnit *sue = dragInfo->First(ev);
 return fListener->HasValidObjectReference(ev,sue);
}

DigitalMeter::ReceiveDrop
void    DigitalMeter::ReceiveDrop(
 Environment *ev, ODDragItemIterator *dragInfo)
{
 ODStorageUnit *sue = dragInfo->First(ev);
 fListener->InternalizeReference(ev,sue);
 SetDirty(ev);
 UpdateDisplay(ev);
}

Unfortunately, SamplePart has only stub methods for receiving drops, so we have to delve into the SOM class implementation of the part. The code below looks funny, but is basically just a C++ function implementation with a little extra work to interface with SOM. As with all methods in the SOM class implementation, this code just sets up exception handling and then delegates the work to the DigitalMeter class. For simplicity’s sake, drag hiliting is not implemented here.

som_DigitalMeter__DragEnter

SOM_Scope ODDragResult SOMLINK som_DigitalMeter__DragEnter(
 PartCo_som_DigitalMeter *somSelf, Environment *ev,
 ODDragItemIterator* dragInfo, ODFacet* facet,
 ODPoint* where)
{
 PartCo_som_DigitalMeterData *somThis = 
 PartCo_som_DigitalMeterGetData(somSelf);
 
 ODBooleanresult = kODFalse;
 SOM_TRY
 result = _fPart->IsAcceptable(ev,dragInfo);
 SOM_CATCH_ALL
 SOM_ENDTRY
 return result;
}

som_DigitalMeter__DragWithin
SOM_Scope ODDragResult  SOMLINK som_DigitalMeter__DragWithin(
 PartCo_som_DigitalMeter *somSelf, Environment *ev,
 ODDragItemIterator* dragInfo, ODFacet* facet,
 ODPoint* where)
{
    // same as DragEnter
 return somSelf->DragEnter(ev,dragInfo,facet,where);
}

som_DigitalMeter__Drop
SOM_Scope ODDropResult  SOMLINK som_DigitalMeter__Drop(
 PartCo_som_DigitalMeter *somSelf, Environment *ev,
 ODDragItemIterator* dropInfo, ODFacet* facet,
 ODPoint* where)
{
 PartCo_som_DigitalMeterData *somThis = 
 PartCo_som_DigitalMeterGetData(somSelf);
 
 SOM_TRY
 _fPart->ReceiveDrop(ev,dropInfo);
 SOM_CATCH_ALL
 SOM_ENDTRY
 return kODDropCopy;
}

4. Listening in

Now we get to the easy part. Every time the particle changes, your DigitalMeter will get a message. The ListenToMessage method simply calls UpdateDisplay to display the new values

DigitalMeter::ListenToMessage 
void
DigitalMeter::ListenToMessage(
 Environment *ev, unsigned long refCon,
 long inMessage, ODByteArray *ioParam)
{
 DigitalMeter  *me = (DigitalMeter *)refCon;
 switch (inMessage) {
 case EduParticle_msgDataChanged:
 case EduParticle_msgTimeChanged:
 me->UpdateDisplay(ev);
 break;
 default:
 break; 
 } 
}

5. Being persistent

As a final touch, it’s nice to store the particle reference in your part’s content, so that the same particle can be displayed in the meter when the document is opened the next time. EduObjectListener handles the work of doing this for you.

DigitalMeter::InternalizeContent 
void 
DigitalMeter::InternalizeContent(
 Environment* ev, ODStorageUnit* storageUnit )
{
 if (ODSUExistsThenFocus(ev,storageUnit, 
 kODPropContents,kDigitalMeterKind )) {
 fListener->Internalize(ev,storageUnit);     
 UpdateDisplay(ev);
 }      
}

DigitalMeter::ExternalizeContent 
void
DigitalMeter::ExternalizeContent( 
 Environment* ev, ODStorageUnit*   storageUnit,
 ODDraftKey key, ODFrame* scopeFrame)
{
 ODSUForceFocus(ev, storageUnit,
 kODPropContents,kDigitalMeterKind);
 fListener->Externalize(ev,storageUnit,key, scopeFrame);
}

After entering these changes, make your part and reopen the Example document. Try to drag a particle into your digital meter. You should begin seeing live updates!

How It Works: A Look Behind the Scenes

You might by now be wondering how EduObject and its related classes do their work. The key magic is really provided by SOM. Each EduObject class implementation is compiled into the EduObject library. Each component dynamically links to the library at run time. This enables each component to share the same EduObject code for particles and listeners.

To implement the classes, I first wrote the interface definitions (IDL files). I then submitted these to the SOM compiler, which emits .xh, .xih, and .cpp files. The .cpp file has method stubs, which I filled in with C++ implementations of each method. I compiled these into the shared library and exported each SOM class data structure.

If you want to explore the class implementations in depth, you can read the full sources, which you received when you downloaded EduObject from my Web page. Here I can only provide a quick tour of some of the key concepts behind dragging, persistence, and change notification.

To perform a drag in OpenDoc, you write data into a storage unit that represents the content of the drag. The storage unit can contain multiple flavors of data, each in a separate value. EduObject has an ExternalizeReference method which writes down two pieces of information, the ID of its containing part and the ID of the particle within the part.

EduObjectExternalizeReference

SOM_Scope void  SOMLINK EduObjectExternalizeReference(
 EduObject *somSelf, Environment *ev,
 ODStorageUnit* storage)
{
 EduObjectData *somThis = EduObjectGetData(somSelf);
SOM_TRY
 ODSUForceFocus(ev,storage,
 kODPropContents,EduObject_ReferenceKind);
    // write partID
 ODPart *part = (ODPart *)_fMgr->GetBase(ev);
 long partID = part->GetID(ev);
 StorageUnitSetValue(storage,ev,sizeof(long), &partID);
    // write objectID
 long objectID = somSelf->GetID(ev);
 StorageUnitSetValue(storage,ev,sizeof(long), &objectID);
SOM_CATCH_ALL
SOM_ENDTRY
}

To receive the drag, EduObjectListener:: HasValidObjectReference just checks to see that the correct value type (flavor) is available in the drag. EduObjectListener::InternalizeObjectReference retrieves the part ID and the object ID. (Persistence basically works the same way: Externalize writes down a part ID and object ID, and Internalize reads these numbers back in.)

EduObjectListenerHasValidObjectReference
SOM_Scope ODBoolean  SOMLINK EduObjectListenerHasValidObjectReference(
 EduObjectListener *somSelf, Environment *ev,
 ODStorageUnit* dragStorage)
{
 EduObjectListenerData *somThis =  
 EduObjectListenerGetData(somSelf);
  
 ODBooleanresult = kODFalse;

SOM_TRY
 result = dragStorage->Exists(ev,
 kODPropContents,EduObject_ReferenceKind,0);
SOM_CATCH_ALL
SOM_ENDTRY
 
 return result;
}

EduObjectListenerInternalizeReference
SOM_Scope void  SOMLINK EduObjectListenerInternalizeReference(
 EduObjectListener *somSelf, Environment *ev, 
 ODStorageUnit* dragStorage)
{
 EduObjectListenerData *somThis = 
 EduObjectListenerGetData(somSelf);

SOM_TRY
 if (ODSUExistsThenFocus(ev,dragStorage,
 kODPropContents,EduObject_ReferenceKind)) {
 StorageUnitGetValue(dragStorage,ev,sizeof(long),&_fPartID);
 StorageUnitGetValue(dragStorage,ev,sizeof(long),&_fObjectID);
SOM_CATCH_ALL
SOM_ENDTRY
}

Now how do we actually get a particle from the simulation to the meter? There is no way to directly get a particle (or any other object) out of an OpenDoc part; the ODPart interface simply does not have a method for returning particles! Therefore it is necessary to extend ODPart with an ODExtension. EduObjectMgr is an extension that adds a specific method for returning the object with a specific ID. This extension effectively publishes a set of particles so that other components can see them.

Within your meter, EduObjectListener::GetObject works as follows: First it uses the part ID to retrieve the ODPart that contains the particle. Then it gets the EduObjectMgr extension from that part. Finally, it asks the extension to return the particle with the specified object ID.

EduObjectListenerGetObject

SOM_Scope EduObject*  SOMLINK EduObjectListenerGetObject(
 EduObjectListener *somSelf, Environment *ev)
{
 EduObjectListenerData *somThis = 
 EduObjectListenerGetData(somSelf);
    // return immediately if it is in the cache
 if (_fObject) return _fObject;

 ODPart *part = kODNULL;
 EduObjectMgr *ex = kODNULL;
SOM_TRY
    // To get the particle, we first get its owner part
 part = _fThisPart->GetStorageUnit(ev)->
 GetDraft(ev)->AcquirePart(ev,_fPartID);
 
 ODISOStr extName = EduObjectMgr_ExtensionName;
 if (part!= kODNULL && part->HasExtension(ev,extName)) {
    // now get the extension of the part, and use it to get the object
 ex = (EduObjectMgr*)part->AcquireExtension(ev,extName);
 _fObject = ex->GetObject(ev,_fObjectID);
 ex->Release(ev);
 ex = kODNULL;
 }
 part->Release(ev);
 part = kODNULL; 
 
    // register to receive change notification
 if (_fObject)
 _fObject->RegisterListener(ev,somSelf);

SOM_CATCH_ALL
 if (part) part->Release(ev);
 if (ex) ex->Release(ev);
SOM_ENDTRY   
 return _fObject;
}

When a EduObjectListener has an EduObject, it calls EduObject::RegisterListener. The EduObject simply stores all registered listeners in a list. EduObject:: TellListeners will transmit a message to every listener in the list. For example, when SetCurrentTime is called for a particle, it in turn calls TellListeners with msg_TimeChanged.

EduParticleSetCurrentTime
SOM_Scope void  SOMLINK EduParticleSetCurrentTime(
 EduParticle *somSelf, Environment *ev, double time)
{
 EduParticleData *somThis = EduParticleGetData(somSelf);
 _fTime = time;
 
 SOM_TRY
 ODByteArrayba;
 ba._length = ba._maximum  = 0;
 ba._buffer = kODNULL;
 somSelf->TellListeners(ev,msgTimeChanged,&ba);
 SOM_CATCH_ALL
 SOM_ENDTRY
}

EduObjectTellListeners

SOM_Scope void  SOMLINK EduObjectTellListeners(
 EduObject *somSelf, Environment *ev,
 long message, ODByteArray* param)
{
 EduObjectData *somThis = EduObjectGetData(somSelf);
 if (! _fIsTelling) return;
SOM_TRY
 ListenerIter  iter(_fListenerList);
 for(   EduObjectListener *thisListener = iter.First(); 
 iter.IsNotComplete();
 thisListener = iter.Next()) {
 thisListener->ListenToMessage(ev,message,param);
 }
SOM_CATCH_ALL
SOM_ENDTRY
}

Conclusion: Towards Fast, Flexible Suites

The model-view-controller technique used here to implement fast, object-based data sharing is a common technique within many monolithic applications. It is powerful because it separates the data model (particle) from its interface (viewer components), while providing change notification techniques (listeners) to keep everything nicely synchronized. It allows a suite of editors and viewers all to operate and display the same data.

Before OpenDoc, there was no way to use this technique outside the bounds of a monolithic application. SOM, in particular, makes it possible to share objects across separately authored and compiled components. SOM makes it possible to distribute an object library and the object interfaces for a specific data type. Then anybody can write a component to view and edit that type of data. (And in the future, with DSOM, objects can even be distributed across the Internet).

OpenDoc and SOM provide a powerful way to enable a group of small developers to collectively produce a software suite. For example, four projects have already contributed educational components based on EduObject. Together we are quickly building a powerful suite for physics and calculus, each contributing based on our particular strengths. The components in this suite integrate smoothly, and will provide teachers much flexibility in customizing their presentation to their students.

(You can contribute an educational part too! How about a nice analog clock, or a strip chart, or a speedometer? If you feel inspired, I’d love to see a copy of the result.)

Notice, however, that the specifics of particles are completely separated from the general interfaces that enable data sharing. You could use EduObject, EduObjectListener, and EduObjectMgr with any kind of object - a financial transaction, a personal contact record, or a calendar entry. To do so, just define a new class that inherits from EduObject.

Moreover, there is no reason to limit this power to groups of small developers who are building a suite. A larger developer that produces a deep and rich collection of editors can benefit as well. If I were building a Personal Information Manager today, I would build each contact entry as an object, and make each possible view (e.g. calendar, address book, to-do list) be a separate component. The obvious benefit is that then I could provide customers with custom solutions by substituting components. Some might like a more powerful calendar, whereas others have specific address-book requirements. Moreover, the larger developer could use components to manage the upgrade process; each component can be separately upgraded without forcing a recompile of the project. The developer could even contract some components out, while keeping the core object and components in-house. The flexibility of components allows some powerful marketing opportunities, and the architecture of OpenDoc and SOM supports much quicker time-to-market with custom solutions.

Apple, IBM and the other OpenDoc partners have delivered a powerful architecture for developers to finally realize the promise of object technology - reuse, plug-and-play integration, and smooth evolution. Now we need tool developers to seize the day and make this architecture as accessible as the stand-alone application. Then perhaps, some day soon, reusable, high-quality educational components will be as familiar to every child as their first Lego kit, model train set, or hi-fi stereo system.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Sandvox 2.10.2 - Easily build eye-catchi...
Sandvox is for Mac users who want to create a professional looking website quickly and easily. With Sandvox, you don't need to be a Web genius to build a stylish, feature-rich, standards-compliant... Read more
LibreOffice 5.0.1.2 - Free, open-source...
LibreOffice is an office suite (word processor, spreadsheet, presentations, drawing tool) compatible with other major office suites. The Document Foundation is coordinating development and... Read more
f.lux 36.1 - Adjusts the color of your d...
f.lux makes the color of your computer's display adapt to the time of day, warm at night and like sunlight during the day. Ever notice how people texting at night have that eerie blue glow? Or wake... Read more
VirtualBox 5.0.2 - x86 virtualization so...
VirtualBox is a family of powerful x86 virtualization products for enterprise as well as home use. Not only is VirtualBox an extremely feature rich, high performance product for enterprise customers... Read more
File Juicer 4.43 - Extract images, video...
File Juicer is a drag-and-drop can opener and data archaeologist. Its specialty is to find and extract images, video, audio, or text from files which are hard to open in other ways. In computer... Read more
Apple MainStage 3.2 - Live performance t...
MainStage 3 makes it easy to bring to the stage all the same instruments and effects that you love in your recording. Everything from the Sound Library and Smart Controls you're familiar with from... Read more
formZ 8.5 - Solid and surface modeler. (...
formZ pro is a powerful 3D design application featuring a variety of modeling personalities and tools with an easy to use interface to express and communicate your imagination. It is based on... Read more
PopChar 7.2 - Floating window shows avai...
We're also selling a 5-license family pack for only $25.99! PopChar helps you get the most out of your font collection. With its crystal-clear interface, PopChar provides a frustration-free way to... Read more
FileMaker Pro 14.0.2 - Powerful, easy-to...
FileMaker Pro is powerful, easy-to-use software used to create custom solutions for your business that run on iPad, iPhone, Windows, Mac and the web. Use FileMaker Pro to manage and share information... Read more
djay Pro 1.2 - Transform your Mac into a...
djay Pro provides a complete toolkit for performing DJs. Its unique modern interface is built around a sophisticated integration with iTunes and Spotify, giving you instant access to millions of... Read more

ReBoard: Revolutionary Keyboard (Utilit...
ReBoard: Revolutionary Keyboard 1.0 Device: iOS Universal Category: Utilities Price: $1.99, Version: 1.0 (iTunes) Description: Do everything within the keyboard without switching apps! If you are in WhatsApp, how do you schedule a... | Read more »
Tiny Empire (Games)
Tiny Empire 1.1.3 Device: iOS Universal Category: Games Price: $2.99, Version: 1.1.3 (iTunes) Description: Launch cannonballs and blow tiny orcs into thousands of pieces in this intuitive fantasy-themed puzzle shooter! Embark on an... | Read more »
Astropad Mini (Productivity)
Astropad Mini 1.0 Device: iOS iPhone Category: Productivity Price: $4.99, Version: 1.0 (iTunes) Description: *** 50% off introductory price! ​*** Get the high-end experience of a Wacom tablet at a fraction of the price with Astropad... | Read more »
Emo Chorus (Music)
Emo Chorus 1.0.0 Device: iOS Universal Category: Music Price: $1.99, Version: 1.0.0 (iTunes) Description: Realistic Choir simulator ranging from simple Chorus emulation to full ensemble Choir with 128 members. ### introductory offer... | Read more »
Forest Spirit (Games)
Forest Spirit 1.0.5 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0.5 (iTunes) Description: | Read more »
Ski Safari 2 (Games)
Ski Safari 2 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: The world's most fantastical, fun, family-friendly skiing game is back and better than ever! Play as Sven's sister Evana, share... | Read more »
Lara Croft GO (Games)
Lara Croft GO 1.0.47768 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.47768 (iTunes) Description: Lara Croft GO is a turn based puzzle-adventure set in a long-forgotten world. Explore the ruins of an ancient... | Read more »
Whispering Willows (Games)
Whispering Willows 1.23 Device: iOS Universal Category: Games Price: $4.99, Version: 1.23 (iTunes) Description: **LAUNCH SALE 50% OFF** - Whispering Willows is on sale for 50% off ($4.99) until September 9th. | Read more »
Calvino Noir (Games)
Calvino Noir 1.1 Device: iOS iPhone Category: Games Price: $3.99, Version: 1.1 (iTunes) Description: The film noir stealth game. Calvino Noir is the exploratory, sneaking adventure through the 1930s European criminal underworld.... | Read more »
Angel Sword (Games)
Angel Sword 1.0 Device: iOS Universal Category: Games Price: $6.99, Version: 1.0 (iTunes) Description: Prepare to adventure in the most epic full scale multiplayer 3D RPG for mobile! Experience amazing detailed graphics in full HD.... | Read more »

Price Scanner via MacPrices.net

12-inch 1.1GHz Gold MacBook on sale for $1149...
B&H Photo has the 12″ 1.1GHz Gold Retina MacBook on sale for $1149.99 including free shipping plus NY sales tax only. Their price is $150 off MSRP, and it’s the lowest price available for this... Read more
27-inch 3.3GHz 5K iMac on sale for $1849, sav...
Best Buy has the 27″ 3.3GHz 5K iMac on sale for $1849.99. Their price is $150 off MSRP, and it’s the lowest price available for this model. Choose free shipping or free local store pickup (if... Read more
Worldwide Tablet Shipments Expected to Declin...
Does Apple badly need a touchscreen convertible/hybrid laptop MacBook? Yes, judging from a new market forecast from the International Data Corporation (IDC) Worldwide Quarterly Tablet Tracker, which... Read more
Continued PC Shipment Shrinkage Expected Thro...
Worldwide PC shipments are expected to fall by -8.7 percent in 2015 and not stabilize until 2017, according to the latest International Data Corporation (IDC) Worldwide Quarterly PC Tracker data. The... Read more
Imminent iPhone 6s Announcement Leads To 103%...
NextWorth Solutions, with its online and in-store electronics trade-in programs including http://NextWorth.com, reports that it has experienced a 103 percent surge in quoted trade-in values over the... Read more
Weekend Deal: 13-inch Retina MacBook Pros for...
Save up to $100 on the purchase of a new 2015 13″ Retina MacBook Pro at the following resellers this weekend. Shipping is free with each model: 2.7GHz/128GB MSRP $1299 2.7GHz/... Read more
13-inch 2.5GHz MacBook Pro on sale for $999,...
B&H Photo has the 13″ 2.5GHz MacBook Pro on sale for $999.99 including free shipping plus NY sales tax only. Their price is $100 off MSRP. Read more
Apple refurbished iPad Air 2s available for u...
The Apple Store has Apple Certified Refurbished iPad Air 2s available for up to $140 off the price of new models. Apple’s one-year warranty is included with each model, and shipping is free: - 128GB... Read more
The ‘Book Mystique Still Magic After All Thes...
This column has been called The ‘Book Mystique for what has been a 15 year run so far, and I have no interest in changing the name. The reference is to what I’ve observed is a the near magical... Read more
15-inch Retina MacBook Pros on sale for up to...
B&H Photo has 2015 15″ Retina MacBook Pros on sale for up to $184 off MSRP including free shipping plus NY sales tax only: - 15″ 2.2GHz Retina MacBook Pro: $1815 $184 off - 15″ 2.5GHz Retina... Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
Software QA Engineer, *Apple* Pay Security...
Changing the world is all in a day039s work at Apple . If you love innovation, here039s your chance to make a career of it. You039ll work hard. But the job comes with Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.