From a1bf8d22297416de9524370f4c0d4f0f0c9e89d4 Mon Sep 17 00:00:00 2001 From: curt Date: Tue, 5 Sep 2000 21:38:00 +0000 Subject: [PATCH] 9/05/2000 updates from David Megginson relating to the property manager and easyxml support. This is a bit of simgear side support to facilitate a configurable instrument panel in flightgear. --- simgear/misc/Makefile.am | 1 + simgear/misc/props.cxx | 213 ++++++++++++++++++------ simgear/misc/props.hxx | 87 ++++++++-- simgear/misc/props_io.cxx | 332 ++++++++++++++++++++++++++++++++++++++ simgear/xml/easyxml.cxx | 14 +- 5 files changed, 578 insertions(+), 69 deletions(-) create mode 100644 simgear/misc/props_io.cxx diff --git a/simgear/misc/Makefile.am b/simgear/misc/Makefile.am index f2166e1f..b1119180 100644 --- a/simgear/misc/Makefile.am +++ b/simgear/misc/Makefile.am @@ -21,6 +21,7 @@ libsgmisc_a_SOURCES = \ fgpath.cxx \ fgstream.cxx \ props.cxx \ + props_io.cxx \ strutils.cxx \ texcoord.cxx \ zfstream.cxx diff --git a/simgear/misc/props.cxx b/simgear/misc/props.cxx index 60a2b193..b5919127 100644 --- a/simgear/misc/props.cxx +++ b/simgear/misc/props.cxx @@ -304,8 +304,6 @@ float SGValue::getFloatValue () const { switch (_type) { - case UNKNOWN: - return 0.0; case BOOL: return (float)(getRawBool()); case INT: @@ -314,6 +312,7 @@ SGValue::getFloatValue () const return getRawFloat(); case DOUBLE: return (float)(getRawDouble()); + case UNKNOWN: case STRING: return (float)atof(getRawString().c_str()); } @@ -330,8 +329,6 @@ double SGValue::getDoubleValue () const { switch (_type) { - case UNKNOWN: - return 0.0; case BOOL: return (double)(getRawBool()); case INT: @@ -340,6 +337,7 @@ SGValue::getDoubleValue () const return (double)(getRawFloat()); case DOUBLE: return getRawDouble(); + case UNKNOWN: case STRING: return atof(getRawString().c_str()); } @@ -357,8 +355,6 @@ SGValue::getStringValue () const { char buf[512]; switch (_type) { - case UNKNOWN: - return getRawString(); case BOOL: if (getRawBool()) string_val = "true"; @@ -377,6 +373,7 @@ SGValue::getStringValue () const sprintf(buf, "%f", getRawDouble()); string_val = buf; return string_val; + case UNKNOWN: case STRING: return getRawString(); } @@ -739,11 +736,11 @@ SGPropertyList::getValue (const string &name) const * better to get the SGValue and query it repeatedly. */ bool -SGPropertyList::getBoolValue (const string &name) const +SGPropertyList::getBoolValue (const string &name, bool defaultValue) const { const SGValue * val = getValue(name); if (val == 0) - return false; + return defaultValue; else return val->getBoolValue(); } @@ -756,11 +753,11 @@ SGPropertyList::getBoolValue (const string &name) const * better to get the SGValue and query it repeatedly. */ int -SGPropertyList::getIntValue (const string &name) const +SGPropertyList::getIntValue (const string &name, int defaultValue) const { const SGValue * val = getValue(name); if (val == 0) - return 0; + return defaultValue; else return val->getIntValue(); } @@ -773,11 +770,11 @@ SGPropertyList::getIntValue (const string &name) const * better to get the SGValue and query it repeatedly. */ float -SGPropertyList::getFloatValue (const string &name) const +SGPropertyList::getFloatValue (const string &name, float defaultValue) const { const SGValue * val = getValue(name); if (val == 0) - return 0.0; + return defaultValue; else return val->getFloatValue(); } @@ -790,11 +787,11 @@ SGPropertyList::getFloatValue (const string &name) const * better to get the SGValue and query it repeatedly. */ double -SGPropertyList::getDoubleValue (const string &name) const +SGPropertyList::getDoubleValue (const string &name, double defaultValue) const { const SGValue * val = getValue(name); if (val == 0) - return 0.0; + return defaultValue; else return val->getDoubleValue(); } @@ -807,11 +804,12 @@ SGPropertyList::getDoubleValue (const string &name) const * better to save the SGValue and query it repeatedly. */ const string & -SGPropertyList::getStringValue (const string &name) const +SGPropertyList::getStringValue (const string &name, + const string &defaultValue) const { const SGValue * val = getValue(name); if (val == 0) - return empty_string; + return defaultValue; else return val->getStringValue(); } @@ -1041,7 +1039,7 @@ get_base (const string &parent, const string &child, */ SGPropertyNode::SGPropertyNode (const string &path = "", SGPropertyList * props = 0) - : _props(props) + : _props(props), _node(0) { setPath(path); } @@ -1052,6 +1050,8 @@ SGPropertyNode::SGPropertyNode (const string &path = "", */ SGPropertyNode::~SGPropertyNode () { + delete _node; + _node = 0; } @@ -1089,24 +1089,6 @@ SGPropertyNode::getName () const } -/** - * Return the value of the current node. - * - * Currently, this does a lookup each time, but we could cache the - * value safely as long as it's non-zero. - * - * Note that this will not create the value if it doesn't already exist. - */ -SGValue * -SGPropertyNode::getValue () -{ - if (_props == 0 || _path.size() == 0) - return 0; - else - return _props->getValue(_path); -} - - /** * Return the number of children for the current node. */ @@ -1143,17 +1125,18 @@ SGPropertyNode::size () const * A return value of true means success; otherwise, the node supplied * is unmodified. */ -bool -SGPropertyNode::getParent (SGPropertyNode &parent) const +SGPropertyNode & +SGPropertyNode::getParent () const { + if (_node == 0) + _node = new SGPropertyNode(); + string::size_type pos = _path.rfind('/'); if (pos != string::npos) { - parent.setPath(_path.substr(0, pos-1)); - parent.setPropertyList(_props); - return true; - } else { - return false; + _node->setPropertyList(_props); + _node->setPath(_path.substr(0, pos-1)); } + return *_node; } @@ -1163,11 +1146,14 @@ SGPropertyNode::getParent (SGPropertyNode &parent) const * A return value of true means success; otherwise, the node supplied * is unmodified. */ -bool -SGPropertyNode::getChild (SGPropertyNode &child, int n) const +SGPropertyNode & +SGPropertyNode::getChild (int n) const { + if (_node == 0) + _node = new SGPropertyNode(); + if (_props == 0) - return false; + return *_node; int s = 0; string base; @@ -1180,12 +1166,9 @@ SGPropertyNode::getChild (SGPropertyNode &child, int n) const while (it != end) { if (get_base(pattern, it->first, base) && base != lastBase) { if (s == n) { - string path = _path; - path += '/'; - path += base; - child.setPath(path); - child.setPropertyList(_props); - return true; + _node->setPropertyList(_props); + _node->setPath(_path + string("/") + base); + return *_node; } else { s++; lastBase = base; @@ -1194,7 +1177,133 @@ SGPropertyNode::getChild (SGPropertyNode &child, int n) const it++; } - return false; + return *_node; +} + + +/** + * Return a node for an arbitrary subpath. + * + * Never returns 0. + */ +SGPropertyNode & +SGPropertyNode::getSubNode (const string &subpath) const +{ + if (_node == 0) + _node = new SGPropertyNode(); + + _node->setPropertyList(_props); + _node->setPath(_path + string("/") + subpath); + return *_node; } + +/** + * Return the value of the current node. + * + * Currently, this does a lookup each time, but we could cache the + * value safely as long as it's non-zero. + * + * Note that this will not create the value if it doesn't already exist. + */ +SGValue * +SGPropertyNode::getValue (const string &subpath) +{ + if (_props == 0 || _path.size() == 0) + return 0; + + if (subpath.size() == 0) + return _props->getValue(_path); + else + return _props->getValue(_path + string("/") + subpath); +} + + +/** + * Return a bool value. + */ +bool +SGPropertyNode::getBoolValue (const string &subpath, bool defaultValue) const +{ + if (_props == 0 || _path.size() == 0) + return defaultValue; + + if (subpath == "") + return _props->getBoolValue(_path, defaultValue); + else + return _props->getBoolValue(_path + string("/") + subpath, + defaultValue); +} + + +/** + * Return an int value. + */ +int +SGPropertyNode::getIntValue (const string &subpath, int defaultValue) const +{ + if (_props == 0 || _path.size() == 0) + return defaultValue; + + if (subpath == "") + return _props->getIntValue(_path, defaultValue); + else + return _props->getIntValue(_path + string("/") + subpath, + defaultValue); +} + + +/** + * Return a float value. + */ +float +SGPropertyNode::getFloatValue (const string &subpath, float defaultValue) const +{ + if (_props == 0 || _path.size() == 0) + return defaultValue; + + if (subpath == "") + return _props->getFloatValue(_path, defaultValue); + else + return _props->getFloatValue(_path + string("/") + subpath, + defaultValue); +} + + +/** + * Return a double value. + */ +double +SGPropertyNode::getDoubleValue (const string &subpath, + double defaultValue) const +{ + if (_props == 0 || _path.size() == 0) + return defaultValue; + + if (subpath == "") + return _props->getDoubleValue(_path, defaultValue); + else + return _props->getDoubleValue(_path + string("/") + subpath, + defaultValue); +} + + +/** + * Return a string value. + */ +const string & +SGPropertyNode::getStringValue (const string &subpath, + const string &defaultValue) const +{ + if (_props == 0 || _path.size() == 0) + return defaultValue; + + if (subpath == "") + return _props->getStringValue(_path, defaultValue); + else + return _props->getStringValue(_path + string("/") + subpath, + defaultValue); +} + + // end of props.cxx diff --git a/simgear/misc/props.hxx b/simgear/misc/props.hxx index d2d76902..ebaaf7f2 100644 --- a/simgear/misc/props.hxx +++ b/simgear/misc/props.hxx @@ -19,9 +19,42 @@ #include #include +#include using std::string; using std::map; +using std::istream; +using std::ostream; + +#ifdef UNKNOWN +#pragma warn A sloppy coder has defined UNKNOWN as a macro! +#undef UNKNOWN +#endif + +#ifdef BOOL +#pragma warn A sloppy coder has defined BOOL as a macro! +#undef BOOL +#endif + +#ifdef INT +#pragma warn A sloppy coder has defined INT as a macro! +#undef INT +#endif + +#ifdef FLOAT +#pragma warn A sloppy coder has defined FLOAT as a macro! +#undef FLOAT +#endif + +#ifdef DOUBLE +#pragma warn A sloppy coder has defined DOUBLE as a macro! +#undef DOUBLE +#endif + +#ifdef STRING +#pragma warn A sloppy coder has defined STRING as a macro! +#undef STRING +#endif @@ -80,7 +113,7 @@ public: virtual int getIntValue () const; virtual float getFloatValue () const; virtual double getDoubleValue () const; - virtual const string &getStringValue () const; + virtual const string & getStringValue () const; // Setters. virtual bool setBoolValue (bool value); @@ -231,11 +264,17 @@ public: virtual SGValue * getValue (const string &name, bool create = false); virtual const SGValue * getValue (const string &name) const; - virtual bool getBoolValue (const string &name) const; - virtual int getIntValue (const string &name) const; - virtual float getFloatValue (const string &name) const; - virtual double getDoubleValue (const string &name) const; - virtual const string &getStringValue (const string &name) const; + virtual bool getBoolValue (const string &name, + bool defaultValue = false) const; + virtual int getIntValue (const string &name, + int defaultValue = 0) const; + virtual float getFloatValue (const string &name, + float defaultValue = 0.0) const; + virtual double getDoubleValue (const string &name, + double defaultValue = 0.0L) const; + virtual const string & getStringValue (const string &name, + const string &defaultValue = "") + const; virtual bool setBoolValue (const string &name, bool value); virtual bool setIntValue (const string &name, int value); @@ -331,17 +370,45 @@ public: // Accessors for derived information. virtual int size () const; virtual const string &getName () const; - virtual SGValue * getValue (); - virtual bool getParent (SGPropertyNode &parent) const; - virtual bool getChild (SGPropertyNode &child, int n) const; + virtual SGPropertyNode &getParent () const; + virtual SGPropertyNode &getChild (int n) const; + virtual SGPropertyNode &getSubNode (const string &subpath) const; + + // Get values directly. + virtual SGValue * getValue (const string &subpath = ""); + virtual bool getBoolValue (const string &subpath = "", + bool defaultValue = false) const; + virtual int getIntValue (const string &subpath = "", + int defaultValue = 0) const; + virtual float getFloatValue (const string &subpath = "", + float defaultValue = 0.0) const; + virtual double getDoubleValue (const string &subpath = "", + double defaultValue = 0.0L) const; + virtual const string & + getStringValue (const string &subpath = "", + const string &defaultValue = "") const; private: string _path; - mutable string _name; // for pointer persistence only SGPropertyList * _props; + // for pointer persistence... + // NOT THREAD SAFE!!! + // (each thread must have its own node + // object) + mutable string _name; + mutable SGPropertyNode * _node; }; + +//////////////////////////////////////////////////////////////////////// +// Input and output. +//////////////////////////////////////////////////////////////////////// + +extern bool readPropertyList (istream &input, SGPropertyList * props); +extern bool writePropertyList (ostream &output, const SGPropertyList * props); + + //////////////////////////////////////////////////////////////////////// // Global property manager. diff --git a/simgear/misc/props_io.cxx b/simgear/misc/props_io.cxx new file mode 100644 index 00000000..8515fc73 --- /dev/null +++ b/simgear/misc/props_io.cxx @@ -0,0 +1,332 @@ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "props.hxx" + +#include +#include +#include + +using std::istream; +using std::ostream; +using std::string; +using std::vector; + + +//////////////////////////////////////////////////////////////////////// +// Visitor class for building the property list. +//////////////////////////////////////////////////////////////////////// + +class PropVisitor : public XMLVisitor +{ +public: + PropVisitor (SGPropertyList * props) : _props(props), _level(0), _ok(true) {} + void startDocument (); + void startElement (const char * name, const XMLAttributes &atts); + void endElement (const char * name); + void data (const char * s, int length); + void warning (const char * message, int line, int col); + void error (const char * message, int line, int col); + + bool isOK () const { return _ok; } + +private: + + void pushState (const char * name) { + _states.push_back(_state); + _level++; + _state.name = name; + _state.type = SGValue::UNKNOWN; + _state.data = ""; + _state.hasChildren = false; + _state.hasData = false; + } + + void popState () { + _state = _states.back(); + _states.pop_back(); + _level--; + } + + struct State + { + State () : hasChildren(false), hasData(false) {} + string name; + SGValue::Type type; + string data; + bool hasChildren; + bool hasData; + }; + + SGPropertyList * _props; + State _state; + vector _states; + int _level; + bool _ok; +}; + +void +PropVisitor::startDocument () +{ + _level = 0; + _ok = true; +} + + +void +PropVisitor::startElement (const char * name, const XMLAttributes &atts) +{ + if (!_ok) + return; + + if (_level == 0 && strcmp(name, "PropertyList")) { + _ok = false; + FG_LOG(FG_INPUT, FG_ALERT, "XML document has root element \"" + << name << "\" instead of \"PropertyList\""); + return; + } + + // Mixed content? + _state.hasChildren = true; + if (_state.hasData) { + FG_LOG(FG_INPUT, FG_ALERT, + "XML element has mixed elements and data in element " + << _state.name); + _ok = false; + return; + } + + // Start a new state. + pushState(name); + + // See if there's a type specified. + const char * type = atts.getValue("type"); + if (type == 0 || !strcmp("unknown", type)) + _state.type = SGValue::UNKNOWN; + else if (!strcmp("bool", type)) + _state.type = SGValue::BOOL; + else if (!strcmp("int", type)) + _state.type = SGValue::INT; + else if (!strcmp("float", type)) + _state.type = SGValue::FLOAT; + else if (!strcmp("double", type)) + _state.type = SGValue::DOUBLE; + else if (!strcmp("string", type)) + _state.type = SGValue::STRING; + else + FG_LOG(FG_INPUT, FG_ALERT, "Unrecognized type " << type + << ", using UNKNOWN"); +} + +void +PropVisitor::endElement (const char * name) +{ + if (!_ok) + return; + + // See if there's a property to add. + if (_state.hasData) { + bool status = false; + + // Figure out the path name. + string path = ""; + for (int i = 2; i < _level; i++) { + path += '/'; + path += _states[i].name; + } + path += '/'; + path += _state.name; + + // Set the value + switch (_state.type) { + case SGValue::BOOL: + if (_state.data == "true" || _state.data == "TRUE") { + status = _props->setBoolValue(path, true); + } else if (atof(_state.data.c_str()) != 0.0) { + status = _props->setBoolValue(path, true); + } else { + status =_props->setBoolValue(path, false); + } + break; + case SGValue::INT : + status = _props->setIntValue(path, atoi(_state.data.c_str())); + break; + case SGValue::FLOAT: + status = _props->setFloatValue(path, atof(_state.data.c_str())); + break; + case SGValue::DOUBLE: + status = _props->setDoubleValue(path, atof(_state.data.c_str())); + break; + case SGValue::STRING: + status = _props->setStringValue(path, _state.data); + break; + default: + status = _props->setUnknownValue(path, _state.data); + break; + } + if (!status) + FG_LOG(FG_INPUT, FG_ALERT, "Failed to set property " + << path << " to " << _state.data); + } + + // Pop the stack. + popState(); +} + +void +PropVisitor::data (const char * s, int length) +{ + if (!_ok) + return; + + // Check if there is any non-whitespace + if (!_state.hasData) + for (int i = 0; i < length; i++) + if (s[i] != ' ' && s[i] != '\t' && s[i] != '\n' && s[i] != '\r') + _state.hasData = true; + + _state.data += string(s, length); // FIXME: inefficient +} + +void +PropVisitor::warning (const char * message, int line, int col) +{ + FG_LOG(FG_INPUT, FG_ALERT, "Warning importing property list: " + << message << " (" << line << ',' << col << ')'); +} + +void +PropVisitor::error (const char * message, int line, int col) +{ + FG_LOG(FG_INPUT, FG_ALERT, "Error importing property list: " + << message << " (" << line << ',' << col << ')'); + _ok = false; +} + + + +//////////////////////////////////////////////////////////////////////// +// Property list reader. +//////////////////////////////////////////////////////////////////////// + +bool +readPropertyList (istream &input, SGPropertyList * props) +{ + PropVisitor visitor(props); + return readXML(input, visitor) && visitor.isOK(); +} + + + +//////////////////////////////////////////////////////////////////////// +// Property list writer. +//////////////////////////////////////////////////////////////////////// + +#define INDENT_STEP 2 + +/** + * Return the type name. + */ +static const char * +getTypeName (SGValue::Type type) +{ + switch (type) { + case SGValue::UNKNOWN: + return "unknown"; + case SGValue::BOOL: + return "bool"; + case SGValue::INT: + return "int"; + case SGValue::FLOAT: + return "float"; + case SGValue::DOUBLE: + return "double"; + case SGValue::STRING: + return "string"; + } +} + + +/** + * Escape characters for output. + */ +static void +writeData (ostream &output, const string &data) +{ + for (int i = 0; i < data.size(); i++) { + switch (data[i]) { + case '&': + output << "&"; + break; + case '<': + output << "<"; + break; + case '>': + output << ">"; + break; + default: + output << data[i]; + break; + } + } +} + +static void +doIndent (ostream &output, int indent) +{ + while (indent-- > 0) { + output << ' '; + } +} + + +static bool +writeNode (ostream &output, SGPropertyNode node, int indent) +{ + const string &name = node.getName(); + int size = node.size(); + + // Write out the literal value, if any. + SGValue * value = node.getValue(); + if (value != 0) { + SGValue::Type type = value->getType(); + doIndent(output, indent); + output << '<' << name; + if (type != SGValue::UNKNOWN) + output << " type=\"" << getTypeName(type) << '"'; + output << '>'; + writeData(output, value->getStringValue()); + output << "' << endl; + } + + // Write out the children, if any. + if (size > 0) { + doIndent(output, indent); + output << '<' << name << '>' << endl;; + for (int i = 0; i < size; i++) { + writeNode(output, node.getChild(i), indent + INDENT_STEP); + } + doIndent(output, indent); + output << "' << endl; + } +} + +bool +writePropertyList (ostream &output, const SGPropertyList * props) +{ + SGPropertyNode root ("/", (SGPropertyList *)props); // FIXME + + output << "" << endl << endl; + output << "" << endl; + + for (int i = 0; i < root.size(); i++) { + writeNode(output, root.getChild(i), INDENT_STEP); + } + + output << "" << endl; +} diff --git a/simgear/xml/easyxml.cxx b/simgear/xml/easyxml.cxx index 2331fc37..81038fec 100644 --- a/simgear/xml/easyxml.cxx +++ b/simgear/xml/easyxml.cxx @@ -20,9 +20,10 @@ int XMLAttributes::findAttribute (const char * name) const { int s = size(); - for (int i = 0; i < s; i++) + for (int i = 0; i < s; i++) { if (strcmp(name, getName(i)) == 0) return i; + } return -1; } @@ -39,7 +40,7 @@ XMLAttributes::getValue (const char * name) const if (pos >= 0) return getValue(pos); else - return getValue(0); + return 0; } @@ -122,9 +123,9 @@ class ExpatAtts : public XMLAttributes public: ExpatAtts (const char ** atts) : _atts(atts) {} - int size () const; - const char * getName (int i) const; - const char * getValue (int i) const; + virtual int size () const; + virtual const char * getName (int i) const; + virtual const char * getValue (int i) const; private: const char ** _atts; @@ -162,8 +163,7 @@ ExpatAtts::getValue (int i) const static void start_element (void * userData, const char * name, const char ** atts) { - ExpatAtts attributes(atts); - VISITOR.startElement(name, attributes); + VISITOR.startElement(name, ExpatAtts(atts)); } static void -- 2.39.5