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();
---