Documentation  |   Table of Contents   |  < Previous   |  Next >   |  Index

3    Personal Data Interchange

Palm OS® Programmer's Companion

Volume II Communications

     

The Palm OS® provides the PDI library API for exchanging Personal Data Interchange (PDI) information with other devices and media. This chapter contains the following sections that describe how to use the Palm OS PDI library:

For detailed information about the PDI library data types, constants, and functions, see Chapter 88, "Personal Data Interchange Library," in Palm OS Programmer's API Reference.

The PDI reader and writer objects make use of the United Data Access (UDA) Manager to manage input and output data streams. "Using UDA for Different Media" provides an overview of using the UDA Manager. The reference information for UDA functions is in Chapter 89, "Unified Data Access Manager," in Palm OS Programmer's API Reference.

About Personal Data Interchange ^TOP^

Personal data interchange involves the exchange of information using a communications medium. The Palm OS PDI Library facilitates the exchange of information using standard vObjects, including data formatted according to vCard and vCal standards.

The vObject standards are maintained by a group known as the versit consortium, which consists of individuals from a number of companies and institutions. The best information about the PDI standards can be found at the consortium's web site:

http://www.imc.org/pdi/ 


These standards are finding increased use in a number of computers and hand-held devices that wish to exchange personal data such as business card and calendar information.

The PDI Library provides a PdiReaderType object for reading vObjects from an input stream, and a PdiWriterType object for writing vObjects to an output stream. The input streams and output streams can be connected to various data sources.

About vObjects ^TOP^

This section provides a brief overview of vObject standards. Two common vObject types are vCards and vCals:

  • vCards are used to exchange virtual business card information electronically. Each vCard can include a large variety of personal and business information about an individual, including name, address, and telecommunications numbers.
  • vCals are used to exchange virtual calendaring and scheduling information electronically. Each vCal can include:
    • vEvent objects, each of which represents a scheduled amount of time on a calendar
    • vTodo objects, each of which defines an action item or assignment

Overview of vObject Structure ^TOP^

This section provides a brief overview of vObject standards, including the vCard and vCal standards. Each vObject standard provides the same, basic organizational structure:

  • Each vObject is a collection of one or more property definitions.
  • Each property definition contains a name, a value, and an optional collection of property parameter definitions.
  • Each property parameter definition contains a name and a value. Each parameter value qualifies the property definition with additional information.
  • A property value can be structured to contain multiple values. The values are typically separated with commas or semicolons.

The vObject standards also allow developers to add custom extensions. All vObject readers that conform to the standard, including the PdiReaderType object, can read these extensions, though not all readers will act upon the information contained in them.

Each property has the following syntax:


PropertyName [';' Parameters] ':' PropertyValue 

Note that property and parameter names are case insensitive.

Listing 3.1 shows a typical vCard definition.

Listing 3.1  Example of a vCard definition


BEGIN:VCARD 
VERSION:2.1 
N:Smith, John;M.;Mr.; Esq. 
TEL;WORK;VOICE; MSG:+1 (408) 555-1234 
TEL;CELL:+1 (408) 555-4321 
TEL;WORK;FAX:+1 (408) 555-9876 
ADR;WORK;PARCEL;POSTAL;DOM:Suite 101;1 Central  St.;Any 
Town;NC;28654 
END:VCARD 

Each line in Listing 3.1 is a property definition, with the exception of the next to last line, which is a continuation of the ADR property definition, and begins with white space. Each property definition is delimited by a CR/LF sequence.

The BEGIN, VERSION, and END lines are examples of simple property definitions.

The N (Name) property has a structured value. The components of the name are separated by semicolons.

Each TEL (Telephone) property has parameters that qualify the kind of telephone number that is being specified.

The ADR (Address) property has parameters and a structured value.


NOTE: The vObject specifications also allow long lines of text to be folded. This means that wherever you can have white space in a property definition, you can insert a CR/LF followed by white space, as shown in the next to last line in Listing 3.1 When the vObject reader finds a CR/LF followed by white space, it unfolds the text back into one long line.

Grouping vObjects

You can specify multiple vObjects in a single vObject data stream. You can also specify a vObject as the value of a property; for example, you can include a vCard as the value of the ADR property of another vCard.

Grouping Properties

You can specify a name for a group of related properties within a vObject. The name is a single character that you use as a prefix to each property in the group.

One use of this facility is to group a comment that describes a property with the property to keep the two together. For example, the following creates a group named G that includes a vCard home telephone property with a comment property:


G.TEL;HOME:+1 (831) 555-1234 
G.Note: This is my home office number. 

Encodings

The default encoding for vObject properties is 7-bit. You can override this encoding for individual property values by using the ENCODING parameter. You can specify various encoding values, including BASE64, QUOTED-PRINTABLE, and 8-BIT.

Character Sets

The default character set for vObject properties is ASCII. You can override the character set for individual property values by using the CHARSET parameter. You can specify any character set that has been registered with the Internet Assigned Numbers Authority (IANA). For example, to specify the Latin/Hebrew encoding, you would use the value ISO-8859-8.

Finding More Information

For a complete description of the vObject specifications, visit the versit consortium's web site:

http://www.imc.org/pdi/ 


About the PDI Library ^TOP^

The Palm OS PDI library is a shared library that provides objects and functions for:

The PDI library handles reading and writing objects in a number of different formats, and from or to a variety of media. For more information about specifying the media, see "Using UDA for Different Media."

PDI Property and Parameter Types ^TOP^

The PDI library provides constants that you can use with the reader and writer objects to specify property information. These include the following types of constants that specify vObject standard entities:

  • The Property Name constants represent the PDI property names. Each of the property name constants starts with the kPdiPRN_ prefix. For example, the kPdiPRN_ADR constant represents the ADR property name. For more information, see the section Property Name Constants in Chapter 88, "Personal Data Interchange Library," in Palm OS Programmer's API Reference.
  • The Property Value Field constants represent the position of property value fields for properties with structured field values. Each of the property value field constants starts with the kPdiPVF_ prefix. For example, the kPdiPVF_ADR_COUNTRY constant represents the COUNTRY field of an ADR property value. For more information, see the section Property Value Field Constants in Chapter 88, "Personal Data Interchange Library," in Palm OS Programmer's API Reference.
  • The Parameter Name constants represent the names of vObject property parameters. Each of the parameter name constants starts with the kPdiPAN_ prefix. For example, the kPdiPAN_Type constant represents the TYPE parameter, and the kPdiPAN_Encoding constant represents the ENCODING parameter. For more information, see the section Parameter Name Constants in Chapter 88, "Personal Data Interchange Library," in Palm OS Programmer's API Reference.
  • The Parameter Value constants represent the combined name and value of parameters. Each of the parameter value constants starts with the kPdiPAV_ prefix. For example, kPdiPAV_ENCODING_BASE64 constant represents the Base64 encoding. For more information, see the section Parameter Value Constants in Chapter 88, "Personal Data Interchange Library," in Palm OS Programmer's API Reference.

For a complete list of all of these constants, see the PdiConst.h file.

The PDI Library Properties Dictionary ^TOP^

The PDI library features a dictionary that stores information about the properties that are considered "well-known." A well-known property is one that is defined in one of the vObject standard specifications, including the vCard and vCal standards. Both of these standards can be found online at the PDI developer's web page:

http://www.imc.org/pdi/pdiproddev.html 


PDI readers and writers use information in the properties dictionary to determine how to read or write a certain property. Specifically, the dictionary stores information about the format of each property value; the reader uses this information to correctly parse the property value, and the writer uses this information to correctly format the written value. This information is important because some property values are structured with multiple fields, while others contain a single value field.

For example, the standard address (ADR) property has a structured value with seven required fields, and the fields are separated by semicolons. The dictionary stores this information, and the PDI reader then knows to read seven, semicolon-separated fields when parsing an ADR property.

By default, each PDI reader and writer uses a standard dictionary when parsing input and generating output. You can, however, override this behavior to parse or generate the value for a property in some other way. For more information, see "Reading Property Values" and "Writing Property Values."

You can also amend or replace the dictionary to add parsing and/or generation of customized PDI properties for your application. For more information, see "Adding Custom Extensions."

PDI Readers ^TOP^

The PDI library provides the PDI reader object for reading and parsing vObject input. A PDI reader object is a structure that stores the current state of parsing through a PDI input stream.

The PDI reader parses the input stream one property at a time, starting with the Begin Object property and finishing with the End Object property.

The PdiReaderType structure stores a variety of information about the current state of parsing the input stream, including the following information about the current property:

  • the encoding and character set
  • the type of the current property, parameter, and property value
  • the name of the current property and parameter
  • the current property's value string
  • a mask of the parsing events encountered for the current property

About Parsing Events

The PDI reader records each parsing event that it encounters while processing a property. For example, when it parses a BEGIN:VCARD property, the PDI reader records the kPdiBeginObjectEventMask, and when it parses a property name, the PDI reader records the kPdiPropertyNameEventMask.

Each event is represented by one of the Reader Event Constants, which are described in Chapter 88, "Personal Data Interchange Library," in Palm OS Programmer's API Reference. The PDI reader records the event by adding (OR'ing) the event constant into the events field of the PdiReaderType structure.

You can determine if a specific event has occurred while parsing the current property by testing that event's constant against the events field in the reader structure. For example, the following statement returns false if the end of the input stream was reached.


return((reader->events & kPdiEOFEventMask)==0); 

PDI Writers ^TOP^

The PDI library provides the PDI writer object for writing vObject output. A PDI writer object is a structure that stores the current state of and manages the generation of PDI data.

The PDI writer sends data to the output stream one property at a time, starting with the Begin Object property and finishing with the End Object property.

The PdiWriterType structure stores information about the current state of writing the output stream, including the following:

  • the encoding and character set of the current property
  • the mode used to write the current property value, which specifies how the property value is structured
  • the number of required fields for the current property value

Format Compatibility ^TOP^

The PDI library can read and write data streams in the following formats:

  • vCard 3.0
  • vCard 2.1
  • vCal 1.0
  • iCalendar
  • Palm format

You can use the PDI library to convert an input data stream that uses one format into an output data stream in another format. For more information, see "Specifying PDI Versions."

Compatibility with Earlier Versions of the Palm OS

The PDI library has been designed to maintain compatibility with earlier versions of the Palm OS, which means that you can use the library functions to receive vObjects from or send vObjects to devices that use those earlier versions.

To take advantage of this compatibility, the PDI library has been built to send or receive data in different formats, one of which is the format supported by earlier versions of the Palm OS that included the ImcUtils implementation.

To include support for this compatibility in a PDI Reader, specify the kPdiOpenParser constant in your call to the PdiReaderNew function.

To include support for this compatibility in a PDI Writer, specify the kPdiPalmCompatibility option when calling the PdiWriterNew function.

International Considerations ^TOP^

The PDI library handles various character sets, including Katakana. If you specify the CHARSET parameter in the input stream, the PDI reader will correctly read the property value.

The PDI library included with version 4.0 of the Palm OS® understands the following character sets:

  • charEncodingAscii
  • charEncodingISO8859_1
  • charEncodingShiftJIS
  • charEncodingISO2022Jp

If you specify an unknown character set, the current character set becomes unknown, as represented by the charEncodingUnknown constant.

Features Not Yet Supported ^TOP^

The PDI library included with version 4.0 of the Palm OS does not handle the following features:

  • Multi-part MIME messages are not handled.
  • The XML version of vObjects is not supported.
  • Applications ignore grouping. The PDI reader parses group identifiers, but ignores them. However, the name of the group most recently parsed is stored in the groupName field of the PdiReaderType object.

Using the PDI Library ^TOP^

This section describes how to use the functions in the PDI library to read or write PDI content. Figure 3.1 shows the typical sequences of calls that you make to read or write vObjects.

To read vObjects, you need to:

  • access the PDI library
  • create a PDI reader
  • read each property in the input stream:
    • read the property name
    • read any parameters for the property
    • read the property value
  • delete the PDI reader
  • unload the PDI library

To write vObjects, you need to:

  • access the PDI library
  • create a PDI writer
  • write each property in the input stream:
    • write the property name
    • write any parameters for the property
    • write the property value
  • delete the PDI writer
  • unload the PDI library

The remainder of this section describes the following operations:

The section "Using a PDI Reader - An Example" provides a detailed example of creating a PDI Reader and using it to import vCard data into a database.

The section "Using a PDI Writer - An Example" provides a detailed example of creating a PDI Writer and using it to export data from a database in vCal format.

Figure 3.1  Using the PDI library

Accessing the PDI Library ^TOP^

Before you can use the PDI library, you must load the library and obtain a reference number for it. Each of the functions in the library requires a reference number argument, which is used with the system code to access a shared library.

The example function LoadPdiLibrary, which is shown in Listing 3.2, makes sure that the PDI library is loaded and returns a reference number for it.

Listing 3.2  Loading the PDI library


Static Err LoadPdiLibrary(UInt16 *libRefNum) 
{ 
   Err   error 
  
   error = SysLibFind(kPdiLibName, librefNum); 
   if (error != 0) 
   { 
      error = SysLibLoad(sysResTLibrary,
                               sysFileCPdiLib, libRefNum); 
   } 
   if (error) 
   { 
      ErrNonFatalDisplay(kPdiLibName "not found") 
      return error; 
   } 
   error = PdiLibOpen(*libRefNum); 
   return error; 
} 

The LoadPdiLibrary function first calls the SysLibFind function to determine if the library has already been loaded, which might be the case if your code has been called by another application that has already loaded the library. Note that the call to SysLibFind uses the kPdiLibName constant, which is defined as follows in the PdiLib.h file:


#define kPdiLibName "Pdi.lib" 

If the library has not already been loaded, LoadPdiLibrary calls the SysLibLoad function to load the library and obtain a reference number for it.

After obtaining a reference number for the library, LoadPdiLibrary calls the PdiLibOpen function to open the loaded library.

Unloading the PDI Library ^TOP^

When you are done with the library, you should unload it. The example function UnloadPdiLibrary, which is shown in Listing 3.2, unloads the PDI library.

Listing 3.3  Unloading the PDI library


static void UnloadPdiLibrary(UInt16 refNum) 
{ 
   if (PdiLibClose(refNum) == 0) 
   { 
      SysLibRemove(refNum); 
   } 
} 

Note that the library reference number becomes invalid after you call the SysLibRemove function.

Creating a PDI Reader ^TOP^

To create a PDI reader, you need to first access the library, and then call the PdiReaderNew function, which is declared as follows:


PdiReaderType* PdiReaderNew(UInt16 libRefnum,
UDAReader *input, UInt16 optionFlags) 

The PdiReaderNew parameters are:

Once you have created the reader, you can use it to parse properties from the input stream. The section "Using a PDI Reader - An Example" provides an example of creating and using a PDI reader.

Reading Properties ^TOP^

To read PDI property data with a PDI reader, you need to call the data reading functions:

  • PdiReadProperty reads a property and all of its parameters from the input stream.
  • PdiReadPropertyName reads just the name of the next property from the input stream. You can call this function if you want to then handle the reading of the property's parameters individually.
  • PdiReadParameter reads a single parameter and its value from the input stream.
  • PdiReadPropertyField reads a property value field. A property value can a simple value, or it can be structured to contain multiple fields that are separated by commas or semicolons, as described in "Reading Property Values."

The most common way to read input data is to follow these steps:

  • Call PdiReadProperty to read the vObject Begin property. For example, if you are reading vCards, you can call PdiReadProperty until it reads the kPdiPRN_BEGIN_VCARD property from the input stream.
  • Once you have found the beginning of the object, repeatedly call PdiReadProperty to read the next property and its parameters.
  • For each property, call the PdiReadPropertyField function as required to read the fields of the property.
  • Continue reading properties until you read the vObject End property. For vCards, you process properties until PdiReadProperty reads the kPdiPRN_END_VCARD property from the input stream.

Examining Property Information

After calling a property-reading function, you can access fields of the PdiReaderType object to determine information about the current property. The current property is the one that is currently being parsed, or which has just been parsed.

For example, you can examine the property field of the PdiReaderType object to determine which type of property has just been read, or you can call the PdiParameterPairTest macro to determine if a certain parameter pair was present in the property definition.

Reading Property Values ^TOP^

Some properties have simple values and others have structured values. A structured property value has multiple fields that are separated by commas or semicolons.

For example, the following phone property definition has a simple value:


TEL;CELL:+1 (408) 555-4321 

Note that the phone property contains a semicolon to separate the CELL parameter from the property name. Each property's value follows the colon in the definition.

The following name property definition has a structured value that contains four fields separated by semicolons:


N:Smith; John;M.;Mr.; Esq. 

You must pass a parameter to the PdiReadPropertyField function to tell it how to process a property value. To specify how the field is formatted, use one of the Property Value Format Constants described in Chapter 88, "Personal Data Interchange Library," in Palm OS Programmer's API Reference.

You can specify kPdiDefaultFields to allow the PDI reader to determine the property value format. The reader looks up the property name in the dictionary to determine its format.

  • Specify kPdiNoFields to have the reader parse the entire value in one operation.
  • Specify kPdiCommaFields or kPdiSemicolonFields to have the reader parse a single field from the value.
  • Specify kPdiConvertComma or kPdiConvertSemicolon to have the reader parse all of the fields in a value into a single value.

You can usually specify kPdiDefaultFields and allow the PDI Reader to use the information in the dictionary to properly parse the value. However, this might not always meet your needs, especially if your input stream contains custom properties.

Table 3.1 shows the results of using the different format constants to read the same property from the input stream. The example property is a standard address (ADR) property that has a structured value with seven, semicolon-delimited fields:


ADR:postoffice;extended;street;locale;region;postal_code;country 

Note that since the ADR property is defined in the vCard standard as a structured value with seven, semicolon-delimited field, the PDI library dictionary defines its default format as kPdiSemicolon.

Table 3.1  Parsing a structured value with different value format types 

Value format type

Description of PdiReadPropertyField results

kPdiNoFields

One call returns the entire value as a string:

"postoffice;extended;street;locale;region;postal_code;country"

kPdiSemicolon

Each call returns a single, semicolon-delimited field from the value. For example:

• the first call returns "postoffice"
• the second call returns "extended"
• the third call returns "street"

kPdiComma

Each call returns a single, comma-delimited field from the value. For example, if the input string is "postoffice,extended,street," then:

• the first call returns "postoffice"
• the second call returns "extended"
• the third call returns "street"

kPdiConvertSemicolon

One call returns the entire value as a string that has newline characters wherever a semicolon appeared in the input:

"postoffice
extended
street
locale
region
postal_code
country"

kPdiConvertComma

One call returns the entire value as a string that has newline characters wherever a comma appeared in the input:

"postoffice
extended
street
locale
region
postal_code
country"

kPdiDefaultFields

Same as kPdiSemicolon, because the PDI library dictionary defines the property value format of the ADR field as kPdiSemicolon.

Reading Value Fields One At a Time

If you are reading the fields in a structured value one at a time, and you don't know the exact number of fields, you can call PdiReadPropertyField repeatedly until it returns a nonzero result.

For example, the following code segment from the DateTransfer.c program parses each field of the EXDATE property value fields:

Listing 3.4  Reading an undetermined number of value fields


while (PdiReadPropertyField(pdiRefNum, reader, &tempP,
                         kPdiResizableBuffer, kPdiSemicolonFields) == 0) 
   { 
               // Resize handle to hold exception 
      err = MemHandleResize(exceptionListH,
sizeof(ExceptionsListType) + sizeof(DateType) * exceptionCount); 
      ErrFatalDisplayIf(err != 0, "Memory full"); 
              // Lock exception handle 
      exceptionListP = MemHandleLock(exceptionListH); 
              // Calc exception ptr 
      exceptionP = (DateType*)((UInt32)exceptionListP
                         + (UInt32)sizeof(UInt16) 
                         + (UInt32)(sizeof(DateType) * exceptionCount)); 
               // Store exception into exception handle 
      MatchDateTimeToken(tempP, exceptionP, NULL); 
               // Increase exception count 
      exceptionCount++; 
               // Unlock exceptions list handle 
      MemHandleUnlock(exceptionListH); 
   } 


NOTE: If you leave fields in a structured value unread, the next call to PdiReadProperty will skip over them and correctly find the beginning of the next property.

Adding Custom Extensions

The vObject standards are extensible, which means that you can add custom properties to vCards and other vObjects. The PDI library handles these custom properties; however, you must either add an entry to the library's dictionary for each custom property, or specify a constant other than kPdiDefaultFields when parsing the property's value.

Each PDI reader object and each PDI writer object can have a custom dictionary associated with it. You can configure the custom dictionary to amend or to replace the standard, built-in dictionary.

To associate a custom dictionary with a reader or writer, you need to first create the dictionary with the You can then call the PdiDefineReaderDictionary function to associate that dictionary with a reader object or call the PdiDefineWriterDictionary function to associate the dictionary with a writer object.


NOTE: For more information about the dictionary tool at http://www.palmos.com/dev/tech/kb.

Creating a PDI Writer ^TOP^

To create a PDI writer, you need to first access the library, and then call the PdiWriterNew function, which is declared as follows:


PdiWriterType* PdiWriterNew(UInt16 libRefnum,
UDAWriter *output, UInt8 optionFlags) 

The PdiWriterNew parameters are:

Once you have created the writer, you can use it to generate properties to the output stream. The section "Using a PDI Writer - An Example" provides an example of creating and using a PDI writer.

Writing Properties ^TOP^

To write PDI data with a PDI writer, you need to call the data writing functions. The most commonly used functions are:

The most common way to write output data is to follow these steps:

  • Call PdiWriteBeginObject to write the vObject Begin property. For example, if you are writing vCards, you call PdiWriteBeginObject to write the kPdiPRN_BEGIN_VCARD property to the output stream.
  • For each property that you want to write, call PdiWriteProperty to write the next property and its parameters, and then call the PdiWritePropertyValue function to write the property's value.
  • Call PdiWriteEndObject to write the vObject End property. For example, if you are writing vCards, you call PdiWriteEndObject to write the kPdiPRN_END_VCARD property to the output stream.

Writing Property Values ^TOP^

In many cases, you can simply call the PdiWritePropertyValue function to write a value to the output stream. If a value contains a variable number of fields, you can instead use the PdiWritePropertyFields to write the fields from an array. Or you can use the PdiWritePropertyStr to write multiple fields separated by commas or semicolons.

Specifying PDI Versions ^TOP^

The PDI library options constants control how the PDI reader and PDI writer operate. These options are described in Reader and Writer Options Constants in Chapter 88, "Personal Data Interchange Library," in Palm OS Programmer's API Reference.

Using UDA for Different Media ^TOP^

The PDI reader and writer objects use Unified Data Access (UDA) Manager objects for reading from and writing to a variety of media. The UDA data types, constants, and functions are documented in Chapter 89, "Unified Data Access Manager," in Palm OS Programmer's API Reference. This section provides an overview of using UDA objects with the PDI library.

About the UDA Library ^TOP^

The UDA Manager provides an abstract layer for reading, filtering, and writing data to and from different media. The UDA Manager provides three general purpose object types:

  • UDAReaderType objects (UDA Readers) read data from an input stream.
  • UDAFilterType objects (UDA Filters) take input from UDA Readers or UDA Filters, perform some encoding or decoding operations, and output the data to a memory buffer.
  • UDAWriterType objects (UDA Writers) write data to a filter or an output stream.

The UDA Manager provides general purpose functions for creating these object types. In addition, the UDA Manager provides built-in object types for working with memory buffers and the Exchange Manager.


NOTE: The implementation of the UDA Manager in version 4.0 of the Palm OS does not provide built-in filter objects. These objects are planned for future versions.

Interfacing with the Exchange Manager

The UDA Manager provides two functions for interfacing with the Exchange Manager:

  • The UDAExchangeReaderNew function creates a UDA Reader object that reads data from an Exchange Manager socket.
  • The UDAExchangeWriterNew function creates a UDA Writer object that writes data to an Exchange Manager socket.

The Exchange Manager, which is described in Chapter 1, "Object Exchange," provides a mechanism for reading typed data in a transport-independent manner.

When you use the UDA interface to the Exchange Manager, you add the benefits of a simple, uniform way to read and write data in a transport-independent manner. This allows you to create PDI readers and writers that can work on data that is stored on a variety of media types.

If you wish to parse PDI objects from memory, you can use an object created by the UDAMemoryReaderNew function instead of an Exchange Manager reader object.

The PDI Reader example in the next section reads its data from an Exchange Manger socket, using the UDAExchangeReaderNew function to create the reader object.

The PDI Writer example in "Using a PDI Writer - An Example" writes its data to an Exchange Manager socket, using the UDAExchangeWriterNew function to create the writer object.

Using a PDI Reader - An Example ^TOP^

This section provides an example of reading PDI data from an input stream and storing it in a database. This example is from the AddressTransfer.c file, which is located inside of the Examples/Address/Src folder.

Listing 3.5 shows the TransferReceiveData function from the AddressTransfer.c sample program. This function controls the reading of vCard data into the address database by performing the following operations:

  • Calls the ExgAccept function to accept a connection from a remote device.
  • Calls a local function, PrvTransferPdiLoadLibrary, to load an open the PDI library. The PrvTransferPdiLoadLibrary function is almost exactly the same as the LoadPdiLibrary function shown in Listing 3.2.
  • Calls the UDAExchangeReaderNew function to create an input data stream for connection with the Exchange Manager.
  • Calls the PdiReaderNew function to create a new PDI reader object that reads from the input stream.
  • Repeatedly calls the local function TransferImportVCard to read vCard data and store it into the address database. This function is described in the next section, Importing vCard Data Into a Database.
  • Calls the ExgDisconnect function to terminate the transfer and close the connection.
  • Calls the PrvTransferPdiLibUnload function to unload the PDI library.
  • Deletes the PDI reader and UDA input stream objects.

Listing 3.5  Reading a PDI input stream


extern Err TransferReceiveData(DmOpenRef dbP, ExgSocketPtr exgSocketP) 
{ 
   volatile Err err; 
   UInt16 pdiRefNum = sysInvalidRefNum; 
   PdiReaderType* reader = NULL; 
   UDAReader* stream = NULL; 
   Boolean loaded; 
  
   if ((err = ExgAccept(exgSocketP)) != 0) 
      return err; 
   if ((err = PrvTransferPdiLibLoad(&pdiRefNum, &loaded))) 
   { 
      pdiRefNum = sysInvalidRefNum; 
      goto errorDisconnect; 
   } 
   if ((stream = UDAExchangeReaderNew(exgSocketP)) == NULL) 
   { 
      err = exgMemError; 
      goto errorDisconnect; 
   } 
   if ((reader = PdiReaderNew(pdiRefNum, stream, kPdiOpenParser)) == NULL) 
   { 
      err = exgMemError; 
      goto errorDisconnect; 
   } 
   reader->appData = exgSocketP; 
   ErrTry 
   { 
      while(TransferImportVCard(dbP, pdiRefNum, reader, false, false)){}; 
   } 
   ErrCatch(inErr) 
   { 
      err = inErr; 
   } ErrEndCatch 
   if (err == errNone && exgSocketP->goToParams.uniqueID == 0) 
      err = exgErrBadData; 
errorDisconnect: 
   if (reader) 
      PdiReaderDelete(pdiRefNum, &reader); 
   if (stream) 
      UDADelete(stream); 
   if (pdiRefNum != sysInvalidRefNum) 
      PrvTransferPdiLibUnload(pdiRefNum, loaded); 
   ExgDisconnect(exgSocketP, err); // closes transfer dialog 
   err = errNone; // error was reported, so don't return it 
   return err; 
} 

Importing vCard Data Into a Database

The TransferImportVCard function imports a vCard record from an input stream. Listing 3.6 shows the basic outline of the TransferImportVCard function; you can review the entire function by viewing the AddressTransfer.c file, which is located inside of the Examples/Address/Src folder.

Listing 3.6  Importing vCard data into a database


Boolean TransferImportVCard(DmOpenRef dbP, UInt16 pdiRefNum,
PdiReaderType* reader, Boolean obeyUniqueIDs, Boolean beginAlreadyRead) 
{ 
  
...    // local declarations and initialization code 
  
   ErrTry 
   { 
      phoneField = firstPhoneField; 
      if (!beginAlreadyRead) 
      { 
         PdiReadProperty(pdiRefNum, reader); 
         beginAlreadyRead = reader->property == kPdiPRN_BEGIN_VCARD; 
      } 
      if (!beginAlreadyRead) 
         ErrThrow(exgErrBadData); 
      PdiEnterObject(pdiRefNum, reader); 
      PdiDefineResizing(pdiRefNum, reader, 16, tableMaxTextItemSize); 
      while (PdiReadProperty(pdiRefNum, reader) == 0 
               && (property = reader->property) != kPdiPRN_END_VCARD) 
      { 
         switch(property) 
         { 
         case kPdiPRN_N: 
         PdiReadPropertyField(pdiRefNum, reader,
                        (Char **) &newRecord.fields[name],
                           kPdiResizableBuffer, kPdiDefaultFields); 
         PdiReadPropertyField(pdiRefNum, reader,
                        (Char **) &newRecord.fields[firstName],
                           kPdiResizableBuffer, kPdiDefaultFields); 
      break; 
         case kPdiPRN_NOTE: 
            PdiDefineResizing(pdiRefNum, reader, 16,
                           noteViewMaxLength); 
            PdiReadPropertyField(pdiRefNum, reader,
                        Char **) &newRecord.fields[note],
                           kPdiResizableBuffer, kPdiNoFields); 
            PdiDefineResizing(pdiRefNum, reader, 16,
                           tableMaxTextItemSize); 
             break; 
  
,,,  // other cases here for other properties 
  
      } 
      } // end while 
                if (newRecord.fields[name] != NULL 
         && newRecord.fields[company] != NULL 
         && newRecord.fields[firstName] != NULL 
         && StrCompare(newRecord.fields[name],
                              newRecord.fields[company]) == 0) 
      {     // if company & name fields are identical, assume company only 
         MemPtrFree(newRecord.fields[name]); 
         newRecord.fields[name] = NULL; 
      } 
AddRecord: 
         err = AddrDBNewRecord(dbP, (AddrDBRecordType*) &newRecord,
                                 &indexNew); 
         if (err) 
            ErrThrow(exgMemError); 
  
   ...      // handle category assignment here 
  
   }   //end of ErrTry 
   if (error == exgErrBadData) 
      return false; 
   if (error != errNone) 
      ErrThrow(error); 
   return ((reader->events & kPdiEOFEventMask) == 0); 
} 

The TransferImportVCard function performs the following operations:

  • Calls the PdiReadProperty function to read the BEGIN:VCard property from the input stream.
  • Calls the PdiEnterObject function to notify the PDI library that it is reading a new object from the input stream.
  • Calls the PdiDefineResizing function to set the maximum buffer size for reading properties for the address card.
  • Repeatedly calls the PdiReadProperty function to read properties of the address card. This repeats until PdiReadProperty reads the END:VCard property, which indicates the end of data for the address card.
  • For each address card property, calls PdiReadPropertyField as required to read the values associated with the property. For example, when it reads the kPdiPRN_N name property, AddrImportVCard calls PdiReadPropertyField twice: once to read the last name, and a second time to read the first name.
  • Creates a new address record and adds it to the Address Book database.
  • Deallocates memory that it has allocated and performs other cleanup operations.

Again, note that Listing 3.6 only shows the outline of this function. You can find the entire function in the AddressTransfer.c file.

Using a PDI Writer - An Example ^TOP^

This section provides an example of writing PDI data from a database record to an output stream. This example is from the ToDoTransfer.c file, which is located inside of the Examples/ToDo/Src folder.

Listing 3.7 shows an example of creating and using a PDI writer. The ToDoSendRecordTryCatch function controls the writing of data from the To Do database to vCal objects by performing the following operations:

  • Calls a local function, LoadPdiLibrary, to load and open the PDI library. The LoadPdiLibrary function is shown in Listing 3.2.
  • Calls the PdiWriterNew function to create a new PDI writer object that writes to the UDA output stream specified by the media parameter.
  • Calls the PdiWriteBeginObject function to write the BEGIN:VCAL property to the output stream.
  • Calls the PdiWriteProperty function to write the VERSION property, and then calls the PdiWritePropertyValue function to write the version value.
  • Calls the ToDoExportVCal function to write the To Do record, as described in the next section, Exporting vCal Data From a Database.
  • Calls the PdiWriteEndObject function to write the END:VCAL property to the output stream.
  • Deletes the PDI writer object and unloads the PDI library.

Listing 3.7  Writing a PDI Output Stream


static Err ToDoSendRecordTryCatch (DmOpenRef dbP,
        Int16 recordNum, ToDoDBRecordPtr recordP, UDAWriter* media) 
{ 
   volatile Err error = 0; 
   UInt16 pdiRefNum; 
   PdiWriterType* writer; 
  
   if ((error = LoadPdiLibrary(&pdiRefNum))) 
       return error; 
   writer = PdiWriterNew(pdiRefNum, media, kPdiPalmCompatibility); 
   if (writer) 
      { 
ErrTry 
      { 
         PdiWriteBeginObject(pdiRefNum, writer,
                                                  kPdiPRN_BEGIN_VCALENDAR); 
         PdiWriteProperty(pdiRefNum, writer, kPdiPRN_VERSION); 
         PdiWritePropertyValue(pdiRefNum, writer, (Char*)"1.0",
                                         kPdiWriteData); 
          ToDoExportVCal(dbP, recordNum, recordP, pdiRefNum,
                                 writer, true); 
          PdiWriteEndObject(pdiRefNum, writer,
                                                         kPdiPRN_END_VCALENDAR)
; 
          } 
ErrCatch(inErr) 
        { 
           error = inErr; 
        } ErrEndCatch 
         PdiWriterDelete(pdiRefNum, &writer); 
       } 
   UnloadPdiLibrary(pdiRefNum); 
   return error; 
} 

Exporting vCal Data From a Database

The ToDoExportVCal function exports a vCal record from the To Do database to an output stream. Listing 3.8 shows the basic outline of the ToDoExportVCal function; you can review the entire function by viewing the ToDoTransfer.c file, which is located inside of the Examples/Address/Src folder.

Listing 3.8  Exporting vCal data from a database


extern void ToDoExportVCal(DmOpenRef dbP, Int16 index,
ToDoDBRecordPtr recordP, UInt16 pdiRefNum, PdiWriterType* writer,
Boolean writeUniqueIDs) 
{ 
Char *          note; 
      UInt32      uid; 
      Char        tempString[tempStringLengthMax]; 
      UInt16      attr; 
... 
  
   PdiWriteBeginObject(pdiRefNum, writer, kPdiPRN_BEGIN_VTODO); 
      // Emit the Category 
   PdiWriteProperty(pdiRefNum, writer, kPdiPRN_CATEGORIES); 
      // ...code to create the property string (tempString) 
   PdiWritePropertyValue(pdiRefNum, writer, tempString, kPdiWriteText); 
  
      // Code to emit the record information, including the: 
      //   - due date 
      //   - completed flag 
      //   - priority value 
      //   - description text 
... 
  
  
      // Emit the note 
   if (*note != '\0') 
   { 
      PdiWriteProperty(pdiRefNum, writer, kPdiPRN_ATTACH); 
      PdiWritePropertyValue(pdiRefNum, writer, note, kPdiWriteText); 
   } 
  
      // Emit an unique id 
   if (writeUniqueIDs) 
   { 
      PdiWriteProperty(pdiRefNum, writer, kPdiPRN_UID); 
      // Get the record's unique id and append to the string. 
      DmRecordInfo(dbP, index, NULL, &uid, NULL); 
      StrIToA(tempString, uid); 
      PdiWritePropertyValue(pdiRefNum, writer, tempString, kPdiWriteData); 
   } 
  
   PdiWriteEndObject(pdiRefNum, writer, kPdiPRN_END_VTODO); 
} 

The ToDoExportVCal function performs the following operations:

  • Calls the PdiWriteBeginObject function to write the BEGIN:VTODO property to the output stream.
  • Calls the PdiWriteProperty function to write the category information for the To Do record.
  • Calls the PdiWriteProperty function to write other information for the To Do record, including the due date, completed flag, priority value, and description text.
  • Calls the PdiWriteProperty function to write the note and again to write a unique ID for the note.
  • Calls the PdiWriteEndObject function to write the END:VTODO property to the output stream.

Again, note that Listing 3.8 only shows the outline of this function. You can find the entire function in the ToDoTransfer.c file.

Summary of Personal Data Interchange ^TOP^

Summary of Unified Data Access Manager ^TOP^