XML Read/Write

Overview

HUtilityXMLParser and HUtilityXMLGenerator are two classes that read/write generic XML data. Currently, XML is used as part of the HSF specification to describe extensions to the format (e.g. the behavior extension) and we encourage its usage for any kind of user data.Various validating and non-validating XML parsers already exist. However, we felt that integrating a simple parser into MVO would simplify the usage of XML and not force people to add another library to their project and possibly deal with the associated licensing issues.

Parsing XML Data

The HUtilityXMLParser class implements a simple non-validating XML parser. It uses a callback architecture to register functions that handle specific tags and these functions in turn query the parsing toolkit for their parameters.First a new instance of HUtilityXMLParser needs to be created. After that a memory buffer containing the XML data in ASCII format must be passed to the toolkit. This buffer must be a valid block containing any number of XML tags.

HUtilityXMLParser xp; xp.SetupParseBuffer(buffer);

For every XML tag that requires handling a callback function needs to be set. This function receives the tag-name and all the tag parameters. Tags for which no function has been registered will be disregarded. SetTagCallback takes the name of the tag, a function pointer and a void pointer to generic data:

xp.SetTagCallback("BehaviorManager", HBhvBehaviorManager::XMLCallback, model);
xp.SetTagCallback("Animdef", HBhvBehaviorManager::XMLCallback, model);
xp.SetTagCallback("Sensor", HBhvSensor::XMLCallback, model);
...

Finally, ProcessXMLData() must be called to initiate the parsing process:

xp.ProcessXMLData();

Here is an example of a callback function for the animation sensor tag:

// the first parameter is a pointer to the XML parser
// the second parameter indicates if the callback has been called from an "open tag" or from
// a "close tag". For every tag the callback gets called two times. Once to indicate the start
// of the tag and once to indicate the end of the tag
// the third tag is a pointer to extra-data which has been passed in when the callback got registered
void *HBhvSensor::XMLCallback(HUtilityXMLTag *xt, bool open, void *m_pExtraData)
{
char Name[256];
int active = true;

HBaseModel *model = (HBaseModel *)m_pExtraData;

if (open)
{
        // GetProperty retrieves a tag-property
        // there are various overloaded versions of GetProperty for retrieving string and numerical properties
        // it is the responsibility of the callback to set a default if the property has not been explicitly set
        if (!xt->GetProperty("Name", Name))
                strcpy(Name, "");

        xt->GetProperty("Active", active);

        HBhvSensor * sensor = new HBhvSensor(Name, active != 0, model->GetBhvBehaviorManager());
        model->GetBhvBehaviorManager()->AddSensor(sensor);

        // the returned void pointer will be added to the stack and can be retrieved by enclosed tag callbacks
        // by calling the function GetStackData()
        // Example:  HBhvSensor * sensor = (HBhvSensor *)xt->GetXMLParser()->GetStackData();
        return (sensor);
}

return 0;
}

Writing XML Data

HUtilityXMLGenerator allows the creation of valid XML blocks. Similar to HUtilityXMLParser we start by creating a new HUtilityXMLGenerator object. Then each object needs to serialize itself in a hierachical fashion which mirrors the structure of the XML data that needs to be generated:

HUtilityXMLGenerator xmlgen;
Serialize(&xmlgen);

After all XML Data has been created HUtilityXMLGenerator::Finalize needs to be called and the buffer can be written to a file:

xmlgen.Finalize();
FILE *fp;
fp = fopen(filename, "w");
fwrite(xmlgen.GetBuffer(), 1, xmlgen.GetBufferSize(), fp);
fclose(fp);

Here is an example of how a serialization function might look like:

void HBhvSensor::Serialize(HUtilityXMLGenerator *xmlgen)
{
// HUtilityXMLTag represents an XML tag and it's properties
HUtilityXMLTag xmlt;

//the tagname needs to be specified
xmlt.SetTagname("Sensor");

// Properties are added in a sequential fashion
// AddProperty takes the property name, the value and for strings an additional parameter indicating
// if brackets should be placed around the property
xmlt.AddProperty("Name", m_Name, true);
xmlt.AddPropertyInt("Active", (int)m_bDefaultActive);

// To add data (which resides between the start and end-tag) the following function can be used
// xmlgen->AddListItem(text);

// if true is passed into SetOpen this indicates that this tag has further subtags
xmlt.SetOpen(true);

// after all properties have been set the new tag object is added to the xml generator object
xmlgen->AddTag(&xmlt);

// if this tag contains subtags those need to be recursively serialized
START_LIST_ITERATION(HBhvCondition, m_ConditionList)
        temp->Serialize(xmlgen);
END_LIST_ITERATION(m_ConditionList);

START_LIST_ITERATION(HBhvAction, m_ActionList)
        temp->Serialize(xmlgen);
END_LIST_ITERATION(m_ActionList);

// Finally the tag needs to be closed
xmlgen->CloseTag(&xmlt);
}