Legacy Documentclose button

Important: The information in this document is obsolete and should not be used for new development.

Previous Book Contents Book Index Next

Inside Macintosh: /
Creating a Cyberdog Service


Implementing Your Cyberdog Item Class

To implement your own kind of Cyberdog item, you must define a subclass of CyberItem and override most of its methods. These methods are called by the service while opening the Cyberdog item. Listing 2 shows the SOM class definition for the Cyberdog item associated with the Finger service.

Listing 2 The SOM class definition of the Finger service item

module AppleCyberdog
{
   interface FingerItem : CyberItem
   {
      #ifdef __SOMIDL__
         implementation
         {
            functionprefix = FingerItem__;
            override:
               somInit,
               somUninit,
               Open,
               SetUpFromURL,
               GetFlatSize,
               Flatten,
               Unflatten,
               ExternalizeContent,
               Clone,
               Compare,
               GetStringProperty,
               SetDefaultName,
               GetURL,
               GetIconSuite,
               CreateCyberStream,
               IsDownloadable,
               GetContentKind,
               GetFileInfo;
                     
            #ifdef __PRIVATE__
               FingerItemDatafData;
            #endif
         }; 
      #endif
   };
};
The following sections describe how to implement these methods.

Initializing a Cyberdog Item

Your Cyberdog item must contain enough information to identify the location to which the item refers. It should also contain, or be able to derive, the information that needs to be flattened when the item is saved in a storage unit. Listing 3 shows the data maintained in a Finger service's item, which is referenced by the fData field.

Listing 3 The data structure of the Finger service item

struct CFingerItemData
{
            CFingerItemData();
   virtual  ~CFingerItemData();
   
   char*          fURL;
   StringPtr      fDefaultName;
   ScriptCode     fDefaultNameScript;
   static Handle  gIconSuite;
};
The item holds a URL, a display name for the URL, the script system with which to display the name, and a handle to the icon suite associated with the item. A structure for this data is created when the item is initialized, as shown in Listing 4.

Listing 4 The somInit method of the Finger service item

SOM_Scope void  SOMLINK FingerItem__somInit
                           (AppleCyberdog_FingerItem *somSelf)
{
   ...
   AppleCyberdog_FingerItem_parent_CyberItem_somInit(somSelf);
    
    CFingerItemData* data = new CFingerItemData;
    somThis->fData = data;
}
The item's data is deleted when the item is destroyed, as shown in Listing 5.

Listing 5 The somUninit method of the Finger service item

SOM_Scope void  SOMLINK FingerItem__somUninit
                           (AppleCyberdog_FingerItem *somSelf)
{
   ...
   AppleCyberdog_FingerItem_parent_CyberItem_somUninit(somSelf);
    
    CFingerItemData* data = (CFingerItemData*) somThis->fData;
    
    if(data)
    {
      delete data;
      somThis->fData = nil;
    }
}

Setting Up a Cyberdog Item

Your Cyberdog item must provide a SetUpFromURL method. Your service calls this SetUpFromURL method when it creates the item. The SetUpFromURL method is called with the URL, the URL's name, and the name's script as input parameters. If the name is not specified, your SetUpFromURL method must create a name. Listing 6 shows the SetUpFromURL method of the Finger service item, which stores the URL and sets the name.

Listing 6 The SetUpFromURL method of the Finger service item

SOM_Scope void  SOMLINK FingerItem__SetUpFromURL
                           (AppleCyberdog_FingerItem *somSelf, 
                            Environment *ev,
                            char* url,
                            StringPtr defaultName,
                            ScriptCode defaultNameScript)
{
   ...
   CFingerItemData* data = (CFingerItemData*) somThis->fData;
   
   if(url)
   {
      data->fURL = (char*) ODNewPtr(strlen(url)+1);
      strcpy(data->fURL, url);
      
      if(defaultName)
         somSelf->SetDefaultName(ev, defaultName, defaultNameScript);
      else
      {
         short port;
         char host[256];
         char user[256];
         char manufacturedName[256];
         manufacturedName[0] = 0;
         
         ParseFingerURL(url, host, user, &port);
         
         if(user[0] != 0)
         {
            strcat(manufacturedName, user);
            strcat(manufacturedName, " at ");
         }
         strcat(manufacturedName, host);
         ctopstr(manufacturedName);
         
         somSelf->SetDefaultName(ev, 
                        (StringPtr) manufacturedName, smRoman);
      }
   }
}
The ParseFingerURL function decomposes the URL in the url field into its host, user, and port components.

Opening a Cyberdog Item

You must override your Cyberdog item's Open method to specify how to open the Cyberdog item. Your implementation can either open the item synchronously or asynchronously. Listing 7 shows the Open method of the Finger service item. A preference in the Internet Config file controls how to open the item. See "Creating the Preferences Panel" (page 37).

Note
It is not necessary to open a Finger service item asynchronously. The data type being accessed is known to be text and only a small amount of data is transferred. The asynchronous option is provided to show the differences between asynchronous and synchronous opening of Cyberdog items.
Listing 7 The Open method of the Finger service item

SOM_Scope void  SOMLINK FingerItem__Open
                           (AppleCyberdog_FingerItem *somSelf,
                            Environment *ev,
                            ParameterSet* theParams)
{
   ...
   // Try to find a window that already shows the Cyberdog item
   long search4ExistingWindow = true;
   if(theParams)
   {
      if( ! theParams->GetParameter(ev, kCDSearch4ExistingWindowKey,
                            (void**) &search4ExistingWindow) )
         search4ExistingWindow = true;
   }
   if(search4ExistingWindow)
      // Existing window found and selected; do not create a new part.
      if(GetCyberSession(ev)->SelectCyberItemWindow(ev, somSelf))
         return; 
   
   // Extract the opener part (if any) from the parameter set.
   ODPart* openerPart = kODNULL;
   if(theParams)
      if(!theParams->GetParameter(ev, kCDInitialOpenerPartKey,
                           &openerPart))
         theParams->GetParameter(ev, kCDObtainedOpenerPartKey,
                           &openerPart);

// Read the opening mode preference from Internet config.
   OSErr err;
   long  size;
   
   ODBoolean openAsync;
   size = sizeof(ODBoolean);
   err = GetPrefsValue(ev, kFingerOpenAsyncPref, (Ptr) &openAsync, 
                  &size);
   if(err != noErr)
      openAsync = kODFalse;
   if(openAsync)
   // Open asynchronously.
   {
      // Provide a parameter set object for the call to ObtainOpener.
      theParams = AcquireCreateParameterSet(ev, theParams);
      
      // Obtain an opener part for opening asyncronously.
      openerPart = GetCyberSession(ev)->ObtainOpener(ev, 
                        openerPart, kODNULL, somSelf, theParams);
      
      // Create a thread that simulates deferred opening
       const Size kThreadStackSize = 0;
       ThreadID threadID;
       AsyncOpenData* openData 
               = new AsyncOpenData(somSelf, theParams, openerPart);    
       OSErr err = NewThread(kCooperativeThread,
                      gFingerItemAsyncOpenThread, openData,
                      kThreadStackSize, kCreateIfNeeded, 
                      nil, & threadID);
    }
    else
   // Open synchronously.
{
      // Create the right kind of part in the right document.
      CyberSession* cyberSession = ::GetCyberSession(ev);
      TempODPart cyberPart = cyberSession->CreateCyberPart(ev,
                           openerPart, kTextPlainKind, kODNULL);
      
      // If the display part handles the kTextPlain kind of data,
      // create a download part instead.
      if(cyberPart == kODNULL)
         cyberPart = cyberSession->CreatePartInCyberDocument(ev,
                                 kDownloadPartKind, kODNULL);
      
      // Open the Cyberdog item.
      if(cyberPart)
      {
         TempCyberPartExtension cyberPartExt(cyberPart,
                                    kCyberPartExtension); 
         cyberPartExt->OpenCyberItem(ev,  fingerItem, openerPart,
                              params);
      }
    }
}
The Open method attempts to find a window that already displays the item. If it cannot find the window, it opens the window asynchronously or synchronously. If the mode is asynchronous, the Open method initiates a cooperative Thread Manager thread to open the window, as shown in Listing 8.

Listing 8 The thread that opens an item asynchronously for the Finger service

pascal void* FingerItemAsyncOpenThread(AsyncOpenData* openData)
{
   Environment localEv;
   SOM_InitEnvironment(&localEv);
   Environment* ev = &localEv;
   CyberItem* fingerItem = openData->fFingerItem;
   ParameterSet* params = openData->fParams;
   ODPart*     openerPart = openData->fOpenerPart;
   Boolean     aborted = false;
   {
      // Set up a progress broadcaster object.
      CyberProgressBroadcaster* broadcaster 
                              = new CyberProgressBroadcaster;
      broadcaster->ICyberProgressBroadcaster(ev, 
                           gMyCyberAbortUPP, (Ptr) &aborted);
      
      broadcaster->SetProgressMode(ev, kUnmeteredProgress);

      // Attach the broadcaster object to the opener part.
      TempOpenerPartExtension openerPartExt(openerPart,
                                 kCyberOpenerPartExtension);
      openerPartExt->AttachProgressBroadcaster(ev, broadcaster);
   
      // Set the status string to something informative.
      broadcaster->SetStatusString(ev, "\pUm...thinking...");
      
      // Simulate a need for asynchronous operation by waiting.
      unsigned long tickOut = TickCount() + 4*60;  // 4 seconds
      while(TickCount() < tickOut && !aborted)
         YieldToAnyThread();
      
      // Detach the broadcaster object and delete it.
      openerPartExt->DetachProgressBroadcaster(ev, broadcaster);
      delete broadcaster;
   }
   
   // If the user didn't press the Stop button, create and open a
   // Cyberdog display part.
   if(!aborted)
   {
      // Create the right kind of part in the right document.
      CyberSession* cyberSession = ::GetCyberSession(ev);
      TempODPart cyberPart = cyberSession->CreateCyberPart(ev,
                           openerPart, kTextPlainKind, kODNULL);
      
      // If the display part handles the kTextPlain kind of data,
      // create a download part instead.
      if(cyberPart == kODNULL)
         cyberPart = cyberSession->CreatePartInCyberDocument(ev,
                                 kDownloadPartKind, kODNULL);
      
      // Open the Cyberdog item.
      if(cyberPart)
      {
         TempCyberPartExtension cyberPartExt(cyberPart,
                                    kCyberPartExtension); 
         cyberPartExt->OpenCyberItem(ev,  fingerItem, openerPart,
                              params);
      }
   }
   // Release the parameter set.
   params->Release(ev);
   delete openData;
   SOM_UninitEnvironment(&localEv);
   
   return nil;
}

Resolving a Cyberdog Item

When a Cyberdog item is opened, the item determines the kind of data to which it refers. This process is called resolving the item. You must specify how to resolve your Cyberdog item and provide information about the item's state as it is being resolved. To resolve a Cyberdog item, you must override the following methods:

   ODSession* session = GetCyberSession(ev)->GetODSession(ev);
   return session->Tokenize(ev, kTextPlainKind);
Listing 9 The GetFileInfo method of the Finger service item

SOM_Scope void  SOMLINK FingerItem__GetFileInfo
               (AppleCyberdog_FingerItem *somSelf, Environment *ev,
                CDItemInfoType infoType,
                ODOSType* flCreator,
                ODOSType* flType)
{
   ...
   if(infoType == kCDInfoDownload)
   {
      *flCreator = '????';
      *flType = 'TEXT';
   }
   else
   {
      *flCreator = 'odtm';
      *flType = 'FNGR';
   }
}
The kCDInfoDownload constant indicates that the data is in its native form, which is text in the Finger sample.

Creating a Cyberdog Stream

If your item's content is accessible via a stream, you must override your item's CreateCyberStream method to create a Cyberdog stream that uses the service's protocol to download data. Listing 10 shows the CreateCyberStream method of the Finger service item.

Listing 10 The CreateCyberStream method of the Finger item

SOM_Scope CyberStream*  SOMLINK FingerItem__CreateCyberStream
               (AppleCyberdog_FingerItem *somSelf, Environment *ev)
{
   ...
   CFingerItemData* data = (CFingerItemData*) somThis->fData;
   
   AppleCyberdog_FingerStream* stream = new AppleCyberdog_FingerStream;
   stream->IFingerStream(ev, data->fURL);
   
    return stream;
}

Flattening and Unflattening a Cyberdog Item

You must override your Cyberdog item's Flatten and Unflatten methods so that the item can be written to and read from a storage unit. These methods create a flat representation of a Cyberdog item and recreate an item from its flat representation, respectively. You must override its GetFlatSize method to return the size of the flattened item.

You must use Cyberdog's format for your flattened items. You can place your item's unique values (called private data) after the required data. See the description of the CyberItem class in the Classes and Methods chapter of the Cyberdog Programmer's Kit for the structure of a flattened Cyberdog item.

Listing 11 shows the Flatten method of the Finger service item. The Finger service item does not contain any private data.

Listing 11 The Flatten method of the Finger service item

SOM_Scope long  SOMLINK FingerItem__Flatten
               (AppleCyberdog_FingerItem *somSelf, Environment *ev,
                Ptr buffer,
                long length)
{
   ...
   CFingerItemData* data = (CFingerItemData*) somThis->fData;

   long flatSize = somSelf->GetFlatSize(ev);
   
   if(flatSize <= length)
   {
      long classNameLen = strlen(kFingerItemClassName);
      char* url = nil;
      StringPtr defaultName = nil;
      ScriptCode defaultNameScript = smRoman;
      
      if(data)
      {
         url = data->fURL;
         defaultName = data->fDefaultName;
         defaultNameScript = data->fDefaultNameScript;
      }
   
      Ptr p = buffer;
      
      *((long*)p) = flatSize;
      p += sizeof(long);
      
      *((short*)p) = kCyberItemSignature;
      p += sizeof(short);
      
      *((short*)p) = kCyberItemVersionNum;
      p += sizeof(short);
      
      *((long*)p) = classNameLen;
      p += sizeof(long);
      
      memcpy(p, kFingerItemClassName, classNameLen);
      p += classNameLen;
      
      *((short*)p) = defaultNameScript;
      p += sizeof(short);
      
      if(defaultName)
      {
         memcpy(p, defaultName, defaultName[0] + 1);
         p += defaultName[0] + 1;
      }
      else
      {
         *p = 0;
         p += 1;
      }
      
      if(url)
      {
         long urlLen = strlen(url);
         *((long*)p) = urlLen;
         p += sizeof(long);
         memcpy(p, url, urlLen);
         p += urlLen;
      }
      else
      {
         *((long*)p) = 0;
         p += sizeof(long);
      }

      *((long*)p) = 0;
      p += sizeof(long);
   }
   else
      // Error--the buffer is not large enough to flatten into
      flatSize = 0;
      
   return flatSize;
}
Listing 12 shows the Unflatten method of the Finger service item, which unflattens the item. The method shows the minimum amount of effort required to unflatten any Cyberdog item.

Listing 12 The Unflatten method Finger service item

SOM_Scope long  SOMLINK FingerItem__Unflatten
               (AppleCyberdog_FingerItem *somSelf, Environment *ev,
                Ptr buffer)
{
   ...
   CFingerItemData* data = (CFingerItemData*) somThis->fData;
   
   Ptr p = buffer;
   
   long flatSize = *((long*)p);
   p += sizeof(long);
   
   if(data)
   {
      short cyberItemSignature = *((short*)p);
      p += sizeof(short);

      short cyberItemVersion = *((short*)p);
      p += sizeof(short);
      
      long classNameLen = *((long*)p);
      p += sizeof(long);
      p += classNameLen;
      
      short defaultNameScript = *((short*)p);
      p += sizeof(short);
      
      StringPtr defaultName = (StringPtr) p;
      somSelf->SetDefaultName(ev, defaultName, defaultNameScript);
      p += defaultName[0] + 1;
      
      long urlLen = *((long*)p);
      p += sizeof(long);
      
      if(data->fURL)
         ODDisposePtr(data->fURL);
      data->fURL = (char*) ODNewPtr(urlLen + 1);
      memcpy(data->fURL, p, urlLen);
      data->fURL[urlLen] = 0;
      p += urlLen;
   }
   
   return flatSize;
}
Listing 13 shows the GetFlatSize method of the Finger service item, which returns the size of the flattened item. Because the item does not contain any private data, it returns the minimum size of any flattened item.

Listing 13 The GetFlatSize method of the Finger service item

SOM_Scope long  SOMLINK FingerItem__GetFlatSize
               (AppleCyberdog_FingerItem *somSelf, Environment *ev)
{
   ...
   CFingerItemData* data = (CFingerItemData*) somThis->fData;

   long flatSize;
   long classNameLen = strlen("AppleCyberdog_FingerItem");
   long urlLen = 0;
   long defaultNameLen = 0;
   
   if(data)
   {
      if(data->fURL)
         urlLen = strlen(data->fURL);
      if(data->fDefaultName)
         defaultNameLen = data->fDefaultName[0];
   }
   flatSize =  sizeof(long) + // Overall length
            sizeof(short) +   // CyberItem Signature
            sizeof(short) +   // Version
            sizeof(long) +    // Class length
            classNameLen +    // Class name (no null
                              // termination)
            sizeof(ScriptCode) +// Display name script code
            defaultNameLen + 1 +// Display name (plus 1 byte
                              // for the length byte)
            sizeof(long) +    // URL length
            urlLen +          // URL
            sizeof(long);     // Private data length

   return flatSize;
}

Writing a Cyberdog Item to a Storage Unit

You must override your Cyberdog item's ExternalizeContent method to write its content to storage. Listing 14 shows the ExternalizeContent method of the Finger service item. The method writes a Finger service item to storage in its internal format (kCyberItemKind) and also writes the URL as text.

Listing 14 The ExternalizeContent method of the Finger service item

SOM_Scope void  SOMLINK FingerItem__ExternalizeContent
               (AppleCyberdog_FingerItem *somSelf, Environment *ev,
                ODStorageUnit* su)
{
   ...
   CFingerItemData* data = (CFingerItemData*) somThis->fData;

   // Add a kCyberItemKind key with the flattened 
   // Cyberdog item as its value.
   
   su->AddValue(ev, kCyberItemKind);
   somSelf->StreamToStorageUnit(ev, su);
   
   // Add a kODPlatformDataType key (scrapbook text) with the URL 
   // as its value.
   if(data && data->fURL)
   {
      ODTranslation* translation
       = ::GetCyberSession(ev)->GetODSession(ev)->GetTranslation(ev);
      TempODValueType textType 
       = translation->GetISOTypeFromPlatformType
                           (ev, 'TEXT', kODPlatformDataType);
      
      if(textType != nil)
      {
         su->AddValue(ev, textType);
         StorageUnitSetValue
               (su, ev, strlen(data->fURL), (ODValue)data->fURL);
      }
   }
}

Cloning and Comparing a Cyberdog Item

You must override your Cyberdog item's Clone method to allow the item to be copied. You must override your Cyberdog item's Compare method to allow an item to be compared with other items. An item that is created as a clone of another item should be equivalent when they are compared.

Listing 15 shows the Clone method of the Finger service item. After the method creates and initializes a new Finger service item, the method calls the item's SetUpFromURL method to set initial values for the item's fields. See "Setting Up a Cyberdog Item" (page 10) for information about the SetUpFromURL method.

Listing 15 The Clone method of the Finger service item

SOM_Scope CyberItem*  SOMLINK FingerItem__Clone
               (AppleCyberdog_FingerItem *somSelf, Environment *ev)
{
   ...
   CFingerItemData* data = (CFingerItemData*) somThis->fData;
   
   AppleCyberdog_FingerItem* clone = new AppleCyberdog_FingerItem;
   clone->ICyberItem(ev);
   clone->SetUpFromURL(ev, 
          data->fURL, data->fDefaultName, data->fDefaultNameScript);
   
   return clone;
}
Listing 16 shows the Compare method of the Finger service item. The method calls its inherited Compare method to determine whether the items being compared are derived from the same SOM class. If so, the URL text is compared for equality. If the URLs match, the items are equivalent.

Listing 16 The Compare method of the Finger service item

SOM_Scope CDCompareType  SOMLINK FingerItem__Compare
               (AppleCyberdog_FingerItem *somSelf, Environment *ev,
                CyberItem* compare)
{
   ...
   // Call the inherited method first to determine whether the
   // Cyberdog items are of the same SOM class or not.
   CDCompareType val = AppleCyberdog_FingerItem_parent_CyberItem_Compare
                                       (somSelf,ev,compare);
   
   if(val == kCDCompareEqual)
   {
      // The item is a Finger service item.
      // Base the comparison on the URL.
      
      char* ourURL = somSelf->GetURL(ev);
      char* itsURL = compare->GetURL(ev);
      
      int stringCompare = strcmp(ourURL, itsURL);
      
      if(stringCompare < 0)
         val = kCDCompareLessThan;
      else if(stringCompare > 0)
         val = kCDCompareGreaterThan;
      else
         val = kCDCompareEqual;
   }
   return val;
}

Retrieving a Cyberdog Item's Icons

You must override your Cyberdog item's GetIconSuite method to retrieve the item's icon suite. Listing 17 shows the GetIconSuite method of the Finger service item.

Listing 17 The GetIconSuite method of the Finger service item

SOM_Scope Handle  SOMLINK FingerItem__GetIconSuite
               (AppleCyberdog_FingerItem *somSelf, Environment *ev,
                CDItemInfoType infoType)
{
   ...
   CFingerItemData* data = (CFingerItemData*) somThis->fData;

   if(data->gIconSuite == nil)
   {
      CUsingLibraryResources rsrcAccess;
      ::GetDetachedIconSuite(&data->gIconSuite, 128);
   }
return data->gIconSuite;
}
The GetIconSuite method calls the GetDetachedIconSuite function, shown in Listing 18, to retrieve the icon suite and detach its handles.

Listing 18 The GetDetachedIconSuite function

OSErr GetDetachedIconSuite (Handle *theIconSuite, short resID,
                     IconSelectorValue selector)
{
   OSErr err;
   THz zone = GetZone();
   
   SetZone(SystemZone());
   
   *theIconSuite = nil;
   err = GetIconSuite(theIconSuite, resID, selector);
   
   // Detach the resource handles.
   if (err == noErr)
   {
      RoutineDescriptor detachProc =
         BUILD_ROUTINE_DESCRIPTOR(uppIconActionProcInfo, DetachProc);
      err = ForEachIconDo(*theIconSuite, selector, &detachProc, nil);
   }
   SetZone(zone);
   return err;
}
The GetDetachedIconSuite function calls a the DetachProc function for each icon in the suite. The DetachProc function, shown in Listing 19, detaches the icon's handle.

Listing 19 The DetachProc function

static pascal OSErr DetachProc
      (ResType /* theType */, Handle* theIcon, void* /* yourDataPtr */)
{
   if (theIcon && (*theIcon))
      DetachResource(*theIcon);
   return noErr;
}

Getting and Setting a Cyberdog Item's Characteristics

Your service's item class may need to override additional methods that get or set the data associated with the Cyberdog item, as follows:


Previous Book Contents Book Index Next

© Apple Computer, Inc.
1 JUNE 1996