X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fmisc%2Fprops.cxx;h=83a2ef320b3a28b509d5f0dba144e3f408f906ed;hb=2d1b2ca93836bbb6498e67445dfbf5d5a3593331;hp=73a8b5e4c540f0cb04a530923db1fc80d85e7f53;hpb=93c6845a6d54b1939f83bfef09188ad98ca3b901;p=simgear.git diff --git a/simgear/misc/props.cxx b/simgear/misc/props.cxx index 73a8b5e4..83a2ef32 100644 --- a/simgear/misc/props.cxx +++ b/simgear/misc/props.cxx @@ -1,1200 +1,1626 @@ -// props.cxx -- implementation of SimGear Property Manager. -// -// Written by David Megginson - david@megginson.com -// -// This module is in the PUBLIC DOMAIN. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// props.cxx - implementation of a property list. +// Started Fall 2000 by David Megginson, david@megginson.com +// This code is released into the Public Domain. // // See props.html for documentation [replace with URL when available]. // // $Id$ -#ifdef HAVE_CONFIG_H -# include -#endif - +#include #include +#include +#include +#include STL_IOSTREAM +#include #include "props.hxx" -#include +SG_USING_STD(sort); + -#include + +//////////////////////////////////////////////////////////////////////// +// Local classes. +//////////////////////////////////////////////////////////////////////// -using std::string; +/** + * Comparator class for sorting by index. + */ +class CompareIndices +{ +public: + int operator() (const SGPropertyNode * n1, const SGPropertyNode *n2) const { + return (n1->getIndex() < n2->getIndex()); + } +}; -FGPropertyList current_properties; -static string empty_string; + +//////////////////////////////////////////////////////////////////////// +// Convenience macros for value access. +//////////////////////////////////////////////////////////////////////// + +#define TEST_READ(dflt) if (!getAttribute(READ)) return dflt +#define TEST_WRITE if (!getAttribute(WRITE)) return false + +#define DO_TRACE_READ(type) if(getAttribute(TRACE_READ)) trace_read(type) +#define DO_TRACE_WRITE(type) if (getAttribute(TRACE_WRITE)) trace_write(type) + +#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)) + + + +//////////////////////////////////////////////////////////////////////// +// Default values for every type. +//////////////////////////////////////////////////////////////////////// + +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 = ""; //////////////////////////////////////////////////////////////////////// -// Implementation of FGValue. +// Local path normalization code. //////////////////////////////////////////////////////////////////////// +/** + * A component in a path. + */ +struct PathComponent +{ + string name; + int index; +}; /** - * Construct a new value. + * Parse the name for a path component. + * + * Name: [_a-zA-Z][-._a-zA-Z0-9]* */ -FGValue::FGValue () - : _type(UNKNOWN), _tied(false) +static inline string +parse_name (const string &path, int &i) { + string name = ""; + int max = path.size(); + + if (path[i] == '.') { + i++; + if (i < max && path[i] == '.') { + i++; + name = ".."; + } else { + name = "."; + } + if (i < max && path[i] != '/') + throw string(string("Illegal character after ") + name); + } + + else if (isalpha(path[i]) || path[i] == '_') { + name += path[i]; + i++; + + // The rules inside a name are a little + // less restrictive. + while (i < max) { + if (isalpha(path[i]) || isdigit(path[i]) || path[i] == '_' || + path[i] == '-' || path[i] == '.') { + name += path[i]; + } else if (path[i] == '[' || path[i] == '/') { + break; + } else { + throw string("name may contain only ._- and alphanumeric characters"); + } + i++; + } + } + + else { + if (name.size() == 0) + throw string("name must begin with alpha or '_'"); + } + + return name; } /** - * Destroy a value. + * Parse the optional integer index for a path component. + * + * Index: "[" [0-9]+ "]" */ -FGValue::~FGValue () +static inline int +parse_index (const string &path, int &i) { + int index = 0; + + if (path[i] != '[') + return 0; + else + i++; + + for (int max = path.size(); i < max; i++) { + if (isdigit(path[i])) { + index = (index * 10) + (path[i] - '0'); + } else if (path[i] == ']') { + i++; + return index; + } else { + break; + } + } + + throw string("unterminated index (looking for ']')"); } /** - * Return a raw boolean value (no type coercion). + * Parse a single path component. + * + * Component: Name Index? */ -bool -FGValue::getRawBool () const +static inline PathComponent +parse_component (const string &path, int &i) { - if (_tied) { - if (_value.bool_func.getter != 0) - return (*(_value.bool_func.getter))(); - else - return false; - } else { - return _value.bool_val; + PathComponent component; + component.name = parse_name(path, i); + if (component.name[0] != '.') + component.index = parse_index(path, i); + else + component.index = -1; + return component; +} + + +/** + * Parse a path into its components. + */ +static void +parse_path (const string &path, vector &components) +{ + int pos = 0; + int max = path.size(); + + // Check for initial '/' + if (path[pos] == '/') { + PathComponent root; + root.name = ""; + root.index = -1; + components.push_back(root); + pos++; + while (pos < max && path[pos] == '/') + pos++; + } + + while (pos < max) { + components.push_back(parse_component(path, pos)); + while (pos < max && path[pos] == '/') + pos++; } } + +//////////////////////////////////////////////////////////////////////// +// Other static utility functions. +//////////////////////////////////////////////////////////////////////// + + /** - * Return a raw integer value (no type coercion). + * Locate a child node by name and index. */ -int -FGValue::getRawInt () const +static int +find_child (const string &name, int index, vector nodes) { - if (_tied) { - if (_value.int_func.getter != 0) - return (*(_value.int_func.getter))(); - else - return 0; - } else { - return _value.int_val; + int nNodes = nodes.size(); + for (int i = 0; i < nNodes; i++) { + SGPropertyNode * node = nodes[i]; + if (node->getName() == name && node->getIndex() == index) + return i; } + return -1; } /** - * Return a raw floating-point value (no type coercion). + * Locate another node, given a relative path. */ -float -FGValue::getRawFloat () const +static SGPropertyNode * +find_node (SGPropertyNode * current, + const vector &components, + int position, + bool create) { - if (_tied) { - if (_value.float_func.getter != 0) - return (*(_value.float_func.getter))(); + // Run off the end of the list + if (current == 0) { + return 0; + } + + // Success! This is the one we want. + else if (position >= (int)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) + throw string("Attempt to move past root with '..'"); else - return 0.0; - } else { - return _value.float_val; + return find_node(parent, components, position + 1, create); + } + + // Otherwise, a child name + else { + SGPropertyNode * child = + current->getChild(components[position].name, + components[position].index, + create); + return find_node(child, components, position + 1, create); } } + +//////////////////////////////////////////////////////////////////////// +// Implementation of SGPropertyNode. +//////////////////////////////////////////////////////////////////////// + + /** - * Return a raw double-precision floating-point value (no type coercion). + * Default constructor: always creates a root node. */ -double -FGValue::getRawDouble () const +SGPropertyNode::SGPropertyNode () + : _name(""), + _index(0), + _parent(0), + _path_cache(0), + _type(NONE), + _tied(false), + _attr(READ|WRITE) { - if (_tied) { - if (_value.double_func.getter != 0) - return (*(_value.double_func.getter))(); - else - return 0.0L; - } else { - return _value.double_val; +} + + +/** + * Copy constructor. + */ +SGPropertyNode::SGPropertyNode (const SGPropertyNode &node) + : _name(node._name), + _index(node._index), + _parent(0), // don't copy the parent + _path_cache(0), + _type(node._type), + _tied(node._tied), + _attr(node._attr) +{ + switch (_type) { + case NONE: + break; + case ALIAS: + _value.alias = node._value.alias; + break; + case BOOL: + _value.bool_val = node._value.bool_val->clone(); + break; + case INT: + _value.int_val = node._value.int_val->clone(); + break; + case LONG: + _value.long_val = node._value.long_val->clone(); + break; + case FLOAT: + _value.float_val = node._value.float_val->clone(); + break; + case DOUBLE: + _value.double_val = node._value.double_val->clone(); + break; + case STRING: + case UNSPECIFIED: + _value.string_val = node._value.string_val->clone(); + break; } } /** - * Return a raw string value (no type coercion). + * Convenience constructor. */ -const string & -FGValue::getRawString () const +SGPropertyNode::SGPropertyNode (const string &name, + int index, SGPropertyNode * parent) + : _name(name), + _index(index), + _parent(parent), + _path_cache(0), + _type(NONE), + _tied(false), + _attr(READ|WRITE) { - if (_tied && _value.string_func.getter != 0) - return (*(_value.string_func.getter))(); - else - return string_val; } /** - * Set a raw boolean value (no type coercion). - * - * Return false if the value could not be set, true otherwise. + * Destructor. */ -bool -FGValue::setRawBool (bool value) +SGPropertyNode::~SGPropertyNode () { - if (_tied) { - if (_value.bool_func.setter != 0) { - (*_value.bool_func.setter)(value); - return true; - } else { - return false; - } - } else { - _value.bool_val = value; - return true; + for (int i = 0; i < (int)_children.size(); i++) { + delete _children[i]; } + delete _path_cache; + clear_value(); } /** - * Set a raw integer value (no type coercion). - * - * Return false if the value could not be set, true otherwise. + * Delete and clear the current value. */ -bool -FGValue::setRawInt (int value) +void +SGPropertyNode::clear_value () { - if (_tied) { - if (_value.int_func.setter != 0) { - (*_value.int_func.setter)(value); - return true; - } else { - return false; - } - } else { - _value.int_val = value; - return true; + switch (_type) { + case NONE: + case ALIAS: + _value.alias = 0; + break; + case BOOL: + delete _value.bool_val; + _value.bool_val = 0; + break; + case INT: + 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; + break; + case DOUBLE: + delete _value.double_val; + _value.double_val = 0; + break; + case STRING: + case UNSPECIFIED: + delete _value.string_val; + _value.string_val = 0; + break; } + _type = NONE; } /** - * Set a raw floating-point value (no type coercion). - * - * Return false if the value could not be set, true otherwise. + * Get the value as a string. */ -bool -FGValue::setRawFloat (float value) +string +SGPropertyNode::get_string () const { - if (_tied) { - if (_value.float_func.setter != 0) { - (*_value.float_func.setter)(value); - return true; - } else { - return false; - } - } else { - _value.float_val = value; - return true; + TEST_READ(""); + char buf[128]; + + switch (_type) { + case ALIAS: + return _value.alias->getStringValue(); + case BOOL: + if (GET_BOOL) + return "true"; + else + return "false"; + 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; + case DOUBLE: + sprintf(buf, "%f", GET_DOUBLE); + return buf; + case STRING: + case UNSPECIFIED: + return GET_STRING; + case NONE: + default: + return ""; } } /** - * Set a raw double-precision floating-point value (no type coercion). - * - * Return false if the value could not be set, true otherwise. + * Trace a read access for a property. + */ +void +SGPropertyNode::trace_read (SGPropertyNode::Type accessType) const +{ + SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Read node " << getPath() + << ", value \"" << get_string() << '"'); +} + + +/** + * Trace a write access for a property. + */ +void +SGPropertyNode::trace_write (SGPropertyNode::Type accessType) const +{ + SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Write node " << getPath() + << ", value\"" << get_string() << '"'); +} + + +/** + * Alias to another node. */ bool -FGValue::setRawDouble (double value) +SGPropertyNode::alias (SGPropertyNode * target) { - if (_tied) { - if (_value.double_func.setter != 0) { - (*_value.double_func.setter)(value); - return true; - } else { - return false; - } - } else { - _value.double_val = value; - return true; - } + if (target == 0 || _type == ALIAS || _tied) + return false; + clear_value(); + _value.alias = target; + _type = ALIAS; + return true; } /** - * Set a raw string value (no type coercion). - * - * Return false if the value could not be set, true otherwise. + * Alias to another node by path. */ bool -FGValue::setRawString (const string &value) +SGPropertyNode::alias (const string &path) { - if (_tied) { - if (_value.string_func.setter != 0) { - (*_value.string_func.setter)(value); - return true; - } else { - return false; - } + return alias(getNode(path, true)); +} + + +/** + * Remove an alias. + */ +bool +SGPropertyNode::unalias () +{ + if (_type != ALIAS) + return false; + _type = NONE; + _value.alias = 0; + return true; +} + + +/** + * Get the target of an alias. + */ +SGPropertyNode * +SGPropertyNode::getAliasTarget () +{ + return (_type == ALIAS ? _value.alias : 0); +} + + +const SGPropertyNode * +SGPropertyNode::getAliasTarget () const +{ + return (_type == ALIAS ? _value.alias : 0); +} + + +/** + * Get a non-const child by index. + */ +SGPropertyNode * +SGPropertyNode::getChild (int position) +{ + if (position >= 0 && position < nChildren()) + return _children[position]; + else + return 0; +} + + +/** + * Get a const child by index. + */ +const SGPropertyNode * +SGPropertyNode::getChild (int position) const +{ + if (position >= 0 && position < nChildren()) + return _children[position]; + else + 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 _children[pos]; + } else if (create) { + _children.push_back(new SGPropertyNode(name, index, this)); + return _children[_children.size()-1]; } else { - string_val = value; - return true; + return 0; } } /** - * Get the boolean value of a property. - * - * If the native type is not boolean, attempt to coerce it. + * Get a const child by name and index. */ -bool -FGValue::getBoolValue () const +const SGPropertyNode * +SGPropertyNode::getChild (const string &name, int index) const +{ + int pos = find_child(name, index, _children); + if (pos >= 0) + return _children[pos]; + else + return 0; +} + + +/** + * Get all children with the same name (but different indices). + */ +vector +SGPropertyNode::getChildren (const string &name) +{ + vector children; + int max = _children.size(); + + for (int i = 0; i < max; i++) + if (_children[i]->getName() == name) + children.push_back(_children[i]); + + sort(children.begin(), children.end(), CompareIndices()); + return children; +} + + +/** + * Get all children const with the same name (but different indices). + */ +vector +SGPropertyNode::getChildren (const string &name) const +{ + vector children; + int max = _children.size(); + + for (int i = 0; i < max; i++) + if (_children[i]->getName() == name) + children.push_back(_children[i]); + + sort(children.begin(), children.end(), CompareIndices()); + return children; +} + + +string +SGPropertyNode::getPath (bool simplify) const +{ + if (_parent == 0) + return ""; + + string path = _parent->getPath(simplify); + path += '/'; + path += _name; + if (_index != 0 || !simplify) { + char buffer[128]; + sprintf(buffer, "[%d]", _index); + path += buffer; + } + return path; +} + +SGPropertyNode::Type +SGPropertyNode::getType () const +{ + if (_type == ALIAS) + return _value.alias->getType(); + else + return _type; +} + + +bool +SGPropertyNode::getBoolValue () const { + DO_TRACE_READ(BOOL); + TEST_READ(false); switch (_type) { + case ALIAS: + return _value.alias->getBoolValue(); case BOOL: - return getRawBool(); + return GET_BOOL; case INT: - return (getRawInt() == 0 ? false : true); + return GET_INT == 0 ? false : true; + case LONG: + return GET_LONG == 0L ? false : true; case FLOAT: - return (getRawFloat() == 0.0 ? false : true); + return GET_FLOAT == 0.0 ? false : true; case DOUBLE: - return (getRawDouble() == 0.0 ? false : true); - case UNKNOWN: + return GET_DOUBLE == 0.0L ? false : true; case STRING: - return ((getRawString() == "false" || getIntValue() == 0) ? false : true); + case UNSPECIFIED: + return (GET_STRING == "true" || getDoubleValue() != 0.0L); + case NONE: + default: + return false; } - return false; } - -/** - * Get the integer value of a property. - * - * If the native type is not integer, attempt to coerce it. - */ -int -FGValue::getIntValue () const +int +SGPropertyNode::getIntValue () const { + DO_TRACE_READ(INT); + TEST_READ(0); switch (_type) { + case ALIAS: + return _value.alias->getIntValue(); case BOOL: - return getRawBool(); + return int(GET_BOOL); case INT: - return getRawInt(); + return GET_INT; + case LONG: + return int(GET_LONG); case FLOAT: - return (int)(getRawFloat()); + return int(GET_FLOAT); case DOUBLE: - return (int)(getRawDouble()); - case UNKNOWN: + return int(GET_DOUBLE); case STRING: - return atoi(getRawString().c_str()); + case UNSPECIFIED: + return atoi(GET_STRING.c_str()); + case NONE: + default: + return 0; } - return false; } +long +SGPropertyNode::getLongValue () const +{ + DO_TRACE_READ(LONG); + TEST_READ(0L); + 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 UNSPECIFIED: + return strtol(GET_STRING.c_str(), 0, 0); + case NONE: + default: + return 0L; + } +} -/** - * Get the floating-point value of a property. - * - * If the native type is not float, attempt to coerce it. - */ -float -FGValue::getFloatValue () const +float +SGPropertyNode::getFloatValue () const { + DO_TRACE_READ(FLOAT); + TEST_READ(0.0); switch (_type) { - case UNKNOWN: + case ALIAS: + return _value.alias->getFloatValue(); + case BOOL: + return float(GET_BOOL); + case INT: + return float(GET_INT); + case LONG: + return float(GET_LONG); + case FLOAT: + return GET_FLOAT; + case DOUBLE: + return float(GET_DOUBLE); + case STRING: + case UNSPECIFIED: + return atof(GET_STRING.c_str()); + case NONE: + default: return 0.0; + } +} + +double +SGPropertyNode::getDoubleValue () const +{ + DO_TRACE_READ(DOUBLE); + TEST_READ(0.0L); + switch (_type) { + case ALIAS: + return _value.alias->getDoubleValue(); case BOOL: - return (float)(getRawBool()); + return double(GET_BOOL); case INT: - return (float)(getRawInt()); + return double(GET_INT); + case LONG: + return double(GET_LONG); case FLOAT: - return getRawFloat(); + return double(GET_FLOAT); case DOUBLE: - return (float)(getRawDouble()); + return GET_DOUBLE; case STRING: - return (float)atof(getRawString().c_str()); + case UNSPECIFIED: + return strtod(GET_STRING.c_str(), 0); + case NONE: + default: + return 0.0L; } - return false; } +string +SGPropertyNode::getStringValue () const +{ + DO_TRACE_READ(STRING); + return get_string(); +} -/** - * Get the double-precision floating-point value of a property. - * - * If the native type is not double, attempt to coerce it. - */ -double -FGValue::getDoubleValue () const +bool +SGPropertyNode::setBoolValue (bool value) { + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); + _value.bool_val = new SGRawValueInternal; + _type = BOOL; + } + switch (_type) { - case UNKNOWN: - return 0.0; + case ALIAS: + result = _value.alias->setBoolValue(value); + break; case BOOL: - return (double)(getRawBool()); + result = SET_BOOL(value); + break; case INT: - return (double)(getRawInt()); + result = SET_INT(int(value)); + break; + case LONG: + result = SET_LONG(long(value)); + break; case FLOAT: - return (double)(getRawFloat()); + result = SET_FLOAT(float(value)); + break; case DOUBLE: - return getRawDouble(); + result = SET_DOUBLE(double(value)); + break; case STRING: - return atof(getRawString().c_str()); + case UNSPECIFIED: + result = SET_STRING(value ? "true" : "false"); + break; + case NONE: + default: + break; } - return false; + + DO_TRACE_WRITE(BOOL); + return result; } +bool +SGPropertyNode::setIntValue (int value) +{ + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); + _value.int_val = new SGRawValueInternal; + _type = INT; + } -/** - * Get the string value of a property. - * - * If the native type is not string, attempt to coerce it. - */ -const string & -FGValue::getStringValue () const + switch (_type) { + case ALIAS: + result = _value.alias->setIntValue(value); + break; + case BOOL: + result = SET_BOOL(value == 0 ? false : true); + break; + case INT: + result = SET_INT(value); + break; + case LONG: + result = SET_LONG(long(value)); + break; + case FLOAT: + result = SET_FLOAT(float(value)); + break; + case DOUBLE: + result = SET_DOUBLE(double(value)); + break; + case STRING: + case UNSPECIFIED: { + char buf[128]; + sprintf(buf, "%d", value); + result = SET_STRING(buf); + break; + } + case NONE: + default: + break; + } + + DO_TRACE_WRITE(INT); + return result; +} + +bool +SGPropertyNode::setLongValue (long value) { - char buf[512]; + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); + _value.long_val = new SGRawValueInternal; + _type = LONG; + } + switch (_type) { - case UNKNOWN: - return getRawString(); + case ALIAS: + result = _value.alias->setLongValue(value); + break; case BOOL: - if (getRawBool()) - string_val = "true"; - else - string_val = "false"; - return string_val; + result = SET_BOOL(value == 0L ? false : true); + break; case INT: - sprintf(buf, "%d", getRawInt()); - string_val = buf; - return string_val; + result = SET_INT(int(value)); + break; + case LONG: + result = SET_LONG(value); + break; case FLOAT: - sprintf(buf, "%f", getRawFloat()); - string_val = buf; - return string_val; + result = SET_FLOAT(float(value)); + break; case DOUBLE: - sprintf(buf, "%f", getRawDouble()); - string_val = buf; - return string_val; + result = SET_DOUBLE(double(value)); + break; case STRING: - return getRawString(); + case UNSPECIFIED: { + char buf[128]; + sprintf(buf, "%ld", value); + result = SET_STRING(buf); + break; + } + case NONE: + default: + break; } - return empty_string; + + DO_TRACE_WRITE(LONG); + return result; } +bool +SGPropertyNode::setFloatValue (float value) +{ + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); + _value.float_val = new SGRawValueInternal; + _type = FLOAT; + } + + switch (_type) { + case ALIAS: + result = _value.alias->setFloatValue(value); + break; + case BOOL: + result = SET_BOOL(value == 0.0 ? false : true); + break; + case INT: + result = SET_INT(int(value)); + break; + case LONG: + result = SET_LONG(long(value)); + break; + case FLOAT: + result = SET_FLOAT(value); + break; + case DOUBLE: + result = SET_DOUBLE(double(value)); + break; + case STRING: + case UNSPECIFIED: { + char buf[128]; + sprintf(buf, "%f", value); + result = SET_STRING(buf); + break; + } + case NONE: + default: + break; + } + + DO_TRACE_WRITE(FLOAT); + return result; +} -/** - * Set the boolean value and change the type if unknown. - * - * Returns true on success. - */ bool -FGValue::setBoolValue (bool value) +SGPropertyNode::setDoubleValue (double value) { - if (_type == UNKNOWN || _type == BOOL) { - _type = BOOL; - return setRawBool(value); - } else { - return false; + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); + _value.double_val = new SGRawValueInternal; + _type = DOUBLE; } + + switch (_type) { + case ALIAS: + result = _value.alias->setDoubleValue(value); + break; + case BOOL: + result = SET_BOOL(value == 0.0L ? false : true); + break; + case INT: + result = SET_INT(int(value)); + break; + case LONG: + result = SET_LONG(long(value)); + break; + case FLOAT: + result = SET_FLOAT(float(value)); + break; + case DOUBLE: + result = SET_DOUBLE(value); + break; + case STRING: + case UNSPECIFIED: { + char buf[128]; + sprintf(buf, "%f", value); + result = SET_STRING(buf); + break; + } + case NONE: + default: + break; + } + + DO_TRACE_WRITE(DOUBLE); + return result; } +bool +SGPropertyNode::setStringValue (string value) +{ + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); + _value.string_val = new SGRawValueInternal; + _type = STRING; + } + + switch (_type) { + case ALIAS: + result = _value.alias->setStringValue(value); + break; + case BOOL: + result = SET_BOOL((value == "true" || atoi(value.c_str())) ? true : false); + break; + case INT: + result = SET_INT(atoi(value.c_str())); + break; + case LONG: + result = SET_LONG(strtol(value.c_str(), 0, 0)); + break; + case FLOAT: + result = SET_FLOAT(atof(value.c_str())); + break; + case DOUBLE: + result = SET_DOUBLE(strtod(value.c_str(), 0)); + break; + case STRING: + case UNSPECIFIED: + result = SET_STRING(value); + break; + case NONE: + default: + break; + } + + DO_TRACE_WRITE(STRING); + return result; +} -/** - * Set the integer value and change the type if unknown. - * - * Returns true on success. - */ bool -FGValue::setIntValue (int value) +SGPropertyNode::setUnspecifiedValue (string value) { - if (_type == UNKNOWN || _type == INT) { - _type = INT; - return setRawInt(value); - } else { - return false; + bool result = false; + TEST_WRITE; + if (_type == NONE) { + clear_value(); + _value.string_val = new SGRawValueInternal; + _type = UNSPECIFIED; + } + + switch (_type) { + case ALIAS: + result = _value.alias->setUnspecifiedValue(value); + break; + case BOOL: + result = SET_BOOL((value == "true" || atoi(value.c_str())) ? true : false); + break; + case INT: + result = SET_INT(atoi(value.c_str())); + break; + case LONG: + result = SET_LONG(strtol(value.c_str(), 0, 0)); + break; + case FLOAT: + result = SET_FLOAT(atof(value.c_str())); + break; + case DOUBLE: + result = SET_DOUBLE(strtod(value.c_str(), 0)); + break; + case STRING: + case UNSPECIFIED: + result = SET_STRING(value); + break; + case NONE: + default: + break; } -} + DO_TRACE_WRITE(UNSPECIFIED); + return result; +} -/** - * Set the floating-point value and change the type if unknown. - * - * Returns true on success. - */ bool -FGValue::setFloatValue (float value) +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) { - if (_type == UNKNOWN || _type == FLOAT) { - _type = FLOAT; - return setRawFloat(value); - } else { + if (_type == ALIAS || _tied) return false; - } -} + bool old_val = false; + if (useDefault) + old_val = getBoolValue(); -/** - * Set the double-precision value and change the type if unknown. - * - * Returns true on success. - */ -bool -FGValue::setDoubleValue (double value) -{ - if (_type == UNKNOWN || _type == DOUBLE) { - _type = DOUBLE; - return setRawDouble(value); - } else { - return false; - } -} + clear_value(); + _type = BOOL; + _tied = true; + _value.bool_val = rawValue.clone(); + if (useDefault) + setBoolValue(old_val); -/** - * Set the string value and change the type if unknown. - * - * Returns true on success. - */ -bool -FGValue::setStringValue (const string &value) -{ - if (_type == UNKNOWN || _type == STRING) { - _type = STRING; - return setRawString(value); - } else { - return false; - } + return true; } - -/** - * Set a string value and don't modify the type. - * - * Returns true on success. - */ bool -FGValue::setUnknownValue (const string &value) +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) { - if (_type == UNKNOWN || _type == STRING) { - return setRawString(value); - } else { + if (_type == ALIAS || _tied) return false; - } -} + int old_val = 0; + if (useDefault) + old_val = getIntValue(); -/** - * Tie a boolean value to external functions. - * - * If useDefault is true, attempt the assign the current value - * (if any) after tying the functions. - * - * Returns true on success (i.e. the value is not currently tied). - */ -bool -FGValue::tieBool (bool_getter getter, bool_setter setter = 0, - bool useDefault = true) -{ - if (_tied) { - return false; - } else { - if (useDefault && setter && _type != UNKNOWN) - (*setter)(getBoolValue()); - _tied = true; - _type = BOOL; - _value.bool_func.getter = getter; - _value.bool_func.setter = setter; - return true; - } -} + clear_value(); + _type = INT; + _tied = true; + _value.int_val = rawValue.clone(); + if (useDefault) + setIntValue(old_val); + + return true; +} -/** - * Tie an integer value to external functions. - * - * If useDefault is true, attempt the assign the current value - * (if any) after tying the functions. - * - * Returns true on success (i.e. the value is not currently tied). - */ bool -FGValue::tieInt (int_getter getter, int_setter setter = 0, - bool useDefault = true) +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) { - if (_tied) { + if (_type == ALIAS || _tied) return false; - } else { - if (useDefault && setter && _type != UNKNOWN) - (*setter)(getIntValue()); - _tied = true; - _type = INT; - _value.int_func.getter = getter; - _value.int_func.setter = setter; - return true; - } -} + long old_val; + if (useDefault) + old_val = getLongValue(); + + clear_value(); + _type = LONG; + _tied = true; + _value.long_val = rawValue.clone(); + + if (useDefault) + setLongValue(old_val); + + return true; +} -/** - * Tie a floating-point value to external functions. - * - * If useDefault is true, attempt the assign the current value - * (if any) after tying the functions. - * - * Returns true on success (i.e. the value is not currently tied). - */ bool -FGValue::tieFloat (float_getter getter, float_setter setter = 0, - bool useDefault = true) +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) { - if (_tied) { + if (_type == ALIAS || _tied) return false; - } else { - if (useDefault && setter && _type != UNKNOWN) - (*setter)(getFloatValue()); - _tied = true; - _type = FLOAT; - _value.float_func.getter = getter; - _value.float_func.setter = setter; - return true; - } -} + float old_val = 0.0; + if (useDefault) + old_val = getFloatValue(); + + clear_value(); + _type = FLOAT; + _tied = true; + _value.float_val = rawValue.clone(); + + if (useDefault) + setFloatValue(old_val); + + return true; +} -/** - * Tie a double-precision floating-point value to external functions. - * - * If useDefault is true, attempt the assign the current value - * (if any) after tying the functions. - * - * Returns true on success (i.e. the value is not currently tied). - */ bool -FGValue::tieDouble (double_getter getter, double_setter setter = 0, - bool useDefault = true) +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) { - if (_tied) { + if (_type == ALIAS || _tied) return false; - } else { - if (useDefault && setter && _type != UNKNOWN) - (*setter)(getDoubleValue()); - _tied = true; - _type = DOUBLE; - _value.double_func.getter = getter; - _value.double_func.setter = setter; - return true; - } -} + double old_val = 0.0; + if (useDefault) + old_val = getDoubleValue(); + + clear_value(); + _type = DOUBLE; + _tied = true; + _value.double_val = rawValue.clone(); + + if (useDefault) + setDoubleValue(old_val); + + return true; + +} -/** - * Tie a string value to external functions. - * - * If useDefault is true, attempt the assign the current value - * (if any) after tying the functions. - * - * Returns true on success (i.e. the value is not currently tied). - */ bool -FGValue::tieString (string_getter getter, string_setter setter = 0, - bool useDefault = true) +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) { - if (_tied) { + if (_type == ALIAS || _tied) return false; - } else { - if (useDefault && setter && _type != UNKNOWN) - (*setter)(getStringValue()); - _tied = true; - _type = STRING; - _value.string_func.getter = getter; - _value.string_func.setter = setter; - return true; - } -} + string old_val; + if (useDefault) + old_val = getStringValue(); + + clear_value(); + _type = STRING; + _tied = true; + _value.string_val = rawValue.clone(); + + if (useDefault) + setStringValue(old_val); + + return true; +} -/** - * Untie a value from external functions. - * - * Will always attempt to intialize the internal value from - * the getter before untying. - * - * Returns true on success (i.e. the value had been tied). - */ bool -FGValue::untie () +SGPropertyNode::untie () { if (!_tied) return false; switch (_type) { case BOOL: { - bool value = getRawBool(); - _tied = false; - setRawBool(value); + bool val = getBoolValue(); + clear_value(); + _type = BOOL; + _value.bool_val = new SGRawValueInternal; + SET_BOOL(val); break; } case INT: { - int value = getRawInt(); - _tied = false; - setRawInt(value); + int val = getIntValue(); + clear_value(); + _type = INT; + _value.int_val = new SGRawValueInternal; + SET_INT(val); + break; + } + case LONG: { + long val = getLongValue(); + clear_value(); + _type = LONG; + _value.long_val = new SGRawValueInternal; + SET_LONG(val); break; } case FLOAT: { - float value = getRawFloat(); - _tied = false; - setRawFloat(value); + float val = getFloatValue(); + clear_value(); + _type = FLOAT; + _value.float_val = new SGRawValueInternal; + SET_FLOAT(val); break; } case DOUBLE: { - double value = getRawDouble(); - _tied = false; - setRawDouble(value); + double val = getDoubleValue(); + clear_value(); + _type = DOUBLE; + _value.double_val = new SGRawValueInternal; + SET_DOUBLE(val); break; } - case STRING: { - string value = getRawString(); - _tied = false; - setRawString(value); + case STRING: + case UNSPECIFIED: { + string val = getStringValue(); + clear_value(); + _type = STRING; + _value.string_val = new SGRawValueInternal; + SET_STRING(val); break; } + case NONE: + default: + break; } + _tied = false; return true; } - - -//////////////////////////////////////////////////////////////////////// -// Implementation of FGPropertyList. -//////////////////////////////////////////////////////////////////////// - - -/** - * Constructor. - */ -FGPropertyList::FGPropertyList () +SGPropertyNode * +SGPropertyNode::getRootNode () { + if (_parent == 0) + return this; + else + return _parent->getRootNode(); } - -/** - * Destructor. - */ -FGPropertyList::~FGPropertyList () +const SGPropertyNode * +SGPropertyNode::getRootNode () const { + if (_parent == 0) + return this; + else + return _parent->getRootNode(); } - -/** - * Look up the FGValue structure associated with a property. - * - * Run some basic validity checks on the property name: it must - * not be empty, must begin with '/', must never have two '//' in a row, - * and must not end with '/'. - */ -FGValue * -FGPropertyList::getValue (const string &name, bool create = false) -{ - const_iterator el = _props.find(name); - if (el == _props.end()) { - if (!create) - return 0; - else { - FG_LOG(FG_GENERAL, FG_INFO, "Creating new property '" << name << '\''); - if (name.size() == 0 || - name[0] != '/' || - name[name.size()-1] == '/' || - name.find("//") != string::npos) { - FG_LOG(FG_GENERAL, FG_ALERT, "Illegal property name: '" - << name << '\''); - return 0; - } - } +SGPropertyNode * +SGPropertyNode::getNode (const string &relative_path, bool create) +{ + if (_path_cache == 0) + _path_cache = new cache_map; + + SGPropertyNode * result = (*_path_cache)[relative_path]; + if (result == 0) { + vector components; + parse_path(relative_path, components); + result = find_node(this, components, 0, create); + (*_path_cache)[relative_path] = result; } - return &(_props[name]); + + return result; } - -/** - * Look up a const value (never created). - */ -const FGValue * -FGPropertyList::getValue (const string &name) const +SGPropertyNode * +SGPropertyNode::getNode (const string &relative_path, int index, bool create) { - value_map::const_iterator el = _props.find(name); - if (el == _props.end()) - return 0; - else - return &(el->second); + vector components; + parse_path(relative_path, components); + if (components.size() > 0) + components[components.size()-1].index = index; + return find_node(this, components, 0, create); } - -/** - * Extract a boolean from the value. - * - * Note that this is inefficient for use in a tight loop: it is - * better to get the FGValue and query it repeatedly. - */ -bool -FGPropertyList::getBoolValue (const string &name) const +const SGPropertyNode * +SGPropertyNode::getNode (const string &relative_path) const { - const FGValue * val = getValue(name); - if (val == 0) - return false; - else - return val->getBoolValue(); + return ((SGPropertyNode *)this)->getNode(relative_path, false); } - -/** - * Extract an integer from the value. - * - * Note that this is inefficient for use in a tight loop: it is - * better to get the FGValue and query it repeatedly. - */ -int -FGPropertyList::getIntValue (const string &name) const +const SGPropertyNode * +SGPropertyNode::getNode (const string &relative_path, int index) const { - const FGValue * val = getValue(name); - if (val == 0) - return 0; - else - return val->getIntValue(); + return ((SGPropertyNode *)this)->getNode(relative_path, index, false); } - -/** - * Extract a float from the value. - * - * Note that this is inefficient for use in a tight loop: it is - * better to get the FGValue and query it repeatedly. - */ -float -FGPropertyList::getFloatValue (const string &name) const -{ - const FGValue * val = getValue(name); - if (val == 0) - return 0.0; - else - return val->getFloatValue(); -} + +//////////////////////////////////////////////////////////////////////// +// Convenience methods using relative paths. +//////////////////////////////////////////////////////////////////////// /** - * Extract a double from the value. - * - * Note that this is inefficient for use in a tight loop: it is - * better to get the FGValue and query it repeatedly. + * Test whether another node has a value attached. */ -double -FGPropertyList::getDoubleValue (const string &name) const +bool +SGPropertyNode::hasValue (const string &relative_path) const { - const FGValue * val = getValue(name); - if (val == 0) - return 0.0; - else - return val->getDoubleValue(); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? false : node->hasValue()); } /** - * Extract a string from the value. - * - * Note that this is inefficient for use in a tight loop: it is - * better to save the FGValue and query it repeatedly. + * Get the value type for another node. */ -const string & -FGPropertyList::getStringValue (const string &name) const +SGPropertyNode::Type +SGPropertyNode::getType (const string &relative_path) const { - const FGValue * val = getValue(name); - if (val == 0) - return empty_string; - else - return val->getStringValue(); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? UNSPECIFIED : (Type)(node->getType())); } /** - * Assign a bool to the value and change the type if unknown. - * - * Note that this is inefficient for use in a tight loop: it is - * better to save the FGValue and modify it repeatedly. - * - * Returns true on success. + * Get a bool value for another node. */ bool -FGPropertyList::setBoolValue (const string &name, bool value) +SGPropertyNode::getBoolValue (const string &relative_path, + bool defaultValue) const { - return getValue(name, true)->setBoolValue(value); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getBoolValue()); } /** - * Assign an integer to the value and change the type if unknown. - * - * Note that this is inefficient for use in a tight loop: it is - * better to save the FGValue and modify it repeatedly. - * - * Returns true on success. + * Get an int value for another node. */ -bool -FGPropertyList::setIntValue (const string &name, int value) +int +SGPropertyNode::getIntValue (const string &relative_path, + int defaultValue) const { - return getValue(name, true)->setIntValue(value); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getIntValue()); } /** - * Assign a float to the value and change the type if unknown. - * - * Note that this is inefficient for use in a tight loop: it is - * better to save the FGValue and modify it repeatedly. - * - * Returns true on success. + * Get a long value for another node. */ -bool -FGPropertyList::setFloatValue (const string &name, float value) +long +SGPropertyNode::getLongValue (const string &relative_path, + long defaultValue) const { - return getValue(name, true)->setFloatValue(value); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getLongValue()); } /** - * Assign a double to the value and change the type if unknown. - * - * Note that this is inefficient for use in a tight loop: it is - * better to save the FGValue and modify it repeatedly. - * - * Returns true on success. + * Get a float value for another node. */ -bool -FGPropertyList::setDoubleValue (const string &name, double value) +float +SGPropertyNode::getFloatValue (const string &relative_path, + float defaultValue) const { - return getValue(name, true)->setDoubleValue(value); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getFloatValue()); } /** - * Assign a string to the value and change the type if unknown. - * - * Note that this is inefficient for use in a tight loop: it is - * better to save the FGValue and modify it repeatedly. - * - * Returns true on success. + * Get a double value for another node. */ -bool -FGPropertyList::setStringValue (const string &name, const string &value) +double +SGPropertyNode::getDoubleValue (const string &relative_path, + double defaultValue) const { - return getValue(name, true)->setStringValue(value); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getDoubleValue()); } /** - * Assign a string to the value, but don't change the type. - * - * Note that this is inefficient for use in a tight loop: it is - * better to save the FGValue and modify it repeatedly. - * - * Returns true on success. + * Get a string value for another node. */ -bool -FGPropertyList::setUnknownValue (const string &name, const string &value) +string +SGPropertyNode::getStringValue (const string &relative_path, + string defaultValue) const { - return getValue(name, true)->setUnknownValue(value); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getStringValue()); } /** - * Tie a boolean value to external functions. - * - * Invokes FGValue::tieBool + * Set a bool value for another node. */ bool -FGPropertyList::tieBool (const string &name, - bool_getter getter, - bool_setter setter, - bool useDefault = true) +SGPropertyNode::setBoolValue (const string &relative_path, bool value) { - FG_LOG(FG_GENERAL, FG_INFO, "Tying bool property '" << name << '\''); - return getValue(name, true)->tieBool(getter, setter, useDefault); + return getNode(relative_path, true)->setBoolValue(value); } /** - * Tie an integer value to external functions. - * - * Invokes FGValue::tieInt + * Set an int value for another node. */ bool -FGPropertyList::tieInt (const string &name, - int_getter getter, - int_setter setter, - bool useDefault = true) +SGPropertyNode::setIntValue (const string &relative_path, int value) { - FG_LOG(FG_GENERAL, FG_INFO, "Tying int property '" << name << '\''); - return getValue(name, true)->tieInt(getter, setter, useDefault); + return getNode(relative_path, true)->setIntValue(value); } /** - * Tie a float value to external functions. - * - * Invokes FGValue::tieFloat + * Set a long value for another node. */ bool -FGPropertyList::tieFloat (const string &name, - float_getter getter, - float_setter setter, - bool useDefault = true) +SGPropertyNode::setLongValue (const string &relative_path, long value) { - FG_LOG(FG_GENERAL, FG_INFO, "Tying float property '" << name << '\''); - return getValue(name, true)->tieFloat(getter, setter, useDefault); + return getNode(relative_path, true)->setLongValue(value); } /** - * Tie a double value to external functions. - * - * Invokes FGValue::tieDouble + * Set a float value for another node. */ bool -FGPropertyList::tieDouble (const string &name, - double_getter getter, - double_setter setter, - bool useDefault = true) +SGPropertyNode::setFloatValue (const string &relative_path, float value) { - FG_LOG(FG_GENERAL, FG_INFO, "Tying double property '" << name << '\''); - return getValue(name, true)->tieDouble(getter, setter, useDefault); + return getNode(relative_path, true)->setFloatValue(value); } /** - * Tie a string value to external functions. - * - * Invokes FGValue::tieString + * Set a double value for another node. */ bool -FGPropertyList::tieString (const string &name, - string_getter getter, - string_setter setter, - bool useDefault = true) +SGPropertyNode::setDoubleValue (const string &relative_path, double value) { - FG_LOG(FG_GENERAL, FG_INFO, "Tying string property '" << name << '\''); - return getValue(name, true)->tieString(getter, setter, useDefault); + return getNode(relative_path, true)->setDoubleValue(value); } /** - * Untie a value from external functions. - * - * Invokes FGValue::untie + * Set a string value for another node. */ bool -FGPropertyList::untie (const string &name) +SGPropertyNode::setStringValue (const string &relative_path, string value) { - FG_LOG(FG_GENERAL, FG_INFO, "Untying property '" << name << '\''); - return getValue(name, true)->untie(); + return getNode(relative_path, true)->setStringValue(value); } - -//////////////////////////////////////////////////////////////////////// -// Implementation of FGPropertyNode. -//////////////////////////////////////////////////////////////////////// - - /** - * Extract the base name of the next level down from the parent. - * - * The parent must have a '/' appended. Note that basename may - * be modified even if the test fails. + * Set an unknown value for another node. */ -static bool -get_base (const string &parent, const string &child, - string &basename) -{ - // First, check that the parent name - // is a prefix of the child name, and - // extract the remainder - if (child.find(parent) != 0) - return false; - - basename = child.substr(parent.size()); - - string::size_type pos = basename.find('/'); - if (pos != string::npos) { - basename.resize(pos); - } - - if (basename.size() == 0) - return false; - else - return true; +bool +SGPropertyNode::setUnspecifiedValue (const string &relative_path, string value) +{ + return getNode(relative_path, true)->setUnspecifiedValue(value); } /** - * Constructor. + * Test whether another node is tied. */ -FGPropertyNode::FGPropertyNode (const string &path = "", - FGPropertyList * props = 0) - : _props(props) +bool +SGPropertyNode::isTied (const string &relative_path) const { - setPath(path); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? false : node->isTied()); } /** - * Destructor. + * Tie a node reached by a relative path, creating it if necessary. */ -FGPropertyNode::~FGPropertyNode () +bool +SGPropertyNode::tie (const string &relative_path, + const SGRawValue &rawValue, + bool useDefault) { + return getNode(relative_path, true)->tie(rawValue, useDefault); } /** - * Set the path. - * - * Strip the trailing '/', if any. + * Tie a node reached by a relative path, creating it if necessary. */ -void -FGPropertyNode::setPath (const string &path) +bool +SGPropertyNode::tie (const string &relative_path, + const SGRawValue &rawValue, + bool useDefault) { - _path = path; - - // Chop the final '/', if present. - if (_path.size() > 0 && _path[_path.size()-1] == '/') - _path.resize(_path.size()-1); + return getNode(relative_path, true)->tie(rawValue, useDefault); } /** - * Return the local name of the property. - * - * The local name is just everything after the last slash. + * Tie a node reached by a relative path, creating it if necessary. */ -const string & -FGPropertyNode::getName () const +bool +SGPropertyNode::tie (const string &relative_path, + const SGRawValue &rawValue, + bool useDefault) { - string::size_type pos = _path.rfind('/'); - if (pos != string::npos) { - _name = _path.substr(pos+1); - return _name; - } else { - return empty_string; - } + return getNode(relative_path, true)->tie(rawValue, useDefault); } /** - * 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. + * Tie a node reached by a relative path, creating it if necessary. */ -FGValue * -FGPropertyNode::getValue () +bool +SGPropertyNode::tie (const string &relative_path, + const SGRawValue &rawValue, + bool useDefault) { - if (_props == 0 || _path.size() == 0) - return 0; - else - return _props->getValue(_path); + return getNode(relative_path, true)->tie(rawValue, useDefault); } /** - * Return the number of children for the current node. + * Tie a node reached by a relative path, creating it if necessary. */ -int -FGPropertyNode::size () const +bool +SGPropertyNode::tie (const string &relative_path, + const SGRawValue &rawValue, + bool useDefault) { - if (_props == 0) - return 0; - - int s = 0; - - string base; - string lastBase; - string pattern = _path; - pattern += '/'; - - FGPropertyList::const_iterator it = _props->begin(); - FGPropertyList::const_iterator end = _props->end(); - while (it != end) { - if (get_base(pattern, it->first, base) && base != lastBase) { - s++; - lastBase = base; - } - it++; - } - - return s; + return getNode(relative_path, true)->tie(rawValue, useDefault); } /** - * Initialize a node to represent this node's parent. - * - * A return value of true means success; otherwise, the node supplied - * is unmodified. + * Tie a node reached by a relative path, creating it if necessary. */ bool -FGPropertyNode::getParent (FGPropertyNode &parent) const +SGPropertyNode::tie (const string &relative_path, + const SGRawValue &rawValue, + bool useDefault) { - 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; - } + return getNode(relative_path, true)->tie(rawValue, useDefault); } /** - * Initialize a node to represent this node's nth child. - * - * A return value of true means success; otherwise, the node supplied - * is unmodified. + * Attempt to untie another node reached by a relative path. */ bool -FGPropertyNode::getChild (FGPropertyNode &child, int n) const +SGPropertyNode::untie (const string &relative_path) { - if (_props == 0) - return false; - - int s = 0; - string base; - string lastBase; - string pattern = _path; - pattern += '/'; - - FGPropertyList::const_iterator it = _props->begin(); - FGPropertyList::const_iterator end = _props->end(); - 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; - } else { - s++; - lastBase = base; - } - } - it++; - } - - return false; + SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? false : node->untie()); } // end of props.cxx