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