Versioning and Storing Additional User Data

Versioning

As discussed in the previous section, one way for you to check if the file contains custom HSF objects that you know/care about is to always use custom opcode handlers, which then check during the reading process to see if there is user data. However, there is one deliberate flaw to the example approach and its corresponding sample code. If the code sees a custom chunk of data following the default TK_Shell object (by noticing a #TKE_Start_User_Data opcode), it simply goes ahead and reads the data, assuming that it was data written out by our custom shell handler. However, what if the file was written out by a custom handler that was not ours?! In this case, we wouldn’t understand the information and don’t care about it. However, the sample code does not properly check if the data is something that we know or care about. Because it is assuming a specific amount of user data, and this is an unsafe assumption, the code is flawed.

One potential solution is to add another stage during the writing process: after writing out the #TKE_Start_User_Data opcode and the # of bytes of custom data, we could also write out some special value which ‘marks’ the custom data as ‘our’ custom data. Then, during reading, we would check that special value to confirm if it was our data. However, this solution is a bit cumbersome since it means that our custom logic would always need to be executed, and to properly handle the case, we’d also have to either A) peek at the data up through the special value and then return from the function (so that the default toolkit will skip the custom data) or B) manually skip through the custom data ourselves by utilizing the ‘number of bytes’ information.

A better solution would be to store some type of additional versioning information in the beginning of the file which could be checked once, and then we would create and register our custom HSF object handlers only if the file was verified to be a custom version that we created with our custom toolkit. Recalling that the first opcode in an HSF file is always a #TKE_Comment opcode (with contents that are specifically formatted to denote file version information), you could export another #TKE_Comment opcode immediately after the first one with contents that contain additional version information. For example:

<TKE_Comment> standard version information; contents:  HSF V11.00
<TKE_Comment> custom version information; contents:  SuperCAD V2.10
<data opcode>
          .
          .
          .
<data opcode>
<TKE_Termination>

The following section details how additional information could be added at the beginning of the file (prior to default HSF objects) as well as at the end of the file.

Storing Additional User Data

In addition to providing support for attaching/retreiving user data to/from default HSF objects (by enabling overloading of the Write and Read methods of opcode handlers), the HOOPS/Stream Toolkit also provides general support for exporting user data via the TK_XML, TK_User_Data, and TK_URL opcode handlers, which export the #TKE_XML, #TKE_Start_User_Data, and #TKE_URL opcodes, respectively. This gives developers the ability to store discrete chunks of user data that may (or may not) be associated with the HSF objects. The TK_XML opcode handler would be used to store/retreive XML data, and the TK_User_Data opcode handler would be used to store and retrieve custom binary data. The TK_URL opcode handler provides informational links corresponding to data (as opposed to #TKE_External_Reference which provides additional content)..

When writing out user data within the Write method of your custom TK_User_Data object, be sure to review the process of formatting user data.

To handle import/export of user data, you will need to register a custom opcode handler for the #TKE_Start_User_Data opcode. This is because the toolkit’s default handler (TK_User_Data) simply skips over the user data that is read in. (Remember that custom opcode handlers such as My_TK_Shell described in the previous section typically only handle user data that is appended to a default HSF object. If you are adding discrete chunks of user data to the file, then you must write or read that data with an entirely new TK_User_Data handler) The following steps are involved:

1. Define a new class derived from TK_User_Data (which we’ll call ``TK_My_User_Data``) that overloads the ``Write`` and ``Read`` methods to process the extra user data.

Example:

#include "object.h"

class TK_My_User_Data : public TK_User_Data
{
        protected:
                int     my_stage;   // denotes the current processing stage

        public:
                TK_My_User_Data(unsigned char opcode) : TK_User_Data(opcode) {}

                // Within Read(), we  may need to verify that the user data is 'our'
                // user data.  As previously noted, one approach is to write out
                // versioning information at the beginning of the file.
                // If it is not our custom version of the file, we would NOT
                // even register this custom user data opcode handler; instead
                // we would allow the default TK_User_Data handler to take care of
                // the TKE_Start_User_Data opcode by simply skipping over any user data
                virtual TK_Status       Read (BStreamFileToolkit & tk) alter;
                virtual TK_Status       Write (BStreamFileToolkit & tk) alter;
};

2. Instruct the toolkit to use our custom user data opcode handler in place of the default handler by calling ``BStreamFileTookit::SetOpcodeHandler``. We specify the type of opcode that we want to replace, and pass in a pointer to the new opcode handler object.

Example:

tk->SetOpcodeHandler(TKE_Start_User_Data, new TK_My_User_Data(TKE_Start_User_Data));

This will also cause the toolkit to delete it’s default handler object for the #TKE_Start_User_Data opcode. Note: As the HOOPS/Stream Reference Manual points out, all opcode handler objects stored in the BStreamFileToolkit object will be deleted when the BStreamFileTookit object is deleted. Therefore, we would not delete the TK_My_User_Data object created in the above example.

Custom handling of the #TKE_XML opcode would be similar to the above, but you would instead register a custom opcode handler for the XML opcode that is derived from TK_XML.