July 29, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Simplifying Access to BREW Databases

  • May 18, 2005
  • By Ray Rischpater
  • Send Email »
  • More Articles »

Inheriting the BREW IDatabase Interface

All of the methods in Listing 4 assume a well-formed initialized ITaskRecord instance. Constructing each instance is the responsibility of the ITaskDatabase interface, which inherits its interface from IDatabase. Inheriting the IDatabase interface gives clients a well-understood way to create and fetch records from the database, and structuring ITaskDatabase as an extension provides a clear delineation of responsibilities for the users of the interface. The internal representation of ITaskDatabase is pretty simple because it needs only carry around the IDatabase instance in addition to the baggage most extensions carry:

Listing 4

typedef struct _SITaskDatabaseData
{
   ITaskDatabaseVtbl *pvt;
   IModule *pIModule;
   IShell *pIShell;
   uint32 nRefs;
   IDatabase *pIDatabase;
} SITaskDatabaseData;

ITaskDatabase_CreateRecord creates a new ITaskRecord just as IDATABASE_CreateRecord creates a new IDBRecord:

Listing 5

static ITaskRecord *ITaskDatabase_CreateRecord(ITaskDatabase *p)
{
   SITaskDatabaseData *pThis = (SITaskDatabaseData *)p;
   STaskRecordData *pNewRecord;

   if ( !pThis || !pThis->pIDatabase ) return NULL;

   pNewRecord = MALLOC( sizeof( STaskRecordData ) );
   if ( !pNewRecord ) return NULL;

   InitRecord( p, pNewRecord );

   pNewRecord->private.pIDBRecord =
      IDATABASE_CreateRecord( pThis->pIDatabase,
                              pNewRecord->private.aFields,
                              TASKRECORD_MAXFIELDCOUNT );
   if ( !pNewRecord->private.pIDBRecord ) FREEIF( pNewRecord );

   return (ITaskRecord *)pNewRecord;
}

Not surprisingly, ITaskDatabase_CreateRecord doesn't do much. First, it creates the necessary memory to hold a new ITaskRecord (including its private data, of course), and then invokes InitRecord to connect up the AEEDBField structure and the public data, as well as initialize the vtable:

Listing 6

static void InitRecord( ITaskDatabase *p, STaskRecordData *pRecord )
{
   UNUSED( p );
   pRecord->private.aFields[ TASKRECORDFIELD_TITLE ].fType =
      AEEDB_FT_STRING;
   pRecord->private.aFields[ TASKRECORDFIELD_TITLE ].fName =
      TASKRECORDFIELD_TITLE;
   pRecord->private.aFields[ TASKRECORDFIELD_TITLE ].wDataLen =
      sizeof(pRecord->public.wszTitle);
   pRecord->private.aFields[ TASKRECORDFIELD_TITLE ].pBuffer =
      &pRecord->public.wszTitle;

   pRecord->private.aFields[ TASKRECORDFIELD_DUE ].fType =
      AEEDB_FT_DWORD;
   pRecord->private.aFields[ TASKRECORDFIELD_DUE ].fName =
      TASKRECORDFIELD_DUE;
   pRecord->private.aFields[ TASKRECORDFIELD_DUE ].wDataLen =
      sizeof( uint32);
   pRecord->private.aFields[ TASKRECORDFIELD_DUE ].pBuffer =
      &pRecord->public.dwDue;

   pRecord->private.aFields[ TASKRECORDFIELD_COMPLETE ].fType =
      AEEDB_FT_BYTE;
   pRecord->private.aFields[ TASKRECORDFIELD_COMPLETE ].fName =
      TASKRECORDFIELD_COMPLETE;
   pRecord->private.aFields[ TASKRECORDFIELD_COMPLETE ].wDataLen =
      sizeof( boolean );
   pRecord->private.aFields[ TASKRECORDFIELD_COMPLETE ].pBuffer =
      &pRecord->public.bComplete;

   pRecord->public.pvt = &pRecord->vt;
   pRecord->public.pvt->Update  = ITaskRecord_Update;
   pRecord->public.pvt->Remove  = ITaskRecord_Remove;
   pRecord->public.pvt->Release = ITaskRecord_Release;
   pRecord->public.pvt->AddRef  = ITaskRecord_AddRef;
   pRecord->public.pvt->SetContentsOfRecord = ITaskRecord_SetContents;
   // Initialize the private data
   pRecord->private.nRefs = 1;
}

InitTaskRecord is one of those long, boring functions in C that's literally all assignments, a necessary evil. Once this bookkeeping is done, however, the remainder of the work is straightforward; simply create the record in the database using the IDATABASE_CreateRecord function and return the new object.

InitRecord is also used when fetching a record, either from ITaskDatabase_GetRecordByID or ITaskDatabase_GetNextRecord, both of which call LoadRecord:

Listing 7

static int LoadRecord( ITaskRecord *pRecord )
{
   AEEDBFieldName name;
   uint16 nLen = 0;
   boolean *pbComplete = NULL;
   int type = IDBRECORD_NextField( pRecord->private.pIDBRecord,
                                   &name, &nLen );

   while( AEEDB_FT_NONE != type )
   {
      switch( name )
      {
         case TASKRECORDFIELD_TITLE:
         WSTRCOPYGUARDTOFIXED( pRecord->public.wszTitle,
                               IDBRECORD_GetFieldString(
                                  pRecord->private.pIDBRecord ));
         break;

      case TASKRECORDFIELD_DUE:
        IDBRECORD_GetFieldDWord( pRecord->private.pIDBRecord,
                                 &pRecord->public.dwDue );
        break;

      case TASKRECORDFIELD_COMPLETE
         pbComplete = IDBRECORD_GetField( pRecord->
                      private.pIDBRecord, &name, &type, &nLen );
         pRecord->public.bComplete = pbComplete ? *pbComplete :
                                     FALSE;
         break;

      default:
        break;
      }
      type = IDBRECORD_NextField( pRecord->private.pIDBRecord,
                                  &name, &nLen );
   }
   return SUCCESS;
}

LoadRecord is a typical BREW database record accessor function, looping through each of the fields and storing the data associated with the field along the way.

The remainder of the ITaskDatabase implementation—including ITaskDatabase_GetRecordByID and ITaskDatabase_GetNexRecord, which invoke the aforementioned InitRecord and LoadRecord functions—merely wraps around the corresponding IDatabase methods, as you can see in Listing 5.

Conclusion

Although many BREW developers don't take a strong object-oriented approach to BREW development when coding in C, one area where such an approach can pay off is when working with objects that must persist between application invocations in a BREW database. By creating a class object that inherits the BREW IDBRecord interface and links its fields to AEEDBField entries, your application can treat data that persists as if it were local in-memory when accessing and mutating fields, while relying on the BREW database to handle object persistence.

For Further Reading

An introduction to Model-View-Controller: http://c2.com/cgi/wiki?ModelViewController

Writing BREW Extensions





Page 3 of 3



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel