X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fmisc%2Fprops.cxx;h=8fbbbd585f7952ec14bcca81b45057203516b854;hb=101fdb359871a14726a83cdb5a8bb2c0f6ee5af1;hp=428aa467863d552627b5b07f90cb5ef2b6536f4a;hpb=5ba74e30b839a573bca5102f7d3104d0a0b147e4;p=simgear.git diff --git a/simgear/misc/props.cxx b/simgear/misc/props.cxx index 428aa467..8fbbbd58 100644 --- a/simgear/misc/props.cxx +++ b/simgear/misc/props.cxx @@ -1,1423 +1,1906 @@ -// 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 "props.hxx" +#if PROPS_STANDALONE + +#include +using std::cerr; +using std::endl; +using std::sort; + +#else + +#include +#include #include -#include "props.hxx" +SG_USING_STD(sort); + +#endif + +#include +#include + + + +//////////////////////////////////////////////////////////////////////// +// 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()); + } +}; + -#include + +//////////////////////////////////////////////////////////////////////// +// Convenience macros for value access. +//////////////////////////////////////////////////////////////////////// -#include +#define TEST_READ(dflt) if (!getAttribute(READ)) return dflt +#define TEST_WRITE if (!getAttribute(WRITE)) return false -using std::string; -SGPropertyList current_properties; + +//////////////////////////////////////////////////////////////////////// +// Default values for every type. +//////////////////////////////////////////////////////////////////////// -static string empty_string; +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 char * const SGRawValue::DefaultValue = ""; //////////////////////////////////////////////////////////////////////// -// Implementation of SGValue. +// 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]* */ -SGValue::SGValue () - : _type(UNKNOWN), _tied(false) +static inline const 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]+ "]" */ -SGValue::~SGValue () +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 -SGValue::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. +//////////////////////////////////////////////////////////////////////// + + +static const char * +copy_string (const char * s) +{ + // FIXME: potential buffer overflow. + // For some reason, strnlen and + // strncpy cause all kinds of crashes. + string str = s; + char * copy = new char[str.size() + 1]; + strcpy(copy, str.c_str()); + return copy; +} + +static bool +compare_strings (const char * s1, const char * s2) +{ + return !strncmp(s1, s2, SGPropertyNode::MAX_STRING_LEN); +} + +/** + * Locate a child node by name and index. + */ +static int +find_child (const char * name, int index, vector nodes) +{ + int nNodes = nodes.size(); + for (int i = 0; i < nNodes; i++) { + SGPropertyNode * node = nodes[i]; + if (compare_strings(node->getName(), name) && node->getIndex() == index) + return i; } + return -1; } /** - * Return a raw integer value (no type coercion). + * Locate another node, given a relative path. */ -int -SGValue::getRawInt () const +static SGPropertyNode * +find_node (SGPropertyNode * current, + const vector &components, + int position, + bool create) { - if (_tied) { - if (_value.int_func.getter != 0) - return (*(_value.int_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; + return find_node(parent, components, position + 1, create); + } + + // Otherwise, a child name + else { + SGPropertyNode * child = + current->getChild(components[position].name.c_str(), + components[position].index, + create); + return find_node(child, components, position + 1, create); + } +} + + + +//////////////////////////////////////////////////////////////////////// +// Private methods from SGPropertyNode (may be inlined for speed). +//////////////////////////////////////////////////////////////////////// + +inline bool +SGPropertyNode::get_bool () const +{ + if (_tied) + return _value.bool_val->getValue(); + else + return _local_val.bool_val; +} + +inline int +SGPropertyNode::get_int () const +{ + if (_tied) + return _value.int_val->getValue(); + else + return _local_val.int_val; +} + +inline long +SGPropertyNode::get_long () const +{ + if (_tied) + return _value.long_val->getValue(); + else + return _local_val.long_val; +} + +inline float +SGPropertyNode::get_float () const +{ + if (_tied) + return _value.float_val->getValue(); + else + return _local_val.float_val; +} + +inline double +SGPropertyNode::get_double () const +{ + if (_tied) + return _value.double_val->getValue(); + else + return _local_val.double_val; +} + +inline const char * +SGPropertyNode::get_string () const +{ + if (_tied) + return _value.string_val->getValue(); + else + return _local_val.string_val; +} + +inline bool +SGPropertyNode::set_bool (bool val) +{ + if (_tied) { + return _value.bool_val->setValue(val); } else { - return _value.int_val; + _local_val.bool_val = val; + return true; } } +inline bool +SGPropertyNode::set_int (int val) +{ + if (_tied) { + return _value.int_val->setValue(val); + } else { + _local_val.int_val = val; + return true; + } +} -/** - * Return a raw floating-point value (no type coercion). - */ -float -SGValue::getRawFloat () const +inline bool +SGPropertyNode::set_long (long val) { if (_tied) { - if (_value.float_func.getter != 0) - return (*(_value.float_func.getter))(); - else - return 0.0; + return _value.long_val->setValue(val); } else { - return _value.float_val; + _local_val.long_val = val; + return true; } } +inline bool +SGPropertyNode::set_float (float val) +{ + if (_tied) { + return _value.float_val->setValue(val); + } else { + _local_val.float_val = val; + return true; + } +} + +inline bool +SGPropertyNode::set_double (double val) +{ + if (_tied) { + return _value.double_val->setValue(val); + } else { + _local_val.double_val = val; + return true; + } +} + +inline bool +SGPropertyNode::set_string (const char * val) +{ + if (_tied) { + return _value.string_val->setValue(val); + } else { + delete (char *)_local_val.string_val; + _local_val.string_val = copy_string(val); + return true; + } +} + +void +SGPropertyNode::clear_value () +{ + switch (_type) { + case NONE: + break; + case ALIAS: + _value.alias = 0; + break; + case BOOL: + if (_tied) { + delete _value.bool_val; + _value.bool_val = 0; + } + _local_val.bool_val = SGRawValue::DefaultValue; + break; + case INT: + if (_tied) { + delete _value.int_val; + _value.int_val = 0; + } + _local_val.int_val = SGRawValue::DefaultValue; + break; + case LONG: + if (_tied) { + delete _value.long_val; + _value.long_val = 0L; + } + _local_val.long_val = SGRawValue::DefaultValue; + break; + case FLOAT: + if (_tied) { + delete _value.float_val; + _value.float_val = 0; + } + _local_val.float_val = SGRawValue::DefaultValue; + break; + case DOUBLE: + if (_tied) { + delete _value.double_val; + _value.double_val = 0; + } + _local_val.double_val = SGRawValue::DefaultValue; + break; + case STRING: + case UNSPECIFIED: + if (_tied) { + delete _value.string_val; + _value.string_val = 0; + } else { + delete (char *)_local_val.string_val; + } + _local_val.string_val = 0; + break; + } + _tied = false; + _type = NONE; +} + /** - * Return a raw double-precision floating-point value (no type coercion). + * Get the value as a string. */ -double -SGValue::getRawDouble () const +const char * +SGPropertyNode::make_string () const { - if (_tied) { - if (_value.double_func.getter != 0) - return (*(_value.double_func.getter))(); + if (!getAttribute(READ)) + return ""; + + switch (_type) { + case ALIAS: + return _value.alias->getStringValue(); + case BOOL: + if (get_bool()) + return "true"; else - return 0.0L; - } else { - return _value.double_val; + return "false"; + case INT: + sprintf(_buffer, "%d", get_int()); + return _buffer; + case LONG: + sprintf(_buffer, "%ld", get_long()); + return _buffer; + case FLOAT: + sprintf(_buffer, "%f", get_float()); + return _buffer; + case DOUBLE: + sprintf(_buffer, "%f", get_double()); + return _buffer; + case STRING: + case UNSPECIFIED: + return get_string(); + case NONE: + default: + return ""; } } +/** + * Trace a write access for a property. + */ +void +SGPropertyNode::trace_write () const +{ +#if PROPS_STANDALONE + cerr << "TRACE: Write node " << getPath () << ", value\"" + << make_string() << '"' << endl; +#else + SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Write node " << getPath() + << ", value\"" << make_string() << '"'); +#endif +} /** - * Return a raw string value (no type coercion). + * Trace a read access for a property. */ -const string & -SGValue::getRawString () const +void +SGPropertyNode::trace_read () const +{ +#if PROPS_STANDALONE + cerr << "TRACE: Write node " << getPath () << ", value \"" + << make_string() << '"' << endl; +#else + SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Read node " << getPath() + << ", value \"" << make_string() << '"'); +#endif +} + + + +//////////////////////////////////////////////////////////////////////// +// Public methods from SGPropertyNode. +//////////////////////////////////////////////////////////////////////// + +/** + * Default constructor: always creates a root node. + */ +SGPropertyNode::SGPropertyNode () + : _name(copy_string("")), + _index(0), + _parent(0), + _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; + _local_val.string_val = 0; } /** - * Set a raw boolean value (no type coercion). - * - * Return false if the value could not be set, true otherwise. + * Copy constructor. */ -bool -SGValue::setRawBool (bool value) +SGPropertyNode::SGPropertyNode (const SGPropertyNode &node) + : _index(node._index), + _parent(0), // don't copy the parent + _path_cache(0), + _type(node._type), + _tied(node._tied), + _attr(node._attr) { - if (_tied) { - if (_value.bool_func.setter != 0) { - (*_value.bool_func.setter)(value); - return true; + _name = copy_string(node._name); + _local_val.string_val = 0; + switch (_type) { + case NONE: + break; + case ALIAS: + _value.alias = node._value.alias; + _tied = false; + break; + case BOOL: + if (_tied) { + _tied = true; + _value.bool_val = node._value.bool_val->clone(); } else { - return false; + _tied = false; + set_bool(node.get_bool()); } - } else { - _value.bool_val = value; - return true; + break; + case INT: + if (_tied) { + _tied = true; + _value.int_val = node._value.int_val->clone(); + } else { + _tied = false; + set_int(node.get_int()); + } + break; + case LONG: + if (_tied) { + _tied = true; + _value.long_val = node._value.long_val->clone(); + } else { + _tied = false; + set_long(node.get_long()); + } + break; + case FLOAT: + if (_tied) { + _tied = true; + _value.float_val = node._value.float_val->clone(); + } else { + _tied = false; + set_float(node.get_float()); + } + break; + case DOUBLE: + if (_tied) { + _tied = true; + _value.double_val = node._value.double_val->clone(); + } else { + _tied = false; + set_double(node.get_double()); + } + break; + case STRING: + case UNSPECIFIED: + if (_tied) { + _tied = true; + _value.string_val = node._value.string_val->clone(); + } else { + _tied = false; + set_string(node.get_string()); + } + break; } } /** - * Set a raw integer value (no type coercion). - * - * Return false if the value could not be set, true otherwise. + * Convenience constructor. + */ +SGPropertyNode::SGPropertyNode (const char * name, + int index, + SGPropertyNode * parent) + : _index(index), + _parent(parent), + _path_cache(0), + _type(NONE), + _tied(false), + _attr(READ|WRITE) +{ + _name = copy_string(name); + _local_val.string_val = 0; +} + + +/** + * Destructor. + */ +SGPropertyNode::~SGPropertyNode () +{ + delete (char *)_name; + for (int i = 0; i < (int)_children.size(); i++) { + delete _children[i]; + } +// delete _path_cache; + clear_value(); +} + + +/** + * Alias to another node. + */ +bool +SGPropertyNode::alias (SGPropertyNode * target) +{ + if (target == 0 || _type == ALIAS || _tied) + return false; + clear_value(); + _value.alias = target; + _type = ALIAS; + return true; +} + + +/** + * Alias to another node by path. + */ +bool +SGPropertyNode::alias (const char * path) +{ + 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. */ -bool -SGValue::setRawInt (int value) +SGPropertyNode * +SGPropertyNode::getChild (const char * name, int index, bool create) { - if (_tied) { - if (_value.int_func.setter != 0) { - (*_value.int_func.setter)(value); - return true; - } else { - return false; - } + 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 { - _value.int_val = value; - return true; + return 0; } } /** - * Set a raw floating-point value (no type coercion). - * - * Return false if the value could not be set, true otherwise. + * Get a const child by name and index. */ -bool -SGValue::setRawFloat (float value) +const SGPropertyNode * +SGPropertyNode::getChild (const char * name, int index) 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; - } + int pos = find_child(name, index, _children); + if (pos >= 0) + return _children[pos]; + else + return 0; } /** - * Set a raw double-precision floating-point value (no type coercion). - * - * Return false if the value could not be set, true otherwise. + * Get all children with the same name (but different indices). */ -bool -SGValue::setRawDouble (double value) +vector +SGPropertyNode::getChildren (const char * name) { - 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; - } + vector children; + int max = _children.size(); + + for (int i = 0; i < max; i++) + if (compare_strings(_children[i]->getName(), name)) + children.push_back(_children[i]); + + sort(children.begin(), children.end(), CompareIndices()); + return children; } /** - * Set a raw string value (no type coercion). - * - * Return false if the value could not be set, true otherwise. + * Get all children const with the same name (but different indices). */ -bool -SGValue::setRawString (const string &value) +vector +SGPropertyNode::getChildren (const char * name) const { - if (_tied) { - if (_value.string_func.setter != 0) { - (*_value.string_func.setter)(value); - return true; - } else { - return false; - } - } else { - string_val = value; - return true; + vector children; + int max = _children.size(); + + for (int i = 0; i < max; i++) + if (compare_strings(_children[i]->getName(), name)) + children.push_back(_children[i]); + + sort(children.begin(), children.end(), CompareIndices()); + return children; +} + + +const char * +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.c_str(); } +SGPropertyNode::Type +SGPropertyNode::getType () const +{ + if (_type == ALIAS) + return _value.alias->getType(); + else + return _type; +} -/** - * Get the boolean value of a property. - * - * If the native type is not boolean, attempt to coerce it. - */ -bool -SGValue::getBoolValue () const + +bool +SGPropertyNode::getBoolValue () const { + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == BOOL) + return get_bool(); + + if (getAttribute(TRACE_READ)) + trace_read(); + if (!getAttribute(READ)) + return SGRawValue::DefaultValue; 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() == "true" || getIntValue() != 0) ? true : false); + case UNSPECIFIED: + return (compare_strings(get_string(), "true") || getDoubleValue() != 0.0L); + case NONE: + default: + return SGRawValue::DefaultValue; } - return false; } - -/** - * Get the integer value of a property. - * - * If the native type is not integer, attempt to coerce it. - */ -int -SGValue::getIntValue () const +int +SGPropertyNode::getIntValue () const { + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == INT) + return get_int(); + + if (getAttribute(TRACE_READ)) + trace_read(); + if (!getAttribute(READ)) + return SGRawValue::DefaultValue; 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()); + case NONE: + default: + return SGRawValue::DefaultValue; } - return false; } - -/** - * Get the floating-point value of a property. - * - * If the native type is not float, attempt to coerce it. - */ -float -SGValue::getFloatValue () const +long +SGPropertyNode::getLongValue () const { + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == LONG) + return get_long(); + + if (getAttribute(TRACE_READ)) + trace_read(); + if (!getAttribute(READ)) + return SGRawValue::DefaultValue; switch (_type) { + case ALIAS: + return _value.alias->getLongValue(); case BOOL: - return (float)(getRawBool()); + return long(get_bool()); case INT: - return (float)(getRawInt()); + return long(get_int()); + case LONG: + return get_long(); case FLOAT: - return getRawFloat(); + return long(get_float()); case DOUBLE: - return (float)(getRawDouble()); - case UNKNOWN: + return long(get_double()); case STRING: - return (float)atof(getRawString().c_str()); + case UNSPECIFIED: + return strtol(get_string(), 0, 0); + case NONE: + default: + return SGRawValue::DefaultValue; } - return false; } - -/** - * Get the double-precision floating-point value of a property. - * - * If the native type is not double, attempt to coerce it. - */ -double -SGValue::getDoubleValue () const +float +SGPropertyNode::getFloatValue () const { + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == FLOAT) + return get_float(); + + if (getAttribute(TRACE_READ)) + trace_read(); + if (!getAttribute(READ)) + return SGRawValue::DefaultValue; switch (_type) { + case ALIAS: + return _value.alias->getFloatValue(); case BOOL: - return (double)(getRawBool()); + return float(get_bool()); case INT: - return (double)(getRawInt()); + return float(get_int()); + case LONG: + return float(get_long()); case FLOAT: - return (double)(getRawFloat()); + return get_float(); case DOUBLE: - return getRawDouble(); - case UNKNOWN: + return float(get_double()); case STRING: - return atof(getRawString().c_str()); + case UNSPECIFIED: + return atof(get_string()); + case NONE: + default: + return SGRawValue::DefaultValue; } - return false; } - -/** - * Get the string value of a property. - * - * If the native type is not string, attempt to coerce it. - */ -const string & -SGValue::getStringValue () const +double +SGPropertyNode::getDoubleValue () const { - char buf[512]; + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == DOUBLE) + return get_double(); + + if (getAttribute(TRACE_READ)) + trace_read(); + if (!getAttribute(READ)) + return SGRawValue::DefaultValue; + switch (_type) { + case ALIAS: + return _value.alias->getDoubleValue(); case BOOL: - if (getRawBool()) - string_val = "true"; - else - string_val = "false"; - return string_val; + return double(get_bool()); case INT: - sprintf(buf, "%d", getRawInt()); - string_val = buf; - return string_val; + return double(get_int()); + case LONG: + return double(get_long()); case FLOAT: - sprintf(buf, "%f", getRawFloat()); - string_val = buf; - return string_val; + return double(get_float()); case DOUBLE: - sprintf(buf, "%f", getRawDouble()); - string_val = buf; - return string_val; - case UNKNOWN: + return get_double(); case STRING: - return getRawString(); + case UNSPECIFIED: + return strtod(get_string(), 0); + case NONE: + default: + return SGRawValue::DefaultValue; } - return empty_string; } +const char * +SGPropertyNode::getStringValue () const +{ + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == STRING) + return get_string(); + + if (getAttribute(TRACE_READ)) + trace_read(); + if (!getAttribute(READ)) + return SGRawValue::DefaultValue; + return make_string(); +} -/** - * Set the boolean value and change the type if unknown. - * - * Returns true on success. - */ bool -SGValue::setBoolValue (bool value) +SGPropertyNode::setBoolValue (bool value) { - if (_type == UNKNOWN) - _type = INT; + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == BOOL) + return set_bool(value); + + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); + _tied = false; + _type = BOOL; + } + switch (_type) { + case ALIAS: + result = _value.alias->setBoolValue(value); + break; case BOOL: - return setRawBool(value); + result = set_bool(value); + break; case INT: - return setRawInt((int)value); + result = set_int(int(value)); + break; + case LONG: + result = set_long(long(value)); + break; case FLOAT: - return setRawFloat((float)value); + result = set_float(float(value)); + break; case DOUBLE: - return setRawDouble((double)value); + result = set_double(double(value)); + break; case STRING: - if (value) - return setRawString("true"); - else - return setRawString("false"); + case UNSPECIFIED: + result = set_string(value ? "true" : "false"); + break; + case NONE: + default: + break; } - return false; -} + if (getAttribute(TRACE_WRITE)) + trace_write(); + return result; +} -/** - * Set the integer value and change the type if unknown. - * - * Returns true on success. - */ bool -SGValue::setIntValue (int value) +SGPropertyNode::setIntValue (int value) { - if (_type == UNKNOWN) + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == INT) + return set_int(value); + + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); _type = INT; + _local_val.int_val = 0; + } + switch (_type) { + case ALIAS: + result = _value.alias->setIntValue(value); + break; case BOOL: - if (value == 0) - return setRawBool(false); - else - return setRawBool(true); + result = set_bool(value == 0 ? false : true); + break; case INT: - return setRawInt(value); + result = set_int(value); + break; + case LONG: + result = set_long(long(value)); + break; case FLOAT: - return setRawFloat((float)value); + result = set_float(float(value)); + break; case DOUBLE: - return setRawDouble((double)value); + result = set_double(double(value)); + break; case STRING: + case UNSPECIFIED: { char buf[128]; sprintf(buf, "%d", value); - return setRawString(buf); + result = set_string(buf); + break; + } + case NONE: + default: + break; } - return false; + + if (getAttribute(TRACE_WRITE)) + trace_write(); + return result; } +bool +SGPropertyNode::setLongValue (long value) +{ + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == LONG) + return set_long(value); + + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); + _type = LONG; + _local_val.long_val = 0L; + } + + switch (_type) { + case ALIAS: + result = _value.alias->setLongValue(value); + break; + case BOOL: + result = set_bool(value == 0L ? false : true); + break; + case INT: + result = set_int(int(value)); + break; + case LONG: + result = set_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, "%ld", value); + result = set_string(buf); + break; + } + case NONE: + default: + break; + } + + if (getAttribute(TRACE_WRITE)) + trace_write(); + return result; +} -/** - * Set the floating-point value and change the type if unknown. - * - * Returns true on success. - */ bool -SGValue::setFloatValue (float value) +SGPropertyNode::setFloatValue (float value) { - if (_type == UNKNOWN) + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == FLOAT) + return set_float(value); + + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); _type = FLOAT; + _local_val.float_val = 0; + } + switch (_type) { + case ALIAS: + result = _value.alias->setFloatValue(value); + break; case BOOL: - if (value == 0.0) - return setRawBool(false); - else - return setRawBool(true); + result = set_bool(value == 0.0 ? false : true); + break; case INT: - return setRawInt((int)value); + result = set_int(int(value)); + break; + case LONG: + result = set_long(long(value)); + break; case FLOAT: - return setRawFloat(value); + result = set_float(value); + break; case DOUBLE: - return setRawDouble((double)value); + result = set_double(double(value)); + break; case STRING: + case UNSPECIFIED: { char buf[128]; sprintf(buf, "%f", value); - return setRawString(buf); + result = set_string(buf); + break; + } + case NONE: + default: + break; } - return false; -} + if (getAttribute(TRACE_WRITE)) + trace_write(); + return result; +} -/** - * Set the double-precision value and change the type if unknown. - * - * Returns true on success. - */ bool -SGValue::setDoubleValue (double value) +SGPropertyNode::setDoubleValue (double value) { - if (_type == UNKNOWN) + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == DOUBLE) + return set_double(value); + + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); + _local_val.double_val = value; _type = DOUBLE; + } + switch (_type) { + case ALIAS: + result = _value.alias->setDoubleValue(value); + break; case BOOL: - if (value == 0.0L) - return setRawBool(false); - else - return setRawBool(true); + result = set_bool(value == 0.0L ? false : true); + break; case INT: - return setRawInt((int)value); + result = set_int(int(value)); + break; + case LONG: + result = set_long(long(value)); + break; case FLOAT: - return setRawFloat((float)value); + result = set_float(float(value)); + break; case DOUBLE: - return setRawDouble(value); + result = set_double(value); + break; case STRING: + case UNSPECIFIED: { char buf[128]; - sprintf(buf, "%lf", value); - return setRawString(buf); + sprintf(buf, "%f", value); + result = set_string(buf); + break; + } + case NONE: + default: + break; } - return false; -} + if (getAttribute(TRACE_WRITE)) + trace_write(); + return result; +} -/** - * Set the string value and change the type if unknown. - * - * Returns true on success. - */ bool -SGValue::setStringValue (const string &value) +SGPropertyNode::setStringValue (const char * value) { - if (_type == UNKNOWN) + // Shortcut for common case + if (_attr == (READ|WRITE) && _type == STRING) + return set_string(value); + + bool result = false; + TEST_WRITE; + if (_type == NONE || _type == UNSPECIFIED) { + clear_value(); _type = STRING; + } switch (_type) { + case ALIAS: + result = _value.alias->setStringValue(value); + break; case BOOL: - if (value == "true" || atoi(value.c_str()) != 0) - return setRawBool(true); - else - return setRawBool(false); + result = set_bool((compare_strings(value, "true") + || atoi(value)) ? true : false); + break; case INT: - return setRawInt(atoi(value.c_str())); + result = set_int(atoi(value)); + break; + case LONG: + result = set_long(strtol(value, 0, 0)); + break; case FLOAT: - return setRawFloat(atof(value.c_str())); + result = set_float(atof(value)); + break; case DOUBLE: - return setRawDouble(atof(value.c_str())); + result = set_double(strtod(value, 0)); + break; case STRING: - return setRawString(value); + case UNSPECIFIED: + result = set_string(value); + break; + case NONE: + default: + break; } - return false; -} + if (getAttribute(TRACE_WRITE)) + trace_write(); + return result; +} -/** - * Set a string value and don't modify the type. - * - * Returns true on success. - */ bool -SGValue::setUnknownValue (const string &value) +SGPropertyNode::setUnspecifiedValue (const char * value) { + bool result = false; + TEST_WRITE; + if (_type == NONE) { + clear_value(); + _type = UNSPECIFIED; + } + switch (_type) { + case ALIAS: + result = _value.alias->setUnspecifiedValue(value); + break; case BOOL: - if (value == "true" || atoi(value.c_str()) != 0) - return setRawBool(true); - else - return setRawBool(false); + result = set_bool((compare_strings(value, "true") + || atoi(value)) ? true : false); + break; case INT: - return setRawInt(atoi(value.c_str())); + result = set_int(atoi(value)); + break; + case LONG: + result = set_long(strtol(value, 0, 0)); + break; case FLOAT: - return setRawFloat(atof(value.c_str())); + result = set_float(atof(value)); + break; case DOUBLE: - return setRawDouble(atof(value.c_str())); + result = set_double(strtod(value, 0)); + break; case STRING: - case UNKNOWN: - return setRawString(value); + case UNSPECIFIED: + result = set_string(value); + break; + case NONE: + default: + break; } - return false; -} + if (getAttribute(TRACE_WRITE)) + trace_write(); + return result; +} -/** - * 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 -SGValue::tieBool (bool_getter getter, bool_setter setter, - bool useDefault) +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) { - if (_tied) { + if (_type == ALIAS || _tied) return false; - } else { - if (useDefault && setter) - (*setter)(getBoolValue()); - _tied = true; - _type = BOOL; - _value.bool_func.getter = getter; - _value.bool_func.setter = setter; - return true; - } -} + useDefault = useDefault && hasValue(); + bool old_val = false; + if (useDefault) + old_val = getBoolValue(); + + clear_value(); + _type = BOOL; + _tied = true; + _value.bool_val = rawValue.clone(); + + if (useDefault) + setBoolValue(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 -SGValue::tieInt (int_getter getter, int_setter setter, - bool useDefault) +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) { - if (_tied) { + if (_type == ALIAS || _tied) return false; - } else { - if (useDefault && setter) - (*setter)(getIntValue()); - _tied = true; - _type = INT; - _value.int_func.getter = getter; - _value.int_func.setter = setter; - return true; - } -} + useDefault = useDefault && hasValue(); + int old_val = 0; + if (useDefault) + old_val = getIntValue(); + + clear_value(); + _type = INT; + _tied = true; + _value.int_val = rawValue.clone(); + + if (useDefault) + setIntValue(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 -SGValue::tieFloat (float_getter getter, float_setter setter, - bool useDefault) +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) { - if (_tied) { + if (_type == ALIAS || _tied) return false; - } else { - if (useDefault && setter) - (*setter)(getFloatValue()); - _tied = true; - _type = FLOAT; - _value.float_func.getter = getter; - _value.float_func.setter = setter; - return true; - } + + useDefault = useDefault && hasValue(); + long old_val = 0; + if (useDefault) + old_val = getLongValue(); + + clear_value(); + _type = LONG; + _tied = true; + _value.long_val = rawValue.clone(); + + if (useDefault) + setLongValue(old_val); + + return true; } +bool +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) +{ + if (_type == ALIAS || _tied) + return false; + + useDefault = useDefault && hasValue(); + 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 -SGValue::tieDouble (double_getter getter, double_setter setter, - bool useDefault) +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) { - if (_tied) { + if (_type == ALIAS || _tied) return false; - } else { - if (useDefault && setter) - (*setter)(getDoubleValue()); - _tied = true; - _type = DOUBLE; - _value.double_func.getter = getter; - _value.double_func.setter = setter; - return true; - } -} + useDefault = useDefault && hasValue(); + 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 -SGValue::tieString (string_getter getter, string_setter setter, - bool useDefault) +SGPropertyNode::tie (const SGRawValue &rawValue, bool useDefault) { - if (_tied) { + if (_type == ALIAS || _tied) return false; - } else { - if (useDefault && setter) - (*setter)(getStringValue()); - _tied = true; - _type = STRING; - _value.string_func.getter = getter; - _value.string_func.setter = setter; - return true; - } -} + useDefault = useDefault && hasValue(); + string old_val; + if (useDefault) + old_val = getStringValue(); + + clear_value(); + _type = STRING; + _tied = true; + _value.string_val = rawValue.clone(); + + if (useDefault) + setStringValue(old_val.c_str()); + + 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 -SGValue::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; + _local_val.bool_val = val; break; } case INT: { - int value = getRawInt(); - _tied = false; - setRawInt(value); + int val = getIntValue(); + clear_value(); + _type = INT; + _local_val.int_val = val; + break; + } + case LONG: { + long val = getLongValue(); + clear_value(); + _type = LONG; + _local_val.long_val = val; break; } case FLOAT: { - float value = getRawFloat(); - _tied = false; - setRawFloat(value); + float val = getFloatValue(); + clear_value(); + _type = FLOAT; + _local_val.float_val = val; break; } case DOUBLE: { - double value = getRawDouble(); - _tied = false; - setRawDouble(value); + double val = getDoubleValue(); + clear_value(); + _type = DOUBLE; + _local_val.double_val = val; break; } - case STRING: { - string value = getRawString(); - _tied = false; - setRawString(value); + case STRING: + case UNSPECIFIED: { + string val = getStringValue(); + clear_value(); + _type = STRING; + _local_val.string_val = copy_string(val.c_str()); break; } + case NONE: + default: + break; } + _tied = false; return true; } - - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGPropertyList. -//////////////////////////////////////////////////////////////////////// - - -/** - * Constructor. - */ -SGPropertyList::SGPropertyList () -{ -} - - -/** - * Destructor. - */ -SGPropertyList::~SGPropertyList () -{ -} - - -/** - * Return true if a value is present. - */ -bool -SGPropertyList::hasValue (const string &name) const +SGPropertyNode * +SGPropertyNode::getRootNode () { - const_iterator el = _props.find(name); - if (el == _props.end()) - return false; + if (_parent == 0) + return this; else - return true; -} - - -/** - * Look up the SGValue 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 '/'. - */ -SGValue * -SGPropertyList::getValue (const string &name, bool create) -{ - const_iterator el = _props.find(name); - if (el == _props.end()) { - if (!create) - return 0; - else { - FG_LOG(FG_GENERAL, FG_DEBUG, "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; - } - } - } - return &(_props[name]); + return _parent->getRootNode(); } - -/** - * Look up a const value (never created). - */ -const SGValue * -SGPropertyList::getValue (const string &name) const +const SGPropertyNode * +SGPropertyNode::getRootNode () const { - value_map::const_iterator el = _props.find(name); - if (el == _props.end()) - return 0; + if (_parent == 0) + return this; else - return &(el->second); + return _parent->getRootNode(); } - -/** - * Extract a boolean from the value. - * - * Note that this is inefficient for use in a tight loop: it is - * better to get the SGValue and query it repeatedly. - */ -bool -SGPropertyList::getBoolValue (const string &name, bool defaultValue) const +SGPropertyNode * +SGPropertyNode::getNode (const char * relative_path, bool create) { - const SGValue * val = getValue(name); - if (val == 0) - return defaultValue; - else - return val->getBoolValue(); -} - +// if (_path_cache == 0) +// _path_cache = new cache_map; -/** - * Extract an integer from the value. - * - * Note that this is inefficient for use in a tight loop: it is - * better to get the SGValue and query it repeatedly. - */ -int -SGPropertyList::getIntValue (const string &name, int defaultValue) const -{ - const SGValue * val = getValue(name); - if (val == 0) - return defaultValue; - else - return val->getIntValue(); +// SGPropertyNode * result = (*_path_cache)[relative_path]; +// if (result == 0) { +// vector components; +// parse_path(relative_path, components); +// result = find_node(this, components, 0, create); +// if (result != 0) +// (*_path_cache)[relative_path] = result; +// } + +// return result; + vector components; + parse_path(relative_path, components); + return find_node(this, components, 0, create); } - -/** - * Extract a float from the value. - * - * Note that this is inefficient for use in a tight loop: it is - * better to get the SGValue and query it repeatedly. - */ -float -SGPropertyList::getFloatValue (const string &name, float defaultValue) const +SGPropertyNode * +SGPropertyNode::getNode (const char * relative_path, int index, bool create) { - const SGValue * val = getValue(name); - if (val == 0) - return defaultValue; - else - return val->getFloatValue(); + 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 double from the value. - * - * Note that this is inefficient for use in a tight loop: it is - * better to get the SGValue and query it repeatedly. - */ -double -SGPropertyList::getDoubleValue (const string &name, double defaultValue) const +const SGPropertyNode * +SGPropertyNode::getNode (const char * relative_path) const { - const SGValue * val = getValue(name); - if (val == 0) - return defaultValue; - else - return val->getDoubleValue(); + return ((SGPropertyNode *)this)->getNode(relative_path, false); } - -/** - * Extract a string from the value. - * - * Note that this is inefficient for use in a tight loop: it is - * better to save the SGValue and query it repeatedly. - */ -const string & -SGPropertyList::getStringValue (const string &name, - const string &defaultValue) const +const SGPropertyNode * +SGPropertyNode::getNode (const char * relative_path, int index) const { - const SGValue * val = getValue(name); - if (val == 0) - return defaultValue; - else - return val->getStringValue(); + return ((SGPropertyNode *)this)->getNode(relative_path, index, false); } - -/** - * 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 SGValue and modify it repeatedly. - * - * Returns true on success. - */ -bool -SGPropertyList::setBoolValue (const string &name, bool value) -{ - return getValue(name, true)->setBoolValue(value); -} + +//////////////////////////////////////////////////////////////////////// +// Convenience methods using relative paths. +//////////////////////////////////////////////////////////////////////// /** - * 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 SGValue and modify it repeatedly. - * - * Returns true on success. + * Test whether another node has a value attached. */ bool -SGPropertyList::setIntValue (const string &name, int value) +SGPropertyNode::hasValue (const char * relative_path) const { - return getValue(name, true)->setIntValue(value); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? false : node->hasValue()); } /** - * 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 SGValue and modify it repeatedly. - * - * Returns true on success. + * Get the value type for another node. */ -bool -SGPropertyList::setFloatValue (const string &name, float value) +SGPropertyNode::Type +SGPropertyNode::getType (const char * relative_path) const { - return getValue(name, true)->setFloatValue(value); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? UNSPECIFIED : (Type)(node->getType())); } /** - * 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 SGValue and modify it repeatedly. - * - * Returns true on success. + * Get a bool value for another node. */ bool -SGPropertyList::setDoubleValue (const string &name, double value) +SGPropertyNode::getBoolValue (const char * relative_path, + bool defaultValue) const { - return getValue(name, true)->setDoubleValue(value); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getBoolValue()); } /** - * 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 SGValue and modify it repeatedly. - * - * Returns true on success. + * Get an int value for another node. */ -bool -SGPropertyList::setStringValue (const string &name, const string &value) +int +SGPropertyNode::getIntValue (const char * relative_path, + int defaultValue) const { - return getValue(name, true)->setStringValue(value); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getIntValue()); } /** - * 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 SGValue and modify it repeatedly. - * - * Returns true on success. + * Get a long value for another node. */ -bool -SGPropertyList::setUnknownValue (const string &name, const string &value) +long +SGPropertyNode::getLongValue (const char * relative_path, + long defaultValue) const { - return getValue(name, true)->setUnknownValue(value); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getLongValue()); } /** - * Tie a boolean value to external functions. - * - * Invokes SGValue::tieBool + * Get a float value for another node. */ -bool -SGPropertyList::tieBool (const string &name, - bool_getter getter, - bool_setter setter, - bool useDefault) +float +SGPropertyNode::getFloatValue (const char * relative_path, + float defaultValue) const { - FG_LOG(FG_GENERAL, FG_DEBUG, "Tying bool property '" << name << '\''); - useDefault = useDefault && hasValue(name); - return getValue(name, true)->tieBool(getter, setter, useDefault); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getFloatValue()); } /** - * Tie an integer value to external functions. - * - * Invokes SGValue::tieInt + * Get a double value for another node. */ -bool -SGPropertyList::tieInt (const string &name, - int_getter getter, - int_setter setter, - bool useDefault) +double +SGPropertyNode::getDoubleValue (const char * relative_path, + double defaultValue) const { - FG_LOG(FG_GENERAL, FG_DEBUG, "Tying int property '" << name << '\''); - useDefault = useDefault && hasValue(name); - return getValue(name, true)->tieInt(getter, setter, useDefault); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getDoubleValue()); } /** - * Tie a float value to external functions. - * - * Invokes SGValue::tieFloat + * Get a string value for another node. */ -bool -SGPropertyList::tieFloat (const string &name, - float_getter getter, - float_setter setter, - bool useDefault) +const char * +SGPropertyNode::getStringValue (const char * relative_path, + const char * defaultValue) const { - FG_LOG(FG_GENERAL, FG_DEBUG, "Tying float property '" << name << '\''); - useDefault = useDefault && hasValue(name); - return getValue(name, true)->tieFloat(getter, setter, useDefault); + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? defaultValue : node->getStringValue()); } /** - * Tie a double value to external functions. - * - * Invokes SGValue::tieDouble + * Set a bool value for another node. */ bool -SGPropertyList::tieDouble (const string &name, - double_getter getter, - double_setter setter, - bool useDefault) +SGPropertyNode::setBoolValue (const char * relative_path, bool value) { - FG_LOG(FG_GENERAL, FG_DEBUG, "Tying double property '" << name << '\''); - useDefault = useDefault && hasValue(name); - return getValue(name, true)->tieDouble(getter, setter, useDefault); + return getNode(relative_path, true)->setBoolValue(value); } /** - * Tie a string value to external functions. - * - * Invokes SGValue::tieString + * Set an int value for another node. */ bool -SGPropertyList::tieString (const string &name, - string_getter getter, - string_setter setter, - bool useDefault) +SGPropertyNode::setIntValue (const char * relative_path, int value) { - FG_LOG(FG_GENERAL, FG_DEBUG, "Tying string property '" << name << '\''); - useDefault = useDefault && hasValue(name); - return getValue(name, true)->tieString(getter, setter, useDefault); + return getNode(relative_path, true)->setIntValue(value); } /** - * Untie a value from external functions. - * - * Invokes SGValue::untie + * Set a long value for another node. */ bool -SGPropertyList::untie (const string &name) -{ - FG_LOG(FG_GENERAL, FG_DEBUG, "Untying property '" << name << '\''); - return getValue(name, true)->untie(); -} - - - -//////////////////////////////////////////////////////////////////////// -// Implementation of SGPropertyNode. -//////////////////////////////////////////////////////////////////////// - - -/** - * 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. - */ -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; -} - - -/** - * Constructor. - */ -SGPropertyNode::SGPropertyNode (const string &path, - SGPropertyList * props) - : _props(props), _node(0) -{ - setPath(path); -} - - -/** - * Destructor. - */ -SGPropertyNode::~SGPropertyNode () +SGPropertyNode::setLongValue (const char * relative_path, long value) { - delete _node; - _node = 0; + return getNode(relative_path, true)->setLongValue(value); } /** - * Set the path. - * - * Strip the trailing '/', if any. + * Set a float value for another node. */ -void -SGPropertyNode::setPath (const string &path) +bool +SGPropertyNode::setFloatValue (const char * relative_path, float value) { - _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)->setFloatValue(value); } /** - * Return the local name of the property. - * - * The local name is just everything after the last slash. + * Set a double value for another node. */ -const string & -SGPropertyNode::getName () const +bool +SGPropertyNode::setDoubleValue (const char * relative_path, double value) { - 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)->setDoubleValue(value); } /** - * Return the number of children for the current node. + * Set a string value for another node. */ -int -SGPropertyNode::size () const +bool +SGPropertyNode::setStringValue (const char * relative_path, const char * value) { - if (_props == 0) - return 0; - - int s = 0; - - string base; - string lastBase; - string pattern = _path; - pattern += '/'; - - SGPropertyList::const_iterator it = _props->begin(); - SGPropertyList::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)->setStringValue(value); } /** - * Initialize a node to represent this node's parent. - * - * A return value of true means success; otherwise, the node supplied - * is unmodified. + * Set an unknown value for another node. */ -SGPropertyNode & -SGPropertyNode::getParent () const +bool +SGPropertyNode::setUnspecifiedValue (const char * relative_path, + const char * value) { - if (_node == 0) - _node = new SGPropertyNode(); - - string::size_type pos = _path.rfind('/'); - if (pos != string::npos) { - _node->setPropertyList(_props); - _node->setPath(_path.substr(0, pos-1)); - } - return *_node; -} - - -/** - * Initialize a node to represent this node's nth child. - * - * A return value of true means success; otherwise, the node supplied - * is unmodified. - */ -SGPropertyNode & -SGPropertyNode::getChild (int n) const -{ - if (_node == 0) - _node = new SGPropertyNode(); - - if (_props == 0) - return *_node; - - int s = 0; - string base; - string lastBase; - string pattern = _path; - pattern += '/'; - - SGPropertyList::const_iterator it = _props->begin(); - SGPropertyList::const_iterator end = _props->end(); - while (it != end) { - if (get_base(pattern, it->first, base) && base != lastBase) { - if (s == n) { - _node->setPropertyList(_props); - _node->setPath(_path + string("/") + base); - return *_node; - } else { - s++; - lastBase = base; - } - } - it++; - } - - return *_node; + return getNode(relative_path, true)->setUnspecifiedValue(value); } /** - * Return a node for an arbitrary subpath. - * - * Never returns 0. + * Test whether another node is tied. */ -SGPropertyNode & -SGPropertyNode::getSubNode (const string &subpath) const +bool +SGPropertyNode::isTied (const char * relative_path) const { - if (_node == 0) - _node = new SGPropertyNode(); - - _node->setPropertyList(_props); - _node->setPath(_path + string("/") + subpath); - return *_node; + const SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? false : node->isTied()); } /** - * Test whether the specified subpath has a value. + * Tie a node reached by a relative path, creating it if necessary. */ bool -SGPropertyNode::hasValue (const string &subpath) const +SGPropertyNode::tie (const char * relative_path, + const SGRawValue &rawValue, + bool useDefault) { - if (_props == 0) - return false; - - if (subpath.size() == 0) - return _props->hasValue(_path); - else - return _props->hasValue(_path + string("/") + subpath); + 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. */ -SGValue * -SGPropertyNode::getValue (const string &subpath) +bool +SGPropertyNode::tie (const char * relative_path, + const SGRawValue &rawValue, + bool useDefault) { - if (_props == 0) - return 0; - - if (subpath.size() == 0) - return _props->getValue(_path); - else - return _props->getValue(_path + string("/") + subpath); + return getNode(relative_path, true)->tie(rawValue, useDefault); } /** - * Return a bool value. + * Tie a node reached by a relative path, creating it if necessary. */ bool -SGPropertyNode::getBoolValue (const string &subpath, bool defaultValue) const +SGPropertyNode::tie (const char * relative_path, + const SGRawValue &rawValue, + bool useDefault) { - if (_props == 0) - return defaultValue; - - if (subpath == "") - return _props->getBoolValue(_path, defaultValue); - else - return _props->getBoolValue(_path + string("/") + subpath, - defaultValue); + return getNode(relative_path, true)->tie(rawValue, useDefault); } /** - * Return an int value. + * Tie a node reached by a relative path, creating it if necessary. */ -int -SGPropertyNode::getIntValue (const string &subpath, int defaultValue) const +bool +SGPropertyNode::tie (const char * relative_path, + const SGRawValue &rawValue, + bool useDefault) { - if (_props == 0) - return defaultValue; - - if (subpath == "") - return _props->getIntValue(_path, defaultValue); - else - return _props->getIntValue(_path + string("/") + subpath, - defaultValue); + return getNode(relative_path, true)->tie(rawValue, useDefault); } /** - * Return a float value. + * Tie a node reached by a relative path, creating it if necessary. */ -float -SGPropertyNode::getFloatValue (const string &subpath, float defaultValue) const +bool +SGPropertyNode::tie (const char * relative_path, + const SGRawValue &rawValue, + bool useDefault) { - if (_props == 0) - return defaultValue; - - if (subpath == "") - return _props->getFloatValue(_path, defaultValue); - else - return _props->getFloatValue(_path + string("/") + subpath, - defaultValue); + return getNode(relative_path, true)->tie(rawValue, useDefault); } /** - * Return a double value. + * Tie a node reached by a relative path, creating it if necessary. */ -double -SGPropertyNode::getDoubleValue (const string &subpath, - double defaultValue) const +bool +SGPropertyNode::tie (const char * relative_path, + const SGRawValue &rawValue, + bool useDefault) { - if (_props == 0) - return defaultValue; - - if (subpath == "") - return _props->getDoubleValue(_path, defaultValue); - else - return _props->getDoubleValue(_path + string("/") + subpath, - defaultValue); + return getNode(relative_path, true)->tie(rawValue, useDefault); } /** - * Return a string value. + * Attempt to untie another node reached by a relative path. */ -const string & -SGPropertyNode::getStringValue (const string &subpath, - const string &defaultValue) const +bool +SGPropertyNode::untie (const char * relative_path) { - if (_props == 0) - return defaultValue; - - if (subpath == "") - return _props->getStringValue(_path, defaultValue); - else - return _props->getStringValue(_path + string("/") + subpath, - defaultValue); + SGPropertyNode * node = getNode(relative_path); + return (node == 0 ? false : node->untie()); } - // end of props.cxx