From 510091f082f18f9d003ab4803786902330303aef Mon Sep 17 00:00:00 2001 From: curt Date: Wed, 21 Mar 2001 04:57:13 +0000 Subject: [PATCH] From David Megginson: 1. Fixed a bug reported by Curt for one of the getChild methods. 2. Cleaned up some of the error reporting, and require PropertyList as the root element name. Also uncluttered the XML output a little (removed type="unknown"). 3. Added a new Long type (i.e. setLongValue, getLongValue, etc.), as requested by Curt aeons ago. 4. Added aliases, so that a property's value can directly reflect the value of another property. 5. Added a generalized inclusion facility, with parameters (panel writers should love this one). --- simgear/misc/fgpath.cxx | 11 + simgear/misc/fgpath.hxx | 3 + simgear/misc/props.cxx | 529 ++++++++++++++++++++++++++++++++---- simgear/misc/props.hxx | 53 +++- simgear/misc/props_io.cxx | 119 ++++++-- simgear/misc/props_test.cxx | 2 +- simgear/xml/easyxml.cxx | 4 + 7 files changed, 652 insertions(+), 69 deletions(-) diff --git a/simgear/misc/fgpath.cxx b/simgear/misc/fgpath.cxx index 0ae86cd8..e6317a3c 100644 --- a/simgear/misc/fgpath.cxx +++ b/simgear/misc/fgpath.cxx @@ -100,3 +100,14 @@ void FGPath::concat( const string p ) { path += part; } } + + +// get the directory part of the path. +string FGPath::dir() { + int index = path.rfind(FG_PATH_SEP); + if (index >= 0) { + return path.substr(0, index); + } else { + return ""; + } +} diff --git a/simgear/misc/fgpath.hxx b/simgear/misc/fgpath.hxx index 30f6376d..d3b86112 100644 --- a/simgear/misc/fgpath.hxx +++ b/simgear/misc/fgpath.hxx @@ -75,6 +75,9 @@ public: // path separator void concat( const string p ); + // get the directory part of the path + string dir(); + // get the path string inline string str() const { return path; } inline const char *c_str() { return path.c_str(); } diff --git a/simgear/misc/props.cxx b/simgear/misc/props.cxx index 8405748c..7c07c203 100644 --- a/simgear/misc/props.cxx +++ b/simgear/misc/props.cxx @@ -18,13 +18,26 @@ #include #include "props.hxx" -#if !defined(FG_HAVE_NATIVE_SGI_COMPILERS) -FG_USING_STD(cerr); -FG_USING_STD(endl); -#endif FG_USING_STD(sort); + +//////////////////////////////////////////////////////////////////////// +// Local classes. +//////////////////////////////////////////////////////////////////////// + +/** + * Comparator class for sorting by index. + */ +class CompareIndices +{ +public: + int operator() (const SGPropertyNode * n1, const SGPropertyNode *n2) const { + return (n1->getIndex() < n2->getIndex()); + } +}; + + //////////////////////////////////////////////////////////////////////// // Convenience macros for value access. @@ -32,12 +45,14 @@ FG_USING_STD(sort); #define GET_BOOL (_value.bool_val->getValue()) #define GET_INT (_value.int_val->getValue()) +#define GET_LONG (_value.long_val->getValue()) #define GET_FLOAT (_value.float_val->getValue()) #define GET_DOUBLE (_value.double_val->getValue()) #define GET_STRING (_value.string_val->getValue()) #define SET_BOOL(val) (_value.bool_val->setValue(val)) #define SET_INT(val) (_value.int_val->setValue(val)) +#define SET_LONG(val) (_value.long_val->setValue(val)) #define SET_FLOAT(val) (_value.float_val->setValue(val)) #define SET_DOUBLE(val) (_value.double_val->setValue(val)) #define SET_STRING(val) (_value.string_val->setValue(val)) @@ -50,6 +65,7 @@ FG_USING_STD(sort); const bool SGRawValue::DefaultValue = false; const int SGRawValue::DefaultValue = 0; +const long SGRawValue::DefaultValue = 0L; const float SGRawValue::DefaultValue = 0.0; const double SGRawValue::DefaultValue = 0.0L; const string SGRawValue::DefaultValue = ""; @@ -227,22 +243,27 @@ find_node (SGPropertyNode * current, int position, bool create) { + // Run off the end of the list if (current == 0) { return 0; } + // Success! This is the one we want. else if (position >= components.size()) { return current; } + // Empty component means root. else if (components[position].name == "") { return find_node(current->getRootNode(), components, position + 1, create); } + // . means current directory else if (components[position].name == ".") { return find_node(current, components, position + 1, create); } + // .. means parent directory else if (components[position].name == "..") { SGPropertyNode * parent = current->getParent(); if (parent == 0) @@ -251,6 +272,7 @@ find_node (SGPropertyNode * current, return find_node(parent, components, position + 1, create); } + // Otherwise, a child name else { SGPropertyNode * child = current->getChild(components[position].name, @@ -287,12 +309,19 @@ SGValue::SGValue (const SGValue &source) _type = source._type; _tied = source._tied; switch (source._type) { + case ALIAS: + // FIXME!!! + _value.alias = (SGValue *)(source.getAlias()); + break; case BOOL: _value.bool_val = source._value.bool_val->clone(); break; case INT: _value.int_val = source._value.int_val->clone(); break; + case LONG: + _value.long_val = source._value.long_val->clone(); + break; case FLOAT: _value.float_val = source._value.float_val->clone(); break; @@ -312,7 +341,8 @@ SGValue::SGValue (const SGValue &source) */ SGValue::~SGValue () { - clear_value(); + if (_type != ALIAS) + clear_value(); } @@ -323,6 +353,8 @@ void SGValue::clear_value () { switch (_type) { + case ALIAS: + _value.alias->clear_value(); case BOOL: delete _value.bool_val; _value.bool_val = 0; @@ -331,6 +363,10 @@ SGValue::clear_value () delete _value.int_val; _value.int_val = 0; break; + case LONG: + delete _value.long_val; + _value.long_val = 0L; + break; case FLOAT: delete _value.float_val; _value.float_val = 0; @@ -348,6 +384,72 @@ SGValue::clear_value () } +/** + * Get the current type. + * + * Does not return a type of ALIAS. + */ +SGValue::Type +SGValue::getType () const +{ + if (_type == ALIAS) + return _value.alias->getType(); + else + return (Type)_type; +} + + +/** + * Get the current aliased value. + */ +SGValue * +SGValue::getAlias () +{ + return (_type == ALIAS ? _value.alias : 0); +} + + +/** + * Get the current aliased value. + */ +const SGValue * +SGValue::getAlias () const +{ + return (_type == ALIAS ? _value.alias : 0); +} + + +/** + * Alias to another value. + */ +bool +SGValue::alias (SGValue * alias) +{ + if (alias == 0 || _type == ALIAS || _tied) + return false; + clear_value(); + _value.alias = alias; + _type = ALIAS; + return true; +} + + +/** + * Unalias from another value. + */ +bool +SGValue::unalias () +{ + // FIXME: keep copy of previous value, + // as with untie() + if (_type != ALIAS) + return false; + _value.string_val = new SGRawValueInternal; + _type = UNKNOWN; + return true; +} + + /** * Get a boolean value. */ @@ -355,10 +457,14 @@ bool SGValue::getBoolValue () const { switch (_type) { + case ALIAS: + return _value.alias->getBoolValue(); case BOOL: return GET_BOOL; case INT: return GET_INT == 0 ? false : true; + case LONG: + return GET_LONG == 0L ? false : true; case FLOAT: return GET_FLOAT == 0.0 ? false : true; case DOUBLE: @@ -377,14 +483,18 @@ int SGValue::getIntValue () const { switch (_type) { + case ALIAS: + return _value.alias->getIntValue(); case BOOL: - return (int)GET_BOOL; + return int(GET_BOOL); case INT: return GET_INT; + case LONG: + return int(GET_LONG); case FLOAT: - return (int)GET_FLOAT; + return int(GET_FLOAT); case DOUBLE: - return (int)GET_DOUBLE; + return int(GET_DOUBLE); case STRING: case UNKNOWN: return atoi(GET_STRING.c_str()); @@ -392,6 +502,32 @@ SGValue::getIntValue () const } +/** + * Get a long integer value. + */ +long +SGValue::getLongValue () const +{ + switch (_type) { + case ALIAS: + return _value.alias->getLongValue(); + case BOOL: + return long(GET_BOOL); + case INT: + return long(GET_INT); + case LONG: + return GET_LONG; + case FLOAT: + return long(GET_FLOAT); + case DOUBLE: + return long(GET_DOUBLE); + case STRING: + case UNKNOWN: + return strtol(GET_STRING.c_str(), 0, 0); + } +} + + /** * Get a float value. */ @@ -399,14 +535,18 @@ float SGValue::getFloatValue () const { switch (_type) { + case ALIAS: + return _value.alias->getFloatValue(); case BOOL: - return (float)GET_BOOL; + return float(GET_BOOL); case INT: - return (float)GET_INT; + return float(GET_INT); + case LONG: + return float(GET_LONG); case FLOAT: return GET_FLOAT; case DOUBLE: - return GET_DOUBLE; + return float(GET_DOUBLE); case STRING: case UNKNOWN: return atof(GET_STRING.c_str()); @@ -421,17 +561,21 @@ double SGValue::getDoubleValue () const { switch (_type) { + case ALIAS: + return _value.alias->getDoubleValue(); case BOOL: - return (double)GET_BOOL; + return double(GET_BOOL); case INT: - return (double)GET_INT; + return double(GET_INT); + case LONG: + return double(GET_LONG); case FLOAT: - return (double)GET_FLOAT; + return double(GET_FLOAT); case DOUBLE: return GET_DOUBLE; case STRING: case UNKNOWN: - return (double)atof(GET_STRING.c_str()); + return strtod(GET_STRING.c_str(), 0); } } @@ -445,6 +589,8 @@ SGValue::getStringValue () const char buf[128]; switch (_type) { + case ALIAS: + return _value.alias->getStringValue(); case BOOL: if (GET_BOOL) return "true"; @@ -453,6 +599,9 @@ SGValue::getStringValue () const case INT: sprintf(buf, "%d", GET_INT); return buf; + case LONG: + sprintf(buf, "%ld", GET_LONG); + return buf; case FLOAT: sprintf(buf, "%f", GET_FLOAT); return buf; @@ -479,14 +628,18 @@ SGValue::setBoolValue (bool value) } switch (_type) { + case ALIAS: + return _value.alias->setBoolValue(value); case BOOL: return SET_BOOL(value); case INT: - return SET_INT((int)value); + return SET_INT(int(value)); + case LONG: + return SET_LONG(long(value)); case FLOAT: - return SET_FLOAT((float)value); + return SET_FLOAT(float(value)); case DOUBLE: - return SET_DOUBLE((double)value); + return SET_DOUBLE(double(value)); case STRING: return SET_STRING(value ? "true" : "false"); } @@ -508,14 +661,54 @@ SGValue::setIntValue (int value) } switch (_type) { + case ALIAS: + return _value.alias->setIntValue(value); case BOOL: return SET_BOOL(value == 0 ? false : true); case INT: return SET_INT(value); + case LONG: + return SET_LONG(long(value)); case FLOAT: - return SET_FLOAT((float)value); + return SET_FLOAT(float(value)); case DOUBLE: - return SET_DOUBLE((double)value); + return SET_DOUBLE(double(value)); + case STRING: { + char buf[128]; + sprintf(buf, "%d", value); + return SET_STRING(buf); + } + } + + return false; +} + + +/** + * Set a long value. + */ +bool +SGValue::setLongValue (long value) +{ + if (_type == UNKNOWN) { + clear_value(); + _value.long_val = new SGRawValueInternal; + _type = LONG; + } + + switch (_type) { + case ALIAS: + return _value.alias->setLongValue(value); + case BOOL: + return SET_BOOL(value == 0L ? false : true); + case INT: + return SET_INT(int(value)); + case LONG: + return SET_LONG(value); + case FLOAT: + return SET_FLOAT(float(value)); + case DOUBLE: + return SET_DOUBLE(double(value)); case STRING: { char buf[128]; sprintf(buf, "%d", value); @@ -540,14 +733,18 @@ SGValue::setFloatValue (float value) } switch (_type) { + case ALIAS: + return _value.alias->setFloatValue(value); case BOOL: return SET_BOOL(value == 0.0 ? false : true); case INT: - return SET_INT((int)value); + return SET_INT(int(value)); + case LONG: + return SET_LONG(long(value)); case FLOAT: return SET_FLOAT(value); case DOUBLE: - return SET_DOUBLE((double)value); + return SET_DOUBLE(double(value)); case STRING: { char buf[128]; sprintf(buf, "%f", value); @@ -572,12 +769,16 @@ SGValue::setDoubleValue (double value) } switch (_type) { + case ALIAS: + return _value.alias->setDoubleValue(value); case BOOL: return SET_BOOL(value == 0.0L ? false : true); case INT: - return SET_INT((int)value); + return SET_INT(int(value)); + case LONG: + return SET_LONG(long(value)); case FLOAT: - return SET_FLOAT((float)value); + return SET_FLOAT(float(value)); case DOUBLE: return SET_DOUBLE(value); case STRING: { @@ -604,14 +805,18 @@ SGValue::setStringValue (string value) } switch (_type) { + case ALIAS: + return _value.alias->setStringValue(value); case BOOL: return SET_BOOL((value == "true" || atoi(value.c_str())) ? true : false); case INT: return SET_INT(atoi(value.c_str())); + case LONG: + return SET_LONG(strtol(value.c_str(), 0, 0)); case FLOAT: return SET_FLOAT(atof(value.c_str())); case DOUBLE: - return SET_DOUBLE((double)atof(value.c_str())); + return SET_DOUBLE(strtod(value.c_str(), 0)); case STRING: return SET_STRING(value); } @@ -627,14 +832,18 @@ bool SGValue::setUnknownValue (string value) { switch (_type) { + case ALIAS: + return _value.alias->setUnknownValue(value); case BOOL: return SET_BOOL((value == "true" || atoi(value.c_str())) ? true : false); case INT: return SET_INT(atoi(value.c_str())); + case LONG: + return SET_LONG(strtol(value.c_str(), 0, 0)); case FLOAT: return SET_FLOAT(atof(value.c_str())); case DOUBLE: - return SET_DOUBLE((double)atof(value.c_str())); + return SET_DOUBLE(strtod(value.c_str(), 0)); case STRING: case UNKNOWN: return SET_STRING(value); @@ -650,7 +859,9 @@ SGValue::setUnknownValue (string value) bool SGValue::tie (const SGRawValue &value, bool use_default) { - if (_tied) + if (_type == ALIAS) + return _value.alias->tie(value, use_default); + else if (_tied) return false; bool old_val; @@ -675,7 +886,9 @@ SGValue::tie (const SGRawValue &value, bool use_default) bool SGValue::tie (const SGRawValue &value, bool use_default) { - if (_tied) + if (_type == ALIAS) + return _value.alias->tie(value, use_default); + else if (_tied) return false; int old_val; @@ -694,13 +907,42 @@ SGValue::tie (const SGRawValue &value, bool use_default) } +/** + * Tie a long value. + */ +bool +SGValue::tie (const SGRawValue &value, bool use_default) +{ + if (_type == ALIAS) + return _value.alias->tie(value, use_default); + else if (_tied) + return false; + + long old_val; + if (use_default) + old_val = getLongValue(); + + clear_value(); + _type = LONG; + _tied = true; + _value.long_val = value.clone(); + + if (use_default) + setLongValue(old_val); + + return true; +} + + /** * Tie a float value. */ bool SGValue::tie (const SGRawValue &value, bool use_default) { - if (_tied) + if (_type == ALIAS) + return _value.alias->tie(value, use_default); + else if (_tied) return false; float old_val; @@ -725,7 +967,9 @@ SGValue::tie (const SGRawValue &value, bool use_default) bool SGValue::tie (const SGRawValue &value, bool use_default) { - if (_tied) + if (_type == ALIAS) + return _value.alias->tie(value, use_default); + else if (_tied) return false; double old_val; @@ -750,7 +994,9 @@ SGValue::tie (const SGRawValue &value, bool use_default) bool SGValue::tie (const SGRawValue &value, bool use_default) { - if (_tied) + if (_type == ALIAS) + return _value.alias->tie(value, use_default); + else if (_tied) return false; string old_val; @@ -779,6 +1025,9 @@ SGValue::untie () return false; switch (_type) { + case ALIAS: { + return _value.alias->untie(); + } case BOOL: { bool val = getBoolValue(); clear_value(); @@ -793,6 +1042,13 @@ SGValue::untie () SET_INT(val); break; } + case LONG: { + long val = getLongValue(); + clear_value(); + _value.long_val = new SGRawValueInternal; + SET_LONG(val); + break; + } case FLOAT: { float val = getFloatValue(); clear_value(); @@ -827,11 +1083,29 @@ SGValue::untie () //////////////////////////////////////////////////////////////////////// +/** + * Utility function: given a value, find the property node. + */ +static SGPropertyNode * +find_node_by_value (SGPropertyNode * start_node, const SGValue * value) +{ + if (start_node->getValue() == value) { + return start_node; + } else for (int i = 0; i < start_node->nChildren(); i++) { + SGPropertyNode * child = + find_node_by_value(start_node->getChild(i), value); + if (child != 0) + return child; + } + return 0; +} + + /** * Default constructor: always creates a root node. */ SGPropertyNode::SGPropertyNode () - : _value(0), _name(""), _index(0), _parent(0) + : _value(0), _name(""), _index(0), _parent(0), _target(0) { } @@ -841,10 +1115,14 @@ SGPropertyNode::SGPropertyNode () */ SGPropertyNode::SGPropertyNode (const string &name, int index, SGPropertyNode * parent) - : _value(0), _name(name), _index(index), _parent(parent) + : _value(0), _name(name), _index(index), _parent(parent), _target(0) { } + +/** + * Destructor. + */ SGPropertyNode::~SGPropertyNode () { delete _value; @@ -852,6 +1130,104 @@ SGPropertyNode::~SGPropertyNode () delete _children[i]; } + +/** + * Get a value, optionally creating it if not present. + */ +SGValue * +SGPropertyNode::getValue (bool create) +{ + if (_value == 0 && create) + _value = new SGValue(); + return _value; +} + + +/** + * Alias to another node. + */ +bool +SGPropertyNode::alias (SGPropertyNode * target) +{ + if (_value == 0) + _value = new SGValue(); + _target = target; + return _value->alias(target->getValue(true)); +} + + +/** + * Alias to another node by path. + */ +bool +SGPropertyNode::alias (const string &path) +{ + return alias(getNode(path, true)); +} + + +/** + * Remove an alias. + */ +bool +SGPropertyNode::unalias () +{ + _target = 0; + return (_value != 0 ? _value->unalias() : false); +} + + +/** + * Test whether this node is aliased. + */ +bool +SGPropertyNode::isAlias () const +{ + return (_value != 0 ? _value->isAlias() : false); +} + + +/** + * Get the target of an alias. + * + * This is tricky, because it is actually the value that is aliased, + * and someone could realias or unalias the value directly without + * going through the property node. The node caches its best guess, + * but it may have to search the whole property tree. + * + * @return The target node for the alias, or 0 if the node is not aliased. + */ +SGPropertyNode * +SGPropertyNode::getAliasTarget () +{ + if (_value == 0 || !_value->isAlias()) { + return 0; + } else if (_target != 0 && _target->getValue() == _value->getAlias()) { + return _target; + } else { + _target = find_node_by_value(getRootNode(), _value->getAlias()); + } +} + + +const SGPropertyNode * +SGPropertyNode::getAliasTarget () const +{ + if (_value == 0 || !_value->isAlias()) { + return 0; + } else if (_target != 0 && _target->getValue() == _value->getAlias()) { + return _target; + } else { + // FIXME: const cast + _target = + find_node_by_value((SGPropertyNode *)getRootNode(), _value->getAlias()); + } +} + + +/** + * Get a non-const child by index. + */ SGPropertyNode * SGPropertyNode::getChild (int position) { @@ -861,6 +1237,10 @@ SGPropertyNode::getChild (int position) return 0; } + +/** + * Get a const child by index. + */ const SGPropertyNode * SGPropertyNode::getChild (int position) const { @@ -870,12 +1250,16 @@ SGPropertyNode::getChild (int position) const return 0; } + +/** + * Get a non-const child by name and index, creating if necessary. + */ SGPropertyNode * SGPropertyNode::getChild (const string &name, int index, bool create) { int pos = find_child(name, index, _children); if (pos >= 0) { - return getChild(pos); + return _children[pos]; } else if (create) { _children.push_back(new SGPropertyNode(name, index, this)); return _children[_children.size()-1]; @@ -884,26 +1268,21 @@ SGPropertyNode::getChild (const string &name, int index, bool create) } } + +/** + * Get a const child by name and index. + */ const SGPropertyNode * SGPropertyNode::getChild (const string &name, int index) const { int pos = find_child(name, index, _children); if (pos >= 0) - _children[_children.size()-1]; + return _children[pos]; else return 0; } -class CompareIndices -{ -public: - int operator() (const SGPropertyNode * n1, const SGPropertyNode *n2) const { - return (n1->getIndex() < n2->getIndex()); - } -}; - - /** * Get all children with the same name (but different indices). */ @@ -923,7 +1302,7 @@ SGPropertyNode::getChildren (const string &name) /** - * Get all children with the same name (but different indices). + * Get all children const with the same name (but different indices). */ vector SGPropertyNode::getChildren (const string &name) const @@ -966,6 +1345,7 @@ SGPropertyNode::getType () const return SGValue::UNKNOWN; } + bool SGPropertyNode::getBoolValue () const { @@ -980,6 +1360,13 @@ SGPropertyNode::getIntValue () const : _value->getIntValue()); } +long +SGPropertyNode::getLongValue () const +{ + return (_value == 0 ? SGRawValue::DefaultValue + : _value->getLongValue()); +} + float SGPropertyNode::getFloatValue () const { @@ -1017,6 +1404,14 @@ SGPropertyNode::setIntValue (int val) return _value->setIntValue(val); } +bool +SGPropertyNode::setLongValue (long val) +{ + if (_value == 0) + _value = new SGValue(); + return _value->setLongValue(val); +} + bool SGPropertyNode::setFloatValue (float val) { @@ -1071,6 +1466,14 @@ SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) return _value->tie(rawValue, useDefault); } +bool +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) +{ + if (_value == 0) + _value = new SGValue(); + return _value->tie(rawValue, useDefault); +} + bool SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) { @@ -1163,7 +1566,7 @@ SGPropertyNode::getValue (const string &relative_path, bool create) SGPropertyNode * node = getNode(relative_path, create); if (node != 0 && !node->hasValue()) node->setUnknownValue(""); - return (node == 0 ? 0 : node->getValue()); + return (node == 0 ? 0 : node->getValue(create)); } @@ -1213,6 +1616,18 @@ SGPropertyNode::getIntValue (const string &relative_path, } +/** + * Get a long value for another node. + */ +long +SGPropertyNode::getLongValue (const string &relative_path, + long defaultValue) const +{ + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getLongValue()); +} + + /** * Get a float value for another node. */ @@ -1269,6 +1684,16 @@ SGPropertyNode::setIntValue (const string &relative_path, int value) } +/** + * Set a long value for another node. + */ +bool +SGPropertyNode::setLongValue (const string &relative_path, long value) +{ + return getNode(relative_path, true)->setLongValue(value); +} + + /** * Set a float value for another node. */ @@ -1344,6 +1769,18 @@ SGPropertyNode::tie (const string &relative_path, } +/** + * Tie a node reached by a relative path, creating it if necessary. + */ +bool +SGPropertyNode::tie (const string &relative_path, + const SGRawValue &rawValue, + bool useDefault) +{ + return getNode(relative_path, true)->tie(rawValue, useDefault); +} + + /** * Tie a node reached by a relative path, creating it if necessary. */ diff --git a/simgear/misc/props.hxx b/simgear/misc/props.hxx index d6809970..f1e23464 100644 --- a/simgear/misc/props.hxx +++ b/simgear/misc/props.hxx @@ -28,6 +28,11 @@ FG_USING_STD(istream); FG_USING_STD(ostream); #endif +#ifdef ALIAS +#pragma warn A sloppy coder has defined ALIAS as a macro! +#undef ALIAS +#endif + #ifdef UNKNOWN #pragma warn A sloppy coder has defined UNKNOWN as a macro! #undef UNKNOWN @@ -43,6 +48,11 @@ FG_USING_STD(ostream); #undef INT #endif +#ifdef LONG +#pragma warn A sloppy coder has defined LONG as a macro! +#undef LONG +#endif + #ifdef FLOAT #pragma warn A sloppy coder has defined FLOAT as a macro! #undef FLOAT @@ -294,6 +304,7 @@ public: enum Type { BOOL, INT, + LONG, FLOAT, DOUBLE, STRING, @@ -303,16 +314,24 @@ public: SGValue (const SGValue &value); ~SGValue (); - Type getType () const { return _type; } + Type getType () const; + + SGValue * getAlias (); + const SGValue * getAlias () const; + bool alias (SGValue * alias); + bool unalias (); + bool isAlias () const { return _type == ALIAS; } bool getBoolValue () const; int getIntValue () const; + long getLongValue () const; float getFloatValue () const; double getDoubleValue () const; string getStringValue () const; bool setBoolValue (bool value); bool setIntValue (int value); + bool setLongValue (long value); bool setFloatValue (float value); bool setDoubleValue (double value); bool setStringValue (string value); @@ -322,6 +341,7 @@ public: bool tie (const SGRawValue &rawValue, bool useDefault = true); bool tie (const SGRawValue &rawValue, bool useDefault = true); + bool tie (const SGRawValue &rawValue, bool useDefault = true); bool tie (const SGRawValue &rawValue, bool useDefault = true); bool tie (const SGRawValue &rawValue, bool useDefault = true); bool tie (const SGRawValue &rawValue, bool useDefault = true); @@ -330,15 +350,21 @@ public: private: + enum { + ALIAS = -1 + }; + void clear_value (); - Type _type; + int _type; bool _tied; // The right kind of pointer... union { + SGValue * alias; SGRawValue * bool_val; SGRawValue * int_val; + SGRawValue * long_val; SGRawValue * float_val; SGRawValue * double_val; SGRawValue * string_val; @@ -358,17 +384,26 @@ class SGPropertyNode public: SGPropertyNode (); - virtual ~SGPropertyNode (); + virtual ~SGPropertyNode (); // Basic properties. bool hasValue () const { return (_value != 0); } SGValue * getValue () { return _value; } + SGValue * getValue (bool create); const SGValue * getValue () const { return _value; } const string &getName () const { return _name; } const int getIndex () const { return _index; } SGPropertyNode * getParent () { return _parent; } const SGPropertyNode * getParent () const { return _parent; } + // Alias support. + bool alias (SGPropertyNode * target); + bool alias (const string &path); + bool unalias (); + bool isAlias () const; + SGPropertyNode * getAliasTarget (); + const SGPropertyNode * getAliasTarget () const; + // Children. const int nChildren () const { return _children.size(); } SGPropertyNode * getChild (int position); @@ -394,12 +429,14 @@ public: bool getBoolValue () const; int getIntValue () const; + long getLongValue () const; float getFloatValue () const; double getDoubleValue () const; string getStringValue () const; bool setBoolValue (bool value); bool setIntValue (int value); + bool setLongValue (long value); bool setFloatValue (float value); bool setDoubleValue (double value); bool setStringValue (string value); @@ -409,6 +446,7 @@ public: bool tie (const SGRawValue &rawValue, bool useDefault = true); bool tie (const SGRawValue &rawValue, bool useDefault = true); + bool tie (const SGRawValue &rawValue, bool useDefault = true); bool tie (const SGRawValue &rawValue, bool useDefault = true); bool tie (const SGRawValue &rawValue, bool useDefault = true); bool tie (const SGRawValue &rawValue, bool useDefault = true); @@ -426,6 +464,8 @@ public: bool defaultValue = false) const; int getIntValue (const string &relative_path, int defaultValue = 0) const; + long getLongValue (const string &relative_path, + long defaultValue = 0L) const; float getFloatValue (const string &relative_path, float defaultValue = 0.0) const; double getDoubleValue (const string &relative_path, @@ -435,6 +475,7 @@ public: bool setBoolValue (const string &relative_path, bool value); bool setIntValue (const string &relative_path, int value); + bool setLongValue (const string &relative_path, long value); bool setFloatValue (const string &relative_path, float value); bool setDoubleValue (const string &relative_path, double value); bool setStringValue (const string &relative_path, string value); @@ -446,6 +487,8 @@ public: bool useDefault = true); bool tie (const string &relative_path, const SGRawValue &rawValue, bool useDefault = true); + bool tie (const string &relative_path, const SGRawValue &rawValue, + bool useDefault = true); bool tie (const string &relative_path, const SGRawValue &rawValue, bool useDefault = true); bool tie (const string &relative_path, const SGRawValue &rawValue, @@ -468,6 +511,7 @@ private: int _index; SGPropertyNode * _parent; vector _children; + mutable SGPropertyNode * _target; }; @@ -477,7 +521,8 @@ private: // I/O functions. //////////////////////////////////////////////////////////////////////// -bool readProperties (istream &input, SGPropertyNode * start_node); +bool readProperties (istream &input, SGPropertyNode * start_node, + const string &base = ""); bool readProperties (const string &file, SGPropertyNode * start_node); bool writeProperties (ostream &output, const SGPropertyNode * start_node); bool writeProperties (const string &file, const SGPropertyNode * start_node); diff --git a/simgear/misc/props_io.cxx b/simgear/misc/props_io.cxx index b9ecabfc..1951c229 100644 --- a/simgear/misc/props_io.cxx +++ b/simgear/misc/props_io.cxx @@ -10,6 +10,7 @@ #include #include +#include "fgpath.hxx" #include "props.hxx" #include STL_IOSTREAM @@ -42,7 +43,8 @@ class PropsVisitor : public XMLVisitor { public: - PropsVisitor (SGPropertyNode * root) : _ok(true), _root(root), _level(0) {} + PropsVisitor (SGPropertyNode * root, const string &base) + : _ok(true), _root(root), _level(0), _base(base) {} void startXML (); void endXML (); @@ -87,7 +89,7 @@ private: SGPropertyNode * _root; int _level; vector _state_stack; - + string _base; }; void @@ -110,10 +112,16 @@ PropsVisitor::startElement (const char * name, const XMLAttributes &atts) State &st = state(); if (_level == 0) { + if (string(name) != "PropertyList") { + FG_LOG(FG_INPUT, FG_ALERT, "Root element name is " << + name << "; expected PropertyList"); + _ok = false; + } push_state(_root, ""); } else { + // Get the index. const char * att_n = atts.getValue("n"); int index = 0; if (att_n != 0) { @@ -123,8 +131,30 @@ PropsVisitor::startElement (const char * name, const XMLAttributes &atts) index = st.counters[name]; st.counters[name]++; } - push_state(st.node->getChild(name, index, true), - atts.getValue("type")); + + // Check for an alias. + SGPropertyNode * node = st.node->getChild(name, index, true); + const char * att_alias = atts.getValue("alias"); + if (att_alias != 0) { + if (!node->alias(att_alias)) + FG_LOG(FG_INPUT, FG_ALERT, "Failed to set alias to " << att_alias); + } + + // Check for an include. + const char * att_include = atts.getValue("include"); + if (att_include != 0) { + FGPath path(FGPath(_base).dir()); + cerr << "Base is " << _base << endl; + cerr << "Dir is " << FGPath(_base).dir() << endl; + path.append(att_include); + if (!readProperties(path.str(), node)) { + FG_LOG(FG_INPUT, FG_ALERT, "Failed to read include file " + << att_include); + _ok = false; + } + } + + push_state(node, atts.getValue("type")); } } @@ -134,9 +164,9 @@ PropsVisitor::endElement (const char * name) State &st = state(); bool ret; - // If there are no children, then - // it is a leaf value. - if (st.node->nChildren() == 0) { + // If there are no children and it's + // not an alias, then it's a leaf value. + if (st.node->nChildren() == 0 && !st.node->isAlias()) { if (st.type == "bool") { if (_data == "true" || atoi(_data.c_str()) != 0) ret = st.node->setBoolValue(true); @@ -183,8 +213,8 @@ PropsVisitor::warning (const char * message, int line, int column) void PropsVisitor::error (const char * message, int line, int column) { - FG_LOG(FG_INPUT, FG_ALERT, "readProperties: FATAL: " - << message << " at line " << line << ", column " << column); + FG_LOG(FG_INPUT, FG_ALERT, "readProperties: FATAL: " << + message << " at line " << line << ", column " << column); _ok = false; } @@ -194,19 +224,38 @@ PropsVisitor::error (const char * message, int line, int column) // Property list reader. //////////////////////////////////////////////////////////////////////// + +/** + * Read properties from an input stream. + * + * @param input The input stream containing an XML property file. + * @param start_node The root node for reading properties. + * @param base A base path for resolving external include references. + * @return true if the read succeeded, false otherwise. + */ bool -readProperties (istream &input, SGPropertyNode * start_node) +readProperties (istream &input, SGPropertyNode * start_node, + const string &base) { - PropsVisitor visitor(start_node); + PropsVisitor visitor(start_node, base); return readXML(input, visitor) && visitor.isOK(); } + +/** + * Read properties from a file. + * + * @param file A string containing the file path. + * @param start_node The root node for reading properties. + * @return true if the read succeeded, false otherwise. + */ bool readProperties (const string &file, SGPropertyNode * start_node) { + cerr << "Reading properties from " << file << endl; ifstream input(file.c_str()); if (input.good()) { - return readProperties(input, start_node); + return readProperties(input, start_node, file); } else { FG_LOG(FG_INPUT, FG_ALERT, "Error reading property list from file " << file); @@ -292,10 +341,17 @@ writeNode (ostream &output, const SGPropertyNode * node, int indent) // write it first. if (node->hasValue()) { doIndent(output, indent); - output << '<' << name << " n=\"" << index - << "\" type=\"" << getTypeName(node->getType()) << "\">"; - writeData(output, node->getStringValue()); - output << "' << endl;; + output << '<' << name << " n=\"" << index; + if (node->isAlias() && node->getAliasTarget() != 0) { + output << "\" alias=\"" + << node->getAliasTarget()->getPath() << "\"/>" << endl; + } else { + if (node->getType() != SGValue::UNKNOWN) + output << "\" type=\"" << getTypeName(node->getType()) << '"'; + output << '>'; + writeData(output, node->getStringValue()); + output << "' << endl;; + } } // If there are children, write them @@ -320,6 +376,14 @@ writeNode (ostream &output, const SGPropertyNode * node, int indent) return true; } + +/** + * Write a property tree to an output stream in XML format. + * + * @param output The output stream. + * @param start_node The root node to write. + * @return true if the write succeeded, false otherwise. + */ bool writeProperties (ostream &output, const SGPropertyNode * start_node) { @@ -337,6 +401,14 @@ writeProperties (ostream &output, const SGPropertyNode * start_node) return true; } + +/** + * Write a property tree to a file in XML format. + * + * @param file The destination file. + * @param start_node The root node to write. + * @return true if the write succeeded, false otherwise. + */ bool writeProperties (const string &file, const SGPropertyNode * start_node) { @@ -351,8 +423,19 @@ writeProperties (const string &file, const SGPropertyNode * start_node) } + +//////////////////////////////////////////////////////////////////////// +// Copy properties from one tree to another. +//////////////////////////////////////////////////////////////////////// + + /** - * Copy one property list to another. + * Copy one property tree to another. + * + * @param in The source property tree. + * @param out The destination property tree. + * @return true if all properties were copied, false if some failed + * (for example, if the property's value is tied read-only). */ bool copyProperties (const SGPropertyNode *in, SGPropertyNode *out) @@ -388,7 +471,7 @@ copyProperties (const SGPropertyNode *in, SGPropertyNode *out) retval = false; break; default: - throw string("Unknown SGValue type"); // FIXME!!! + throw string("Unknown SGValue type"); } } diff --git a/simgear/misc/props_test.cxx b/simgear/misc/props_test.cxx index 3fa2333b..9a0487cc 100644 --- a/simgear/misc/props_test.cxx +++ b/simgear/misc/props_test.cxx @@ -317,7 +317,7 @@ test_property_nodes () cout << "Looking for all /hack[0]/bar children" << endl; vector bar = child->getChildren("bar"); cout << "There are " << bar.size() << " matches" << endl; - for (int i = 0; i < bar.size(); i++) + for (int i = 0; i < (int)bar.size(); i++) cout << bar[i]->getName() << '[' << bar[i]->getIndex() << ']' << endl; cout << endl; diff --git a/simgear/xml/easyxml.cxx b/simgear/xml/easyxml.cxx index 83e0735a..ab97d218 100644 --- a/simgear/xml/easyxml.cxx +++ b/simgear/xml/easyxml.cxx @@ -228,6 +228,10 @@ readXML (istream &input, XMLVisitor &visitor) } } + // Verify end of document. + if (!XML_Parse(parser, buf, 0, true)) + retval = false; + if (retval) visitor.endXML(); -- 2.39.5