Title Banner

Previous Book Contents Book Index Next

Inside Macintosh: OpenDoc Cookbook /
Chapter 2 - SamplePart Tutorial


Persistent Storage

Persistent storage is a way to retain data on a long-term basis, supported by a nonvolatile device such as a hard disk. Persistent data remains stable between computing sessions. All persistent storage in OpenDoc is represented by storage units (ODStorageUnit), which provide a standard, cross-platform interface for all persistent objects. Every object in OpenDoc that needs to maintain its state between sessions is a persistent object, and each has a storage unit. Part objects must handle their storage units in a particularly disciplined manner because they need to satisfy many more requirements than other persistent objects.

Storage units have any number of properties, which are like separate forks of files, and properties have any number of values, which are separate streams of each fork. Each value in the same property holds a different representation of the same data; it should not hold different data. For example, every part has a contents property (kODPropContents), and multiple representations of the content can be stored in different values, but only content data should be stored in the contents property.

If parts have multiple representations of their content, they must write them to storage in order of fidelity. For example, a part's most faithful representation of text may be styled text, while a lower fidelity representation of the same content would be plain ASCII text, a separate value for which would be added later to the same property. The highest fidelity representation of part content is its native format, specific to and usually proprietary to the part editor that created it. Lower fidelity representations enable the part to be viewed in documents without a full complement of part editors, to maintain portability of documents.

To load your part's content into memory from persistent storage, you should basically reverse the process of writing your part. However, your part must be able to work from an empty storage unit as well as one with stored content. Refer to the section "Initialization" for a description of this process.

SamplePart implements its persistent storage protocol in its Externalize, ExternalizeStateInfo, and ExternalizeContent methods. Other methods dealing with storage are WritePartInfo, ReadPartInfo, ClonePartInfo, CloneInto, and Purge. In addition, the utility method SetDirty manipulates the dirty flag, which is simply a Boolean value SamplePart uses to avoid redundancy: it writes the part content and notifies the draft only if the part has been altered.

The sections that follow show the implementations of these methods, except for the CloneInto method, which OpenDoc calls to perform data interchange. The CloneInto method also uses the storage unit API.

The Externalize Method

OpenDoc calls the Externalize method whenever it is necessary to write the part to persistent storage. Your part can also call its own Externalize method whenever it wants to. Before returning from this method, you must write all data that you need to accurately re-create the content and state of your part.

This method must call its parent class behavior (inherited class), because one of its parent class methods contains implementation. This is done in the SOM class implementation, which otherwise delegates all implementation to this method. Refer to the som_SamplePart__Externalize method of the som_SamplePart class in the file som_SamplePart.cpp.

The SamplePart object's implementation of the Externalize method performs the following actions:

  1. Checks the part's dirty flag and storage unit privileges.
    If the part's dirty flag is set to kODTrue, meaning that the part has been changed since it was last written, and if the part's storage unit is not read-only, the method proceeds.

  2. Retrieves a pointer to the part's storage unit.
    The method calls the GetStorageUnit method inherited from the ODPart superclass ODPersistentObject, using the fSelf field to refer to the part editor.

  3. Ensures that the storage unit properties are appropriate.
    The method calls SamplePart subroutines, internal methods CheckAndAddProperties and CleanseContentProperty, to verify that the properties and values are correct.

  4. Writes out the part's status information.
    The method accomplishes this step by calling an internal method, ExternalizeStateInfo.

  5. Writes out the part's content data.
    The method writes out its content data by calling another internal method, ExternalizeContent.

  6. Sets the part's dirty flag to false.
Listing 2-40 shows the implementation of the Externalize method. The other methods that the Externalize method calls are described in the next few sections.

Listing 2-40 Externalize method

void SamplePart::Externalize( Environment* ev )
{
    SOM_Trace("SamplePart","Externalize");

   TRY
      if ( fDirty && !fReadOnlyStorage )
      {
         ODStorageUnit* storageUnit = fSelf->GetStorageUnit(ev);
         this->CheckAndAddProperties(ev, storageUnit);
         this->CleanseContentProperty(ev, storageUnit);
         this->ExternalizeStateInfo(ev, storageUnit, kODNULLKey, kODNULL);
         this->ExternalizeContent(ev, storageUnit, kODNULLKey, kODNULL);
         fDirty = kODFalse;
      }
   CATCH_ALL
      this->DoDialogBox(ev, kODNULL, kErrorBoxID, kErrExternalizeFailed);
      SetErrorCode(kODErrAlreadyNotified);
      RERAISE;
   ENDTRY
}

The CheckAndAddProperties Method

SamplePart calls its own internal CheckAndAddProperties method to verify that the part's storage unit has the properties it needs to run. If such properties are not present, CheckAndAddProperties adds them.

The CheckAndAddProperties method performs the following actions:

  1. Sets up the contents property if it is not present.
    After ensuring that the contents property exists, the method checks for, and if necessary adds, the part's kind value to the contents property. These actions are necessary in case the storage unit is new and the part has not been previously written to storage.

  2. Sets up the preferred kind property if it is not present.
    The method writes out the default part kind for the editor. The user's chosen kind is written out in the ExternalizeStateInfo method.

  3. Sets up the part's display frame list if it is not present.
    The method checks for, and if necessary adds, the display frames property and value.

Listing 2-41 shows the implementation of the CheckAndAddProperties method.

Listing 2-41 CheckAndAddProperties method

void SamplePart::CheckAndAddProperties( Environment*     ev,
                                        ODStorageUnit*   storageUnit )
{
    SOM_Trace("SamplePart","CheckAndAddProperties");

   if ( !storageUnit->Exists(ev, kODPropContents, kODNULL, 0) )
      storageUnit->AddProperty(ev, kODPropContents);
   if ( !storageUnit->Exists(ev, kODPropContents, kSamplePartKind, 0) )
   {  
      storageUnit->Focus(ev, kODPropContents, kODPosUndefined, 
                                          kODNULL, 0, kODPosAll);
      storageUnit->AddValue(ev, kSamplePartKind);
   }
   if ( !storageUnit->Exists(ev, kODPropPreferredKind, kODISOStr, 0) )
   {
      TRY
         ODSetISOStrProp(ev, storageUnit, kODPropPreferredKind, 
                                          kODISOStr, kSamplePartKind);
      CATCH_ALL
         ODSURemoveProperty(ev, storageUnit, kODPropPreferredKind);
      ENDTRY
   }
   if ( !storageUnit->Exists(ev, kODPropDisplayFrames, kODNULL, 0) )
      storageUnit->AddProperty(ev, kODPropDisplayFrames);
   if ( !storageUnit->Exists(ev, kODPropDisplayFrames, kODWeakStorageUnitRefs, 0) )
   {
      storageUnit->Focus(ev, kODPropDisplayFrames, kODPosUndefined, 
                                          kODNULL, 0, kODPosAll);
      storageUnit->AddValue(ev, kODWeakStorageUnitRefs);
   }
}

The CleanseContentProperty Method

The SamplePart object calls its own internal CleanseContentProperty method from its Externalize method. The purpose of this method is to remove any value in the contents property that the part cannot write out accurately, such as values added to the contents property during drag-and-drop operations.

The CleanseContentProperty method performs the following actions:

  1. Focuses the storage unit to its contents property.

  2. Retrieves the type of each value in the contents property.
    The method uses the count of the number of values in the contents property to iterate through all of them. It focuses the storage unit on each value and gets its type.

  3. Removes any unsupported values.
    The method uses the OpenDoc utility method ODISOStrCompare to identify unsupported values by comparing their types to the kSamplePartKind data type. The method then deletes unsupported values using the ODStorageUnit method Remove on the previously focused storage unit.

Listing 2-42 shows the implementation of the CleanseContentProperty method.

Listing 2-42 CleanseContentProperty method

void SamplePart::CleanseContentProperty( Environment*     ev,
                                         ODStorageUnit*   storageUnit )
{
    SOM_Trace("SamplePart","CleanseContentProperty");

   ODULong numValues;
   ODULong index;
   
   storageUnit->Focus(ev, kODPropContents, kODPosUndefined, 
                     kODNULL, 0, kODPosAll);
   numValues = storageUnit->CountValues(ev);
   
   for (index = numValues; index >= 1; index--)
   {
      storageUnit->Focus(ev, kODPropContents, kODPosUndefined, 
                        kODNULL, index, kODPosUndefined);
      TempODValueType value = storageUnit->GetType(ev);
      if ( ODISOStrCompare(value, kSamplePartKind) != 0 )
         storageUnit->Remove(ev);
   }
}

The ExternalizeStateInfo Method

The SamplePart object calls its internal ExternalizeStateInfo method from its Externalize method when it writes the part to storage. This method writes out state information--any information pertaining to the working of the part editor--rather than the content. Such state information may be lost during data interchange operations, so the part must be able to recover gracefully if the state information is incomplete or missing.

The ExternalizeStateInfo method performs the following actions:

  1. Deletes weak references to the part's display frames.
    First the method focuses on the display frames property of the part's storage unit, then removes and adds back the weak storage unit references associated with that property. This action deletes previously written persistent object references, which are not deleted by simply deleting the content of the value.

  2. Gets ID numbers for each display frame in the part's display frame list.
    The method creates a CListIterator object to visit each of the part's display frames, retrieving the frame ID number for each.

    If, however, a draft key is passed in the key parameter, it indicates that the part is being cloned to another draft, in which case the method creates a weak clone of the display frame and uses the frame ID of the cloned frame instead. A draft key is a unique number that identifies a cloning operation on a draft; because cloning is a multistep process, the key is needed to preserve the integrity of each operation.

  3. Writes out weak references for each of the part's display frames.
    Still within the iteration loop of the CListIterator, the method gets the weak reference to the storage unit of each of the part's display frames. Finally, using a macro named StorageUnitSetValue, the method writes that value into the display frames property of the part's storage unit.

    The StorageUnitSetValue macro, defined in the file StorUtil.h, simplifies handling of the ODByteArray structure required by the SetValue method of ODStorageUnit, which the macro calls.

Listing 2-43 shows the implementation of the ExternalizeStateInfo method.

Listing 2-43 ExternalizeStateInfo method

void SamplePart::ExternalizeStateInfo( Environment*    ev,
                                       ODStorageUnit*  storageUnit,
                                       ODDraftKey      key,
                                       ODFrame*        scopeFrame )
{
    SOM_Trace("SamplePart","ExternalizeStateInfo");

   ODStorageUnitRefweakRef;
   ODID           frameID;
   ODID           scopeFrameID = 
                        ( scopeFrame ? scopeFrame->GetID(ev) : kODNULLID );
   ODDraft*       fromDraft = ODGetDraft(ev,fSelf);
   
   storageUnit->Focus(ev, kODPropDisplayFrames, kODPosUndefined,
                        kODWeakStorageUnitRefs, 0, kODPosUndefined);
   storageUnit->Remove(ev);
   storageUnit->AddValue(ev, kODWeakStorageUnitRefs);

   CListIterator fiter(fDisplayFrames);
   for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First();
         fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() )
   {
      frameID = proxy->GetID();
      if ( key )
         frameID = fromDraft->WeakClone(ev, key, frameID, kODNULLID, scopeFrameID);
      storageUnit->GetWeakStorageUnitRef(ev, frameID, weakRef);
      TRY
         StorageUnitSetValue(storageUnit, ev, kODStorageUnitRefSize,
                                                (ODPtr)&weakRef);
      CATCH_ALL
         // Consume the exception
      ENDTRY
   }
}

The ExternalizeContent Method

The SamplePart object's ExternalizeContent method is empty, although any implementation would contain a statement to focus the part's storage unit on its contents property (kODPropContents). Every part must have a property of this type in which to store its content data. OpenDoc uses the contents property to match parts to their correct part editors. Finally, the method would also write the part's content data out to its storage unit in an appropriate manner. SamplePart has no intrinsic content so ExternalizeContent does nothing.

The CloneInto Method

OpenDoc calls the CloneInto method during data interchange operations, that is, when a part is copied to the Clipboard, to a drag-and-drop object, or to a link-source object. The CloneInto method is inherited from the ODPersistentObject class. Generally, a part should respond to the CloneInto method call by writing its own data to the specified destination storage unit and cloning any objects to which it has strong persistent references and which are within the scope of the frame passed in the initiatingFrame parameter.

Note
The scope of a frame includes the content of frames embedded within it but excludes other content of the parts belonging to those embedded frames. Scope and other concepts of cloning are explained in the OpenDoc Programmer's Guide for the Mac OS.
The SamplePart object's implementation of the CloneInto method writes only its own data, state information, and content to the destination storage unit. Because SamplePart does not support embedding of other parts within itself, it has no need to clone any other objects.

SamplePart does the actual work of externalizing its data in the internal methods CheckAndAddProperties (Listing 2-41).

Listing 2-44 shows the implementation of the CloneInto method.

Listing 2-44 CloneInto method

void SamplePart::CloneInto( Environment*     ev,
                            ODDraftKey       key,
                            ODStorageUnit*   destinationSU,
                            ODFrame*         initiatingFrame )
{
    SOM_Trace("SamplePart","CloneInto");
   
   if ( destinationSU->Exists(ev, kODPropContents, kSamplePartKind, 0) == kODFalse )
   {
      this->CheckAndAddProperties(ev, destinationSU);
      this->ExternalizeStateInfo(ev, destinationSU, key, initiatingFrame);
      this->ExternalizeContent(ev, destinationSU, key, initiatingFrame);
   }
}

The InternalizeContent Method

The SamplePart object's internal InternalizeContent method does nothing, because SamplePart has no intrinsic content.

Generally speaking, for parts having content, a method such as this would focus the part's storage unit on the kODPropContents property, then read the stored data values. A reference to the storage unit is passed by OpenDoc to the part's InitPartFromStorage method (which in turn calls this method).

The InternalizeStateInfo Method

The SamplePart object calls its own internal InternalizeStateInfo method from its InitPartFromStorage method when it reads the part in from its persistent storage unit. This method reads in state information--any information pertaining to the working of the part editor--rather than the content. Generally, state information enables a part to present the same setup or configuration to the user as it had when last written out to storage.

The InternalizeStateInfo method reads from storage a list of weak references to its display frames, previously written out by the ExternalizeStateInfo method. The method validates each reference; if the reference is valid, the method adds it to its display frame list using lazy internalization. That is, the method uses a frame proxy object, adding the proxy pointer to its display frame list. The part reads in the actual display frame object only when it is actually needed.

Listing 2-45 shows the implementation of the InternalizeStateInfo method.

Listing 2-45 InternalizeStateInfo method

void SamplePart::InternalizeStateInfo( Environment*     ev,
                                       ODStorageUnit*   storageUnit )
{
    SOM_Trace("SamplePart","InternalizeStateInfo");

   ODStorageUnitRefweakRef;
   ODULong        size;

   if ( storageUnit->Exists(ev, kODPropDisplayFrames, kODWeakStorageUnitRefs, 0) )
   {
      storageUnit->Focus(ev, kODPropDisplayFrames, kODPosUndefined,
                           kODWeakStorageUnitRefs, 0, kODPosUndefined);
      size = storageUnit->GetSize(ev);
      storageUnit->SetOffset(ev, 0);
   
      for ( ODULong offset = 0; offset < size; offset += kODStorageUnitRefSize )
      {  
         TRY
            StorageUnitGetValue(storageUnit, ev, kODStorageUnitRefSize,
                                                (ODPtr)&weakRef);
            if ( storageUnit->IsValidStorageUnitRef(ev, weakRef) )
            {     
               ODID frameID = storageUnit->GetIDFromStorageUnitRef(ev, weakRef);
               CFrameProxy* proxy = new CFrameProxy;
               proxy->InitFrameProxy(frameID, ODGetDraft(ev,storageUnit));
               fDisplayFrames->Add(proxy);
            }
         CATCH_ALL
            // Consume exception
         ENDTRY
      }
   }
}

The ReadPartInfo Method

Every part is displayed in at least one frame represented by an object of class ODFrame. Frame objects have a part info field in which a part editor can store information describing how it should display its part's data in that frame. When you write your part to storage, OpenDoc calls your part's WritePartInfo method, and when you load your part into memory, OpenDoc calls its ReadPartInfo method. Generally, a part should respond to the WritePartInfo method call by writing enough information to persistent storage to be able to reconstruct each frame's part info field, and it should perform that reconstruction in its ReadPartInfo implementation. The WritePartInfo method is described in the section following this one.

The SamplePart object stores a pointer to an object of a C++ helper class named CFrameInfo in its part info field.

The ReadPartInfo method performs the following actions:

  1. Instantiates a frame info object.
    The CFrameInfo constructor initializes the object's internal data fields.

  2. Reads the frame info object into memory.
    The InitFromStorage method reads the CFrameInfo object, containing the frame's status information, from its storage unit.

  3. Returns a pointer to the frame info object.
If the CFrameInfo object's InitFromStorage method fails, the method deletes the object and propagates the exception to the calling method.

Listing 2-46 shows the implementation of the SamplePart object's ReadPartInfo method, the CFrameInfo constructor (defined inline), and the CFrameInfo object's InitFromStorage method.

Listing 2-46 ReadPartInfo, CFrameInfo constructor, and CFrameInfo::InitFromStorage methods

ODInfoType SamplePart::ReadPartInfo( Environment*         ev,
                                     ODFrame*             frame,
                                     ODStorageUnitView*   storageUnitView )
{
    SOM_Trace("SamplePart","ReadPartInfo");

   CFrameInfo* frameInfo = new CFrameInfo;
      
   TRY
      frameInfo->InitFromStorage(ev, storageUnitView);
   CATCH_ALL
      ODDeleteObject(frameInfo);
      RERAISE;
   ENDTRY
   
   return (ODInfoType)frameInfo;
}
CFrameInfo::CFrameInfo()
{
   fFrameActive = kODFalse;
   fFrameReactivate  = kODFalse;
   fShouldDisposeWindow = kODFalse;
   fActiveFacet = kODNULL;
   fSourceFrame = kODNULL;
   fDependentFrame = kODNULL;
   fPartWindow = kODNULL; 
}
void CFrameInfo::InitFromStorage(Environment* ev, ODStorageUnitView* storageUnitView)
{
   ODStorageUnit* storageUnit = storageUnitView->GetStorageUnit(ev);

   if ( storageUnit->Exists(ev, kODNULL, kSamplePartInfo, 0) )
   {
      TRY
         storageUnit->Focus(ev, kODNULL, kODPosSame,
                              kSamplePartInfo, 0 , kODPosUndefined);
         ODStorageUnitRef weakRef = {0,0,0,0};
         StorageUnitGetValue(storageUnit, ev, sizeof(ODStorageUnitRef),
                        (ODPtr)&weakRef);
         if ( storageUnit->IsValidStorageUnitRef(ev, weakRef) )
         {
            ODID frameID = storageUnit->GetIDFromStorageUnitRef(ev, weakRef);
            CFrameProxy* proxy = new CFrameProxy;
            proxy->InitFrameProxy(frameID, ODGetDraft(ev,storageUnit));
            fSourceFrame = proxy;
         }
      CATCH_ALL
         ODDeleteObject(fSourceFrame);
         fSourceFrame = kODNULL;
      ENDTRY

      TRY
         ODStorageUnitRef weakRef = {0,0,0,0};
         StorageUnitGetValue(storageUnit, ev, sizeof(ODStorageUnitRef),
                        (ODPtr)&weakRef);
         
         if ( storageUnit->IsValidStorageUnitRef(ev, weakRef) )
         {
            ODID frameID = storageUnit->GetIDFromStorageUnitRef(ev, weakRef);
            CFrameProxy* proxy = new CFrameProxy;
            proxy->InitFrameProxy(frameID, ODGetDraft(ev,storageUnit));
            fDependentFrame = proxy;
         }
      CATCH_ALL
         ODDeleteObject(fDependentFrame);
         fDependentFrame = kODNULL;
      ENDTRY
   }
}

The WritePartInfo Method

OpenDoc calls a part's WritePartInfo method for each of its display frames whenever the document is saved.

The SamplePart object's implementation of the WritePartInfo method calls the CFrameInfo object's Externalize method, which first gets a reference to the storage unit of the storage-unit view object passed with the call to WritePartInfo. The Externalize method then calls the CFrameInfo object's CleanseFrameInfoProperty method, which iterates through the value types in the storage unit and removes any that are not supported by SamplePart. Finally, Externalize calls the CFrameInfo object's ExternalizeFrameInfo method to actually write out the frame's part info data.

The CFrameInfo object's ExternalizeFrameInfo method works much the same as the SamplePart object's ExternalizeStateInfo method. That is, the method removes, then adds back, the value containing weak references in the storage unit. Then, the method writes weak references to its source frame, if any, and its dependent frame, if any. In both cases, if a draft key exists, the method creates a weak clone of the display frame and writes out the weak reference to the storage unit. The SamplePart object's ExternalizeStateInfo method is described in "The ExternalizeStateInfo Method".

Listing 2-47 shows the implementation of the SamplePart object's WritePartInfo method, the CFrameInfo object's Externalize method, and the CFrameInfo object's ExternalizeFrameInfo method.

Listing 2-47 WritePartInfo, CFrameInfo::Externalize, and CFrameInfo::ExternalizeFrameInfo methods

void SamplePart::WritePartInfo( Environment*         ev,
                                ODInfoType           partInfo,
                                ODStorageUnitView*   storageUnitView )
{
    SOM_Trace("SamplePart","WritePartInfo");
   
   ((CFrameInfo*) partInfo)->Externalize(ev, storageUnitView);
}
void CFrameInfo::Externalize(Environment* ev, ODStorageUnitView* storageUnitView)
{
   ODStorageUnit* storageUnit = storageUnitView->GetStorageUnit(ev);
   this->CleanseFrameInfoProperty(ev, storageUnit);
   this->ExternalizeFrameInfo(ev, storageUnit, kODNULLKey, kODNULL);
}
void CFrameInfo::ExternalizeFrameInfo(Environment* ev, ODStorageUnit* storageUnit,
                              ODDraftKey key, ODFrame* scopeFrame)
{
   if ( storageUnit->Exists(ev, kODNULL, kSamplePartInfo, 0) )
   {
      storageUnit->Focus(ev, kODNULL, kODPosSame, kSamplePartInfo, 0,
                                                kODPosUndefined);
      storageUnit->Remove(ev);
   }
   storageUnit->AddValue(ev, kSamplePartInfo);
   
   {
      ODStorageUnitRef weakRef = {0,0,0,0};
      
      if ( fSourceFrame )
      {
         ODID   frameID = fSourceFrame->GetID();
         ODID   scopeFrameID = ( scopeFrame ? scopeFrame->GetID(ev) : kODNULLID );
         ODDraft* fromDraft = fSourceFrame->GetDraft();

         if ( key )
            frameID = fromDraft->WeakClone(ev, key, frameID, kODNULLID, 
                                                   scopeFrameID);
         storageUnit->GetWeakStorageUnitRef(ev, frameID, weakRef);
      }
      StorageUnitSetValue(storageUnit, ev, sizeof(ODStorageUnitRef),
                                                (ODPtr)&weakRef);
   }
   {
      ODStorageUnitRef weakRef = {0,0,0,0};
      
      if ( fDependentFrame )
      {
         ODID   frameID = fDependentFrame->GetID();
         ODID   scopeFrameID = ( scopeFrame ? scopeFrame->GetID(ev) : kODNULLID );
         ODDraft* fromDraft = fDependentFrame->GetDraft();
   
         if ( key )
            frameID = fromDraft->WeakClone(ev, key, frameID, kODNULLID,
                                                   scopeFrameID);
         storageUnit->GetWeakStorageUnitRef(ev, frameID, weakRef);
      }
      StorageUnitSetValue(storageUnit, ev, sizeof(ODStorageUnitRef),
                                                   (ODPtr)&weakRef);
   }
}

The ClonePartInfo Method

OpenDoc calls a part's ClonePartInfo method when any of its display frames is cloned during data transfer. Generally, a part editor should respond to the ClonePartInfo method call by writing out the frame's part info data, including any additional objects to which the part has strong persistent references and that are within the scope of the specified frame.

The SamplePart object's implementation of ClonePartInfo calls the CloneInto method of the CFrameInfo helper object holding the specified frame's part info data. The CFrameInfo implementation of CloneInto gets the storage unit, prefocused to a property but not to a value, and writes out the frame's part info data by calling the CFrameInfo object's ExternalizeFrameInfo method, which is shown in Listing 2-47.

Listing 2-48 shows the implementation of the SamplePart object's ClonePartInfo method and the CFrameInfo object's CloneInto method.

Listing 2-48 ClonePartInfo and CFrameInfo::CloneInto methods

void SamplePart::ClonePartInfo( Environment*         ev,
                                ODDraftKey           key,
                                ODInfoType           partInfo,
                                ODStorageUnitView*   storageUnitView,
                                ODFrame*             scopeFrame )
{
    SOM_Trace("SamplePart","ClonePartInfo");
   
   ((CFrameInfo*) partInfo)->CloneInto(ev, key, storageUnitView, scopeFrame);
}
void CFrameInfo::CloneInto(Environment *ev, ODDraftKey key,
                     ODStorageUnitView* storageUnitView, ODFrame* scopeFrame)
{
   ODStorageUnit* storageUnit = storageUnitView->GetStorageUnit(ev);

   if ( storageUnit->Exists(ev, kODNULL, kSamplePartInfo, 0) == kODFalse )
   {
      this->ExternalizeFrameInfo(ev, storageUnit, key, scopeFrame);
   }
}

The Release Method

A part's Release method is called by an object, such as another part editor, whenever it releases a reference to this part. The Release method is inherited from the ODRefCntObject class, and the inherited implementation does the actual reference-count management. The som_SamplePart object's Release method calls the inherited method before it calls the SamplePart object's Release method described in this section (see also "SamplePart System Object Model Interface").

The SamplePart object's implementation of the Release method releases the part-wrapper object, to which the fSelf field points, if its reference count falls to 0.

Listing 2-49 shows the implementation of the Release method.

Listing 2-49 Release method

void SamplePart::Release( Environment*  ev )
{
    SOM_Trace("SamplePart","Release");

   if ( fSelf->GetRefCount(ev) == 0 )
      ODGetDraft(ev,fSelf)->ReleasePart(ev,fSelf);
}

The ReleaseAll Method

OpenDoc calls a part's ReleaseAll method when the part object is about to be deleted by its draft. At this point, the part must release all the references it has acquired to other reference-counted objects. Otherwise, it will cause an invalid reference count error at some later time. This method is inherited from the ODPersistentObject class. The som_SamplePart object's ReleaseAll method calls the inherited method after it calls the SamplePart object's ReleaseAll method described in this section (see also "SamplePart System Object Model Interface").

The SamplePart object's implementation of the ReleaseAll method performs the following actions:

  1. Cleans up the SamplePart global variables.
    The ReleaseAll method first ensures that the global variables are no longer needed. The global variables are shared among all instances of the SamplePart class that are currently running, and each instance increments a usage count accordingly. The method decrements the usage count. If the usage count reaches 0, the method releases the menu bar object, deletes the user interface focus set object, and deletes the global variables structure.

  2. Cleans up the part's display frame list.
    The ReleaseAll method first ensures that the part's display frame list is not null. SamplePart maintains proxy display frame objects in its list to support lazy internalization; the actual frames are not read into memory until they are needed. The method iterates through the display frame list, removing the pointer for each proxy from the list and deleting the proxy object. Then the method deletes the frame list object itself.

The ODDeleteObject and ODReleaseObject utility macros, used in the ReleaseAll method to delete objects and release reference-counted objects, are defined in the ODUtils.h file.

Listing 2-50 shows the implementation of the ReleaseAll method.

Listing 2-50 ReleaseAll method

void SamplePart::ReleaseAll( Environment* ev )
{
    SOM_Trace("SamplePart","ReleaseAll");

   TRY
      if ( --gGlobalsUsageCount == 0 )
      {
         ODReleaseObject(ev, gGlobals->fMenuBar);
         ODDeleteObject(gGlobals->fUIFocusSet);
         ODDeleteObject(gGlobals);
      }
   
      if ( fDisplayFrames )
      {
         CListIterator fiter(fDisplayFrames);
         for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First();
               fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() )
         {
            fiter.RemoveCurrent();
            delete proxy;
         }
         ODDeleteObject(fDisplayFrames);
      }
   CATCH_ALL
      RERAISE;
   ENDTRY
}

The Purge Method

When OpenDoc detects a possible shortage of memory, it may call a part's Purge method. The part should free as much memory as possible. OpenDoc passes in the requested number of bytes to free with the method call. Obviously, parts should not free any resources they need to keep running.

The SamplePart object's implementation of the Purge method performs the following actions:

  1. Checks the view type of each of its display frames.
    The method checks first to see if its internal list of display frames has been created. If not, there is no storage to free, so the method returns. Otherwise, the method iterates through all the frame proxy objects in its display frames list. If the frame associated with the proxy has not been loaded into memory, the method ignores it.

  2. Releases the unused thumbnail resource.
    The method ensures that no frame has a view type of thumbnail, as determined in its previous iteration of its frame list, and that the thumbnail resource was previously read into memory. If these conditions prevail, the method increments its count of freed bytes by the size of the resource, releases the resource, and sets to null its global variable that points to the resource.

  3. Returns the cumulative count of the number of bytes freed.
Listing 2-51 shows the implementation of the Purge method.

Listing 2-51 Purge method

ODSize SamplePart::Purge( Environment*    ev,
                          ODSize          /*size*/ )
{
    SOM_Trace("SamplePart","Purge");

   if ( fDisplayFrames == kODNULL ) return 0;
   
   ODSize   bytesFreed  = 0;
   ODBooleanusingThumbnail= kODFalse;
   
   CListIterator fiter(fDisplayFrames);
   for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First();
         fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() )
   {
      if ( proxy->FrameIsLoaded() )
      {
         ODTypeTokenframeView = proxy->GetFrame(ev)->GetViewType(ev);
         if ( frameView == gGlobals->fThumbnailView )
            usingThumbnail = kODTrue;
         proxy->Purge(ev);
      }
   }
   if ( !usingThumbnail && (gGlobals->fThumbnail != kODNULL) )
   {
      bytesFreed += (ODSize) ODGetHandleSize(gGlobals->fThumbnail);
      ReleaseResource(gGlobals->fThumbnail);
      gGlobals->fThumbnail = kODNULL;
   }
   return bytesFreed;
}

The SetDirty Method

The SamplePart object's internal SetDirty method sets its dirty flag to true, indicating that the part's data has been changed by the user. The part editor calls its own SetDirty method whenever it changes its content.

The SetDirty method performs the following actions:

  1. Checks the dirty flag and write status.
    The implementation is protected by its own flag, the internal variable fDirty. If the flag is already true, or if the part's draft is read-only, the method body doesn't execute. Otherwise the method performs the subsequent steps.

  2. Sets the dirty flag to true.

  3. Notifies the draft that its content has changed from its previous version.
    The method gets access to the draft through the ODGetDraft utility method and calls its SetChangedFromPrev method.

Listing 2-52 shows the implementation of the SetDirty method.

Listing 2-52 SetDirty method

void SamplePart::SetDirty( Environment*  ev )
{
    SOM_Trace("SamplePart","SetDirty");

   if ( !fDirty && !fReadOnlyStorage )
   {
      fDirty = kODTrue;
      ODGetDraft(ev,fSelf)->SetChangedFromPrev(ev);
   }
}

Previous Book Contents Book Index Next

© Apple Computer, Inc.
16 JUL 1996




Navigation graphic, see text links

Main | Page One | What's New | Apple Computer, Inc. | Find It | Contact Us | Help