7-19-2011 / Phil Weinstein Short Description: Unit Scheme XML (DOM) Serialization / Import & Export Bug Number: n/a Release notes (y/n): no For Release Nums: 6.1 Added Unit Scheme based serialization in XML using Qt DOM classes, including robust import error checking for unit type, unit and object/ account/type names. Here's an example of a tiny Unit Scheme. The first three lines are XML comments, which we would not (ever) attempt to parse. (If we needed versioning information, that would need to be explicitly implemented). =========================================================================== =========================================================================== XML serialization implementation: The hierarchy of the objects being serialized is this: UnitScheme UnitScheme::Rule (many elements, each with two child elements:) UnitScheme::Key (one element, having only attributes) NumDisplayAttribs (one element, having only attributes) Each class is responsible for its own XML serialization except for the NumDisplayAttribs class which is implemented externally (in UnitSchemeUtils). (This is to avoid introducing QtXML DOM class dependencies into the Slot class hierarchy, as Slots use NumDisplayAttribs instances). Export (XML generation) and Import (XML parsing) is accompished through methods at each class level. For export (XML generation), each class serializes itself as an XML element through the use of a method of this form (with NumDisplayAttribs handled differently, as indicated above): QDomElement asXmlDomElement (QDomDocument&) const; // UnitScheme QDomElement asXmlDomElement (QDomDocument&) const; // UnitScheme::Rule QDomElement asXmlDomElement (QDomDocument&) const; // UnitScheme::Key QDomElement UnitSchemeUtils::asXmlDomElement ( QDomDocument& doc, const NumDisplayAttribs& attribs); ... the QDomDocument parameter is used to construct the QDomElement, using the createElement() FACTORY method of the QDomDocument instance. For import (XML parsing), each class provides an explicit constructor taking a QDomElement as its single parameter (with NumDisplayAttribs handled differently, as indicated above): explicit UnitScheme (const QDomElement&); // constructor explicit Rule (const QDomElement&); // constructor explicit Key (const QDomElement&); // constructor NumDisplayAttribs UnitSchemeUtils::numDisplayAttribsFromXmlDomElem (const QDomElement&) A slight break in encapsulation is required to support "import" (XML parsing). Each class provides its XML element's "TAG" string, through these methods: static QString XmlElementTag(); // UnitScheme::Rule static QString XmlElementTag(); // UnitScheme::Key QString UnitSchemeUtils::NumDisplayAttribs_XmlElemTag(); The Import mechanism presents the user with the following dialog (example) once a file has been successfully parsed (as a Unit Scheme): +--- Importing Unit Scheme --------------------------------------------- | | +-----+ | | ! | The selected file contains a unit scheme named "Standard | | o | RiverWare Units" with 52 rules. What would you like to do? | +-----+ | | [ Add rules from file to this unit scheme ] | [ Overwrite this unit scheme with the file's rules ] [ Cancel ] | +----------------------------------------------------------------------- Errors detected during parsing are reported as RiverWare errors (which show up in the Diagnostic Outputs). And those detected after the DOM has been built, and during the instantiation of UnitScheme objects are accumulated through a mechanism in the UnitSchemeMgr: // Unit Scheme Import Parsing Errors enum { MAX_SAVED_PARSE_ERRORS = 25 }; QStringList _parseErrorList; int _parseErrorCnt; void clearParseErrors() { _parseErrorList.clear(); _parseErrorCnt = 0; } void appendParseError (const QString&); // limited int parseErrorCount() const { return _parseErrorCnt; } QStringList parseErrorList() const { return _parseErrorList; } BELOW are some EXAMPLES of GENERATED ERROR MESSAGES. ("Error parsing Unit Scheme" prefix has been omitted, and the filename was omitted from the first two messages). These first two errors are reported by Qt DOM classes. The latter are detected in our own code during UnitScheme object instantiation. In the latter case, the filename is reported in a displayed error popup BUT is not part of the diagnostics error message ... File: "...", Line 32, Column 2. XML parse error: (unexpected character). File: "...", Line 1, Column 1. XML parse error: (error occurred while parsing element). "Key" element, Line 3, Column 26. Invalid unit type: "BADUNITS". "Key" element, Line 3, Column 56. Invalid object type: "Umbrella". "Key" element, Line 11, Column 57. Invalid unit type: "FWUM". "Rule" element, Line 14, Column 7. Unit Type Mismatch: "Length" "Flow". "Attribs" element, Line 16, Column 64. Invalid unit: "flippers". "Rule" element, Line 18, Column 7. Missing Key. "Rule" element, Line 18, Column 7. Missing Attribs. When the error occurs during a model load, Line and Column fields are omitted BECAUSE these numbers are relative to the XML document, so would be confusing -- or hard to implement correctly -- when the XML document is embedded in some other file (i.e. a RiverWare model file). Note, though, that Unit Scheme serialization in RiverWare model files is not yet implemented. The presence of extra attributes or elements is not regarded as an error. (So, this is substantially downward compatible. That is, serialized unit schemes generated by future versions of RiverWare have a good chance of working with older versions of RiverWare, of course without the support of newly added capabilities). ------------------ Sim/UnitScheme.hpp Sim/UnitScheme.cpp ------------------ New methods: // XML Serialization QDomElement asXmlDomElement (QDomDocument&) const; explicit UnitScheme (const QDomElement&); // constructor New utility: void mergeRuleMap (const QHash&); ---------------------------- Sim/MappedUnitSchemeRule.cpp (UnitScheme::Rule) ---------------------------- New methods: // XML Serialization static QString XmlElementTag(); QDomElement asXmlDomElement (QDomDocument&) const; explicit Rule (const QDomElement&); // constructor Also: bool operator< (const Rule& rhs) const; ---------------------------- Sim/MappedUnitSchemeKey.cpp (UnitScheme::Key) ---------------------------- New methods: // XML Serialization static QString XmlElementTag(); QDomElement asXmlDomElement (QDomDocument&) const; explicit Key (const QDomElement&); // constructor Also: bool operator< (const Key& rhs) const; ----------------------- Sim/UnitSchemeUtils.hpp Sim/UnitSchemeUtils.cpp ----------------------- New functions: // XML Serialization for NumDisplayAttribs QString NumDisplayAttribs_XmlElemTag(); QDomElement asXmlDomElement (QDomDocument&, const NumDisplayAttribs&); NumDisplayAttribs numDisplayAttribsFromXmlDomElem (const QDomElement&); // XML generation support void insertUnitSchemeDocComments (QDomDocument&); // XML parsing utilities QString reportXmlFileParseError (const QString& fname, int line, ... QString reportXmlMissingChild (const QDomElement&, const QString&); int xmlParseObjType (const QDomElement&, const QString&); unit_type xmlParseUnitType (const QDomElement&, const QString&); ScaledUnitPtr xmlParseUnit (const QDomElement&, unit_type, ... void xmlCheckUnitTypeMatch (const QDomElement&, UnitScheme::Rule&); Also, these functions were reworked to support parsing: QString typeIdStr (int typeId); QString typeIdPluralStr (int typeId); int typeIdFromString (const QString&, bool* okRet); --------------------- Sim/UnitSchemeMgr.hpp Sim/UnitSchemeMgr.cpp --------------------- (New UnitSchemeMgr fields and methods were indicated in the discussion above). ------------------------------- QtUtils/UnitSchemeEditorDlg.hpp QtUtils/UnitSchemeEditorDlg.cpp ------------------------------- Signficant implementations: void UnitSchemeEditorDlg::importScheme() void UnitSchemeEditorDlg::exportScheme() ... including management of QFileDialog (file choosers) with advanced history and selection persistence implementations. New utility method: QStringList buildFileHistoryList(); ---